MDL-67814 core_h5p: added renderer and editor classes
[moodle.git] / lib / adminlib.php
blobfda69154731ba2ad4a4b30e9fbd6781559ed49b6
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));
210 // Delete scheduled tasks.
211 $DB->delete_records('task_scheduled', array('component' => $component));
213 // Delete Inbound Message datakeys.
214 $DB->delete_records_select('messageinbound_datakeys',
215 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
217 // Delete Inbound Message handlers.
218 $DB->delete_records('messageinbound_handlers', array('component' => $component));
220 // delete all the logs
221 $DB->delete_records('log', array('module' => $pluginname));
223 // delete log_display information
224 $DB->delete_records('log_display', array('component' => $component));
226 // delete the module configuration records
227 unset_all_config_for_plugin($component);
228 if ($type === 'mod') {
229 unset_all_config_for_plugin($pluginname);
232 // delete message provider
233 message_provider_uninstall($component);
235 // delete the plugin tables
236 $xmldbfilepath = $plugindirectory . '/db/install.xml';
237 drop_plugin_tables($component, $xmldbfilepath, false);
238 if ($type === 'mod' or $type === 'block') {
239 // non-frankenstyle table prefixes
240 drop_plugin_tables($name, $xmldbfilepath, false);
243 // delete the capabilities that were defined by this module
244 capabilities_cleanup($component);
246 // Delete all remaining files in the filepool owned by the component.
247 $fs = get_file_storage();
248 $fs->delete_component_files($component);
250 // Finally purge all caches.
251 purge_all_caches();
253 // Invalidate the hash used for upgrade detections.
254 set_config('allversionshash', '');
256 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
260 * Returns the version of installed component
262 * @param string $component component name
263 * @param string $source either 'disk' or 'installed' - where to get the version information from
264 * @return string|bool version number or false if the component is not found
266 function get_component_version($component, $source='installed') {
267 global $CFG, $DB;
269 list($type, $name) = core_component::normalize_component($component);
271 // moodle core or a core subsystem
272 if ($type === 'core') {
273 if ($source === 'installed') {
274 if (empty($CFG->version)) {
275 return false;
276 } else {
277 return $CFG->version;
279 } else {
280 if (!is_readable($CFG->dirroot.'/version.php')) {
281 return false;
282 } else {
283 $version = null; //initialize variable for IDEs
284 include($CFG->dirroot.'/version.php');
285 return $version;
290 // activity module
291 if ($type === 'mod') {
292 if ($source === 'installed') {
293 if ($CFG->version < 2013092001.02) {
294 return $DB->get_field('modules', 'version', array('name'=>$name));
295 } else {
296 return get_config('mod_'.$name, 'version');
299 } else {
300 $mods = core_component::get_plugin_list('mod');
301 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
302 return false;
303 } else {
304 $plugin = new stdClass();
305 $plugin->version = null;
306 $module = $plugin;
307 include($mods[$name].'/version.php');
308 return $plugin->version;
313 // block
314 if ($type === 'block') {
315 if ($source === 'installed') {
316 if ($CFG->version < 2013092001.02) {
317 return $DB->get_field('block', 'version', array('name'=>$name));
318 } else {
319 return get_config('block_'.$name, 'version');
321 } else {
322 $blocks = core_component::get_plugin_list('block');
323 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
324 return false;
325 } else {
326 $plugin = new stdclass();
327 include($blocks[$name].'/version.php');
328 return $plugin->version;
333 // all other plugin types
334 if ($source === 'installed') {
335 return get_config($type.'_'.$name, 'version');
336 } else {
337 $plugins = core_component::get_plugin_list($type);
338 if (empty($plugins[$name])) {
339 return false;
340 } else {
341 $plugin = new stdclass();
342 include($plugins[$name].'/version.php');
343 return $plugin->version;
349 * Delete all plugin tables
351 * @param string $name Name of plugin, used as table prefix
352 * @param string $file Path to install.xml file
353 * @param bool $feedback defaults to true
354 * @return bool Always returns true
356 function drop_plugin_tables($name, $file, $feedback=true) {
357 global $CFG, $DB;
359 // first try normal delete
360 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
361 return true;
364 // then try to find all tables that start with name and are not in any xml file
365 $used_tables = get_used_table_names();
367 $tables = $DB->get_tables();
369 /// Iterate over, fixing id fields as necessary
370 foreach ($tables as $table) {
371 if (in_array($table, $used_tables)) {
372 continue;
375 if (strpos($table, $name) !== 0) {
376 continue;
379 // found orphan table --> delete it
380 if ($DB->get_manager()->table_exists($table)) {
381 $xmldb_table = new xmldb_table($table);
382 $DB->get_manager()->drop_table($xmldb_table);
386 return true;
390 * Returns names of all known tables == tables that moodle knows about.
392 * @return array Array of lowercase table names
394 function get_used_table_names() {
395 $table_names = array();
396 $dbdirs = get_db_directories();
398 foreach ($dbdirs as $dbdir) {
399 $file = $dbdir.'/install.xml';
401 $xmldb_file = new xmldb_file($file);
403 if (!$xmldb_file->fileExists()) {
404 continue;
407 $loaded = $xmldb_file->loadXMLStructure();
408 $structure = $xmldb_file->getStructure();
410 if ($loaded and $tables = $structure->getTables()) {
411 foreach($tables as $table) {
412 $table_names[] = strtolower($table->getName());
417 return $table_names;
421 * Returns list of all directories where we expect install.xml files
422 * @return array Array of paths
424 function get_db_directories() {
425 global $CFG;
427 $dbdirs = array();
429 /// First, the main one (lib/db)
430 $dbdirs[] = $CFG->libdir.'/db';
432 /// Then, all the ones defined by core_component::get_plugin_types()
433 $plugintypes = core_component::get_plugin_types();
434 foreach ($plugintypes as $plugintype => $pluginbasedir) {
435 if ($plugins = core_component::get_plugin_list($plugintype)) {
436 foreach ($plugins as $plugin => $plugindir) {
437 $dbdirs[] = $plugindir.'/db';
442 return $dbdirs;
446 * Try to obtain or release the cron lock.
447 * @param string $name name of lock
448 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
449 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
450 * @return bool true if lock obtained
452 function set_cron_lock($name, $until, $ignorecurrent=false) {
453 global $DB;
454 if (empty($name)) {
455 debugging("Tried to get a cron lock for a null fieldname");
456 return false;
459 // remove lock by force == remove from config table
460 if (is_null($until)) {
461 set_config($name, null);
462 return true;
465 if (!$ignorecurrent) {
466 // read value from db - other processes might have changed it
467 $value = $DB->get_field('config', 'value', array('name'=>$name));
469 if ($value and $value > time()) {
470 //lock active
471 return false;
475 set_config($name, $until);
476 return true;
480 * Test if and critical warnings are present
481 * @return bool
483 function admin_critical_warnings_present() {
484 global $SESSION;
486 if (!has_capability('moodle/site:config', context_system::instance())) {
487 return 0;
490 if (!isset($SESSION->admin_critical_warning)) {
491 $SESSION->admin_critical_warning = 0;
492 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
493 $SESSION->admin_critical_warning = 1;
497 return $SESSION->admin_critical_warning;
501 * Detects if float supports at least 10 decimal digits
503 * Detects if float supports at least 10 decimal digits
504 * and also if float-->string conversion works as expected.
506 * @return bool true if problem found
508 function is_float_problem() {
509 $num1 = 2009010200.01;
510 $num2 = 2009010200.02;
512 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
516 * Try to verify that dataroot is not accessible from web.
518 * Try to verify that dataroot is not accessible from web.
519 * It is not 100% correct but might help to reduce number of vulnerable sites.
520 * Protection from httpd.conf and .htaccess is not detected properly.
522 * @uses INSECURE_DATAROOT_WARNING
523 * @uses INSECURE_DATAROOT_ERROR
524 * @param bool $fetchtest try to test public access by fetching file, default false
525 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
527 function is_dataroot_insecure($fetchtest=false) {
528 global $CFG;
530 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
532 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
533 $rp = strrev(trim($rp, '/'));
534 $rp = explode('/', $rp);
535 foreach($rp as $r) {
536 if (strpos($siteroot, '/'.$r.'/') === 0) {
537 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
538 } else {
539 break; // probably alias root
543 $siteroot = strrev($siteroot);
544 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
546 if (strpos($dataroot, $siteroot) !== 0) {
547 return false;
550 if (!$fetchtest) {
551 return INSECURE_DATAROOT_WARNING;
554 // now try all methods to fetch a test file using http protocol
556 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
557 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
558 $httpdocroot = $matches[1];
559 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
560 make_upload_directory('diag');
561 $testfile = $CFG->dataroot.'/diag/public.txt';
562 if (!file_exists($testfile)) {
563 file_put_contents($testfile, 'test file, do not delete');
564 @chmod($testfile, $CFG->filepermissions);
566 $teststr = trim(file_get_contents($testfile));
567 if (empty($teststr)) {
568 // hmm, strange
569 return INSECURE_DATAROOT_WARNING;
572 $testurl = $datarooturl.'/diag/public.txt';
573 if (extension_loaded('curl') and
574 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
575 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
576 ($ch = @curl_init($testurl)) !== false) {
577 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
578 curl_setopt($ch, CURLOPT_HEADER, false);
579 $data = curl_exec($ch);
580 if (!curl_errno($ch)) {
581 $data = trim($data);
582 if ($data === $teststr) {
583 curl_close($ch);
584 return INSECURE_DATAROOT_ERROR;
587 curl_close($ch);
590 if ($data = @file_get_contents($testurl)) {
591 $data = trim($data);
592 if ($data === $teststr) {
593 return INSECURE_DATAROOT_ERROR;
597 preg_match('|https?://([^/]+)|i', $testurl, $matches);
598 $sitename = $matches[1];
599 $error = 0;
600 if ($fp = @fsockopen($sitename, 80, $error)) {
601 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
602 $localurl = $matches[1];
603 $out = "GET $localurl HTTP/1.1\r\n";
604 $out .= "Host: $sitename\r\n";
605 $out .= "Connection: Close\r\n\r\n";
606 fwrite($fp, $out);
607 $data = '';
608 $incoming = false;
609 while (!feof($fp)) {
610 if ($incoming) {
611 $data .= fgets($fp, 1024);
612 } else if (@fgets($fp, 1024) === "\r\n") {
613 $incoming = true;
616 fclose($fp);
617 $data = trim($data);
618 if ($data === $teststr) {
619 return INSECURE_DATAROOT_ERROR;
623 return INSECURE_DATAROOT_WARNING;
627 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
629 function enable_cli_maintenance_mode() {
630 global $CFG;
632 if (file_exists("$CFG->dataroot/climaintenance.html")) {
633 unlink("$CFG->dataroot/climaintenance.html");
636 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
637 $data = $CFG->maintenance_message;
638 $data = bootstrap_renderer::early_error_content($data, null, null, null);
639 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
641 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
642 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
644 } else {
645 $data = get_string('sitemaintenance', 'admin');
646 $data = bootstrap_renderer::early_error_content($data, null, null, null);
647 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
650 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
651 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
654 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
658 * Interface for anything appearing in the admin tree
660 * The interface that is implemented by anything that appears in the admin tree
661 * block. It forces inheriting classes to define a method for checking user permissions
662 * and methods for finding something in the admin tree.
664 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
666 interface part_of_admin_tree {
669 * Finds a named part_of_admin_tree.
671 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
672 * and not parentable_part_of_admin_tree, then this function should only check if
673 * $this->name matches $name. If it does, it should return a reference to $this,
674 * otherwise, it should return a reference to NULL.
676 * If a class inherits parentable_part_of_admin_tree, this method should be called
677 * recursively on all child objects (assuming, of course, the parent object's name
678 * doesn't match the search criterion).
680 * @param string $name The internal name of the part_of_admin_tree we're searching for.
681 * @return mixed An object reference or a NULL reference.
683 public function locate($name);
686 * Removes named part_of_admin_tree.
688 * @param string $name The internal name of the part_of_admin_tree we want to remove.
689 * @return bool success.
691 public function prune($name);
694 * Search using query
695 * @param string $query
696 * @return mixed array-object structure of found settings and pages
698 public function search($query);
701 * Verifies current user's access to this part_of_admin_tree.
703 * Used to check if the current user has access to this part of the admin tree or
704 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
705 * then this method is usually just a call to has_capability() in the site context.
707 * If a class inherits parentable_part_of_admin_tree, this method should return the
708 * logical OR of the return of check_access() on all child objects.
710 * @return bool True if the user has access, false if she doesn't.
712 public function check_access();
715 * Mostly useful for removing of some parts of the tree in admin tree block.
717 * @return True is hidden from normal list view
719 public function is_hidden();
722 * Show we display Save button at the page bottom?
723 * @return bool
725 public function show_save();
730 * Interface implemented by any part_of_admin_tree that has children.
732 * The interface implemented by any part_of_admin_tree that can be a parent
733 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
734 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
735 * include an add method for adding other part_of_admin_tree objects as children.
737 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
739 interface parentable_part_of_admin_tree extends part_of_admin_tree {
742 * Adds a part_of_admin_tree object to the admin tree.
744 * Used to add a part_of_admin_tree object to this object or a child of this
745 * object. $something should only be added if $destinationname matches
746 * $this->name. If it doesn't, add should be called on child objects that are
747 * also parentable_part_of_admin_tree's.
749 * $something should be appended as the last child in the $destinationname. If the
750 * $beforesibling is specified, $something should be prepended to it. If the given
751 * sibling is not found, $something should be appended to the end of $destinationname
752 * and a developer debugging message should be displayed.
754 * @param string $destinationname The internal name of the new parent for $something.
755 * @param part_of_admin_tree $something The object to be added.
756 * @return bool True on success, false on failure.
758 public function add($destinationname, $something, $beforesibling = null);
764 * The object used to represent folders (a.k.a. categories) in the admin tree block.
766 * Each admin_category object contains a number of part_of_admin_tree objects.
768 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
770 class admin_category implements parentable_part_of_admin_tree {
772 /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
773 protected $children;
774 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
775 public $name;
776 /** @var string The displayed name for this category. Usually obtained through get_string() */
777 public $visiblename;
778 /** @var bool Should this category be hidden in admin tree block? */
779 public $hidden;
780 /** @var mixed Either a string or an array or strings */
781 public $path;
782 /** @var mixed Either a string or an array or strings */
783 public $visiblepath;
785 /** @var array fast lookup category cache, all categories of one tree point to one cache */
786 protected $category_cache;
788 /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
789 protected $sort = false;
790 /** @var bool If set to true children will be sorted in ascending order. */
791 protected $sortasc = true;
792 /** @var bool If set to true sub categories and pages will be split and then sorted.. */
793 protected $sortsplit = true;
794 /** @var bool $sorted True if the children have been sorted and don't need resorting */
795 protected $sorted = false;
798 * Constructor for an empty admin category
800 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
801 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
802 * @param bool $hidden hide category in admin tree block, defaults to false
804 public function __construct($name, $visiblename, $hidden=false) {
805 $this->children = array();
806 $this->name = $name;
807 $this->visiblename = $visiblename;
808 $this->hidden = $hidden;
812 * Returns a reference to the part_of_admin_tree object with internal name $name.
814 * @param string $name The internal name of the object we want.
815 * @param bool $findpath initialize path and visiblepath arrays
816 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
817 * defaults to false
819 public function locate($name, $findpath=false) {
820 if (!isset($this->category_cache[$this->name])) {
821 // somebody much have purged the cache
822 $this->category_cache[$this->name] = $this;
825 if ($this->name == $name) {
826 if ($findpath) {
827 $this->visiblepath[] = $this->visiblename;
828 $this->path[] = $this->name;
830 return $this;
833 // quick category lookup
834 if (!$findpath and isset($this->category_cache[$name])) {
835 return $this->category_cache[$name];
838 $return = NULL;
839 foreach($this->children as $childid=>$unused) {
840 if ($return = $this->children[$childid]->locate($name, $findpath)) {
841 break;
845 if (!is_null($return) and $findpath) {
846 $return->visiblepath[] = $this->visiblename;
847 $return->path[] = $this->name;
850 return $return;
854 * Search using query
856 * @param string query
857 * @return mixed array-object structure of found settings and pages
859 public function search($query) {
860 $result = array();
861 foreach ($this->get_children() as $child) {
862 $subsearch = $child->search($query);
863 if (!is_array($subsearch)) {
864 debugging('Incorrect search result from '.$child->name);
865 continue;
867 $result = array_merge($result, $subsearch);
869 return $result;
873 * Removes part_of_admin_tree object with internal name $name.
875 * @param string $name The internal name of the object we want to remove.
876 * @return bool success
878 public function prune($name) {
880 if ($this->name == $name) {
881 return false; //can not remove itself
884 foreach($this->children as $precedence => $child) {
885 if ($child->name == $name) {
886 // clear cache and delete self
887 while($this->category_cache) {
888 // delete the cache, but keep the original array address
889 array_pop($this->category_cache);
891 unset($this->children[$precedence]);
892 return true;
893 } else if ($this->children[$precedence]->prune($name)) {
894 return true;
897 return false;
901 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
903 * By default the new part of the tree is appended as the last child of the parent. You
904 * can specify a sibling node that the new part should be prepended to. If the given
905 * sibling is not found, the part is appended to the end (as it would be by default) and
906 * a developer debugging message is displayed.
908 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
909 * @param string $destinationame The internal name of the immediate parent that we want for $something.
910 * @param mixed $something A part_of_admin_tree or setting instance to be added.
911 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
912 * @return bool True if successfully added, false if $something can not be added.
914 public function add($parentname, $something, $beforesibling = null) {
915 global $CFG;
917 $parent = $this->locate($parentname);
918 if (is_null($parent)) {
919 debugging('parent does not exist!');
920 return false;
923 if ($something instanceof part_of_admin_tree) {
924 if (!($parent instanceof parentable_part_of_admin_tree)) {
925 debugging('error - parts of tree can be inserted only into parentable parts');
926 return false;
928 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
929 // The name of the node is already used, simply warn the developer that this should not happen.
930 // It is intentional to check for the debug level before performing the check.
931 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
933 if (is_null($beforesibling)) {
934 // Append $something as the parent's last child.
935 $parent->children[] = $something;
936 } else {
937 if (!is_string($beforesibling) or trim($beforesibling) === '') {
938 throw new coding_exception('Unexpected value of the beforesibling parameter');
940 // Try to find the position of the sibling.
941 $siblingposition = null;
942 foreach ($parent->children as $childposition => $child) {
943 if ($child->name === $beforesibling) {
944 $siblingposition = $childposition;
945 break;
948 if (is_null($siblingposition)) {
949 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
950 $parent->children[] = $something;
951 } else {
952 $parent->children = array_merge(
953 array_slice($parent->children, 0, $siblingposition),
954 array($something),
955 array_slice($parent->children, $siblingposition)
959 if ($something instanceof admin_category) {
960 if (isset($this->category_cache[$something->name])) {
961 debugging('Duplicate admin category name: '.$something->name);
962 } else {
963 $this->category_cache[$something->name] = $something;
964 $something->category_cache =& $this->category_cache;
965 foreach ($something->children as $child) {
966 // just in case somebody already added subcategories
967 if ($child instanceof admin_category) {
968 if (isset($this->category_cache[$child->name])) {
969 debugging('Duplicate admin category name: '.$child->name);
970 } else {
971 $this->category_cache[$child->name] = $child;
972 $child->category_cache =& $this->category_cache;
978 return true;
980 } else {
981 debugging('error - can not add this element');
982 return false;
988 * Checks if the user has access to anything in this category.
990 * @return bool True if the user has access to at least one child in this category, false otherwise.
992 public function check_access() {
993 foreach ($this->children as $child) {
994 if ($child->check_access()) {
995 return true;
998 return false;
1002 * Is this category hidden in admin tree block?
1004 * @return bool True if hidden
1006 public function is_hidden() {
1007 return $this->hidden;
1011 * Show we display Save button at the page bottom?
1012 * @return bool
1014 public function show_save() {
1015 foreach ($this->children as $child) {
1016 if ($child->show_save()) {
1017 return true;
1020 return false;
1024 * Sets sorting on this category.
1026 * Please note this function doesn't actually do the sorting.
1027 * It can be called anytime.
1028 * Sorting occurs when the user calls get_children.
1029 * Code using the children array directly won't see the sorted results.
1031 * @param bool $sort If set to true children will be sorted, if false they won't be.
1032 * @param bool $asc If true sorting will be ascending, otherwise descending.
1033 * @param bool $split If true we sort pages and sub categories separately.
1035 public function set_sorting($sort, $asc = true, $split = true) {
1036 $this->sort = (bool)$sort;
1037 $this->sortasc = (bool)$asc;
1038 $this->sortsplit = (bool)$split;
1042 * Returns the children associated with this category.
1044 * @return part_of_admin_tree[]
1046 public function get_children() {
1047 // If we should sort and it hasn't already been sorted.
1048 if ($this->sort && !$this->sorted) {
1049 if ($this->sortsplit) {
1050 $categories = array();
1051 $pages = array();
1052 foreach ($this->children as $child) {
1053 if ($child instanceof admin_category) {
1054 $categories[] = $child;
1055 } else {
1056 $pages[] = $child;
1059 core_collator::asort_objects_by_property($categories, 'visiblename');
1060 core_collator::asort_objects_by_property($pages, 'visiblename');
1061 if (!$this->sortasc) {
1062 $categories = array_reverse($categories);
1063 $pages = array_reverse($pages);
1065 $this->children = array_merge($pages, $categories);
1066 } else {
1067 core_collator::asort_objects_by_property($this->children, 'visiblename');
1068 if (!$this->sortasc) {
1069 $this->children = array_reverse($this->children);
1072 $this->sorted = true;
1074 return $this->children;
1078 * Magically gets a property from this object.
1080 * @param $property
1081 * @return part_of_admin_tree[]
1082 * @throws coding_exception
1084 public function __get($property) {
1085 if ($property === 'children') {
1086 return $this->get_children();
1088 throw new coding_exception('Invalid property requested.');
1092 * Magically sets a property against this object.
1094 * @param string $property
1095 * @param mixed $value
1096 * @throws coding_exception
1098 public function __set($property, $value) {
1099 if ($property === 'children') {
1100 $this->sorted = false;
1101 $this->children = $value;
1102 } else {
1103 throw new coding_exception('Invalid property requested.');
1108 * Checks if an inaccessible property is set.
1110 * @param string $property
1111 * @return bool
1112 * @throws coding_exception
1114 public function __isset($property) {
1115 if ($property === 'children') {
1116 return isset($this->children);
1118 throw new coding_exception('Invalid property requested.');
1124 * Root of admin settings tree, does not have any parent.
1126 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1128 class admin_root extends admin_category {
1129 /** @var array List of errors */
1130 public $errors;
1131 /** @var string search query */
1132 public $search;
1133 /** @var bool full tree flag - true means all settings required, false only pages required */
1134 public $fulltree;
1135 /** @var bool flag indicating loaded tree */
1136 public $loaded;
1137 /** @var mixed site custom defaults overriding defaults in settings files*/
1138 public $custom_defaults;
1141 * @param bool $fulltree true means all settings required,
1142 * false only pages required
1144 public function __construct($fulltree) {
1145 global $CFG;
1147 parent::__construct('root', get_string('administration'), false);
1148 $this->errors = array();
1149 $this->search = '';
1150 $this->fulltree = $fulltree;
1151 $this->loaded = false;
1153 $this->category_cache = array();
1155 // load custom defaults if found
1156 $this->custom_defaults = null;
1157 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1158 if (is_readable($defaultsfile)) {
1159 $defaults = array();
1160 include($defaultsfile);
1161 if (is_array($defaults) and count($defaults)) {
1162 $this->custom_defaults = $defaults;
1168 * Empties children array, and sets loaded to false
1170 * @param bool $requirefulltree
1172 public function purge_children($requirefulltree) {
1173 $this->children = array();
1174 $this->fulltree = ($requirefulltree || $this->fulltree);
1175 $this->loaded = false;
1176 //break circular dependencies - this helps PHP 5.2
1177 while($this->category_cache) {
1178 array_pop($this->category_cache);
1180 $this->category_cache = array();
1186 * Links external PHP pages into the admin tree.
1188 * See detailed usage example at the top of this document (adminlib.php)
1190 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1192 class admin_externalpage implements part_of_admin_tree {
1194 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1195 public $name;
1197 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1198 public $visiblename;
1200 /** @var string The external URL that we should link to when someone requests this external page. */
1201 public $url;
1203 /** @var string The role capability/permission a user must have to access this external page. */
1204 public $req_capability;
1206 /** @var object The context in which capability/permission should be checked, default is site context. */
1207 public $context;
1209 /** @var bool hidden in admin tree block. */
1210 public $hidden;
1212 /** @var mixed either string or array of string */
1213 public $path;
1215 /** @var array list of visible names of page parents */
1216 public $visiblepath;
1219 * Constructor for adding an external page into the admin tree.
1221 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1222 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1223 * @param string $url The external URL that we should link to when someone requests this external page.
1224 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1225 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1226 * @param stdClass $context The context the page relates to. Not sure what happens
1227 * if you specify something other than system or front page. Defaults to system.
1229 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1230 $this->name = $name;
1231 $this->visiblename = $visiblename;
1232 $this->url = $url;
1233 if (is_array($req_capability)) {
1234 $this->req_capability = $req_capability;
1235 } else {
1236 $this->req_capability = array($req_capability);
1238 $this->hidden = $hidden;
1239 $this->context = $context;
1243 * Returns a reference to the part_of_admin_tree object with internal name $name.
1245 * @param string $name The internal name of the object we want.
1246 * @param bool $findpath defaults to false
1247 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1249 public function locate($name, $findpath=false) {
1250 if ($this->name == $name) {
1251 if ($findpath) {
1252 $this->visiblepath = array($this->visiblename);
1253 $this->path = array($this->name);
1255 return $this;
1256 } else {
1257 $return = NULL;
1258 return $return;
1263 * This function always returns false, required function by interface
1265 * @param string $name
1266 * @return false
1268 public function prune($name) {
1269 return false;
1273 * Search using query
1275 * @param string $query
1276 * @return mixed array-object structure of found settings and pages
1278 public function search($query) {
1279 $found = false;
1280 if (strpos(strtolower($this->name), $query) !== false) {
1281 $found = true;
1282 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1283 $found = true;
1285 if ($found) {
1286 $result = new stdClass();
1287 $result->page = $this;
1288 $result->settings = array();
1289 return array($this->name => $result);
1290 } else {
1291 return array();
1296 * Determines if the current user has access to this external page based on $this->req_capability.
1298 * @return bool True if user has access, false otherwise.
1300 public function check_access() {
1301 global $CFG;
1302 $context = empty($this->context) ? context_system::instance() : $this->context;
1303 foreach($this->req_capability as $cap) {
1304 if (has_capability($cap, $context)) {
1305 return true;
1308 return false;
1312 * Is this external page hidden in admin tree block?
1314 * @return bool True if hidden
1316 public function is_hidden() {
1317 return $this->hidden;
1321 * Show we display Save button at the page bottom?
1322 * @return bool
1324 public function show_save() {
1325 return false;
1330 * Used to store details of the dependency between two settings elements.
1332 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1333 * @copyright 2017 Davo Smith, Synergy Learning
1335 class admin_settingdependency {
1336 /** @var string the name of the setting to be shown/hidden */
1337 public $settingname;
1338 /** @var string the setting this is dependent on */
1339 public $dependenton;
1340 /** @var string the condition to show/hide the element */
1341 public $condition;
1342 /** @var string the value to compare against */
1343 public $value;
1345 /** @var string[] list of valid conditions */
1346 private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1349 * admin_settingdependency constructor.
1350 * @param string $settingname
1351 * @param string $dependenton
1352 * @param string $condition
1353 * @param string $value
1354 * @throws \coding_exception
1356 public function __construct($settingname, $dependenton, $condition, $value) {
1357 $this->settingname = $this->parse_name($settingname);
1358 $this->dependenton = $this->parse_name($dependenton);
1359 $this->condition = $condition;
1360 $this->value = $value;
1362 if (!in_array($this->condition, self::$validconditions)) {
1363 throw new coding_exception("Invalid condition '$condition'");
1368 * Convert the setting name into the form field name.
1369 * @param string $name
1370 * @return string
1372 private function parse_name($name) {
1373 $bits = explode('/', $name);
1374 $name = array_pop($bits);
1375 $plugin = '';
1376 if ($bits) {
1377 $plugin = array_pop($bits);
1378 if ($plugin === 'moodle') {
1379 $plugin = '';
1382 return 's_'.$plugin.'_'.$name;
1386 * Gather together all the dependencies in a format suitable for initialising javascript
1387 * @param admin_settingdependency[] $dependencies
1388 * @return array
1390 public static function prepare_for_javascript($dependencies) {
1391 $result = [];
1392 foreach ($dependencies as $d) {
1393 if (!isset($result[$d->dependenton])) {
1394 $result[$d->dependenton] = [];
1396 if (!isset($result[$d->dependenton][$d->condition])) {
1397 $result[$d->dependenton][$d->condition] = [];
1399 if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1400 $result[$d->dependenton][$d->condition][$d->value] = [];
1402 $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1404 return $result;
1409 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1411 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1413 class admin_settingpage implements part_of_admin_tree {
1415 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1416 public $name;
1418 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1419 public $visiblename;
1421 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1422 public $settings;
1424 /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1425 protected $dependencies = [];
1427 /** @var string The role capability/permission a user must have to access this external page. */
1428 public $req_capability;
1430 /** @var object The context in which capability/permission should be checked, default is site context. */
1431 public $context;
1433 /** @var bool hidden in admin tree block. */
1434 public $hidden;
1436 /** @var mixed string of paths or array of strings of paths */
1437 public $path;
1439 /** @var array list of visible names of page parents */
1440 public $visiblepath;
1443 * see admin_settingpage for details of this function
1445 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1446 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1447 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1448 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1449 * @param stdClass $context The context the page relates to. Not sure what happens
1450 * if you specify something other than system or front page. Defaults to system.
1452 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1453 $this->settings = new stdClass();
1454 $this->name = $name;
1455 $this->visiblename = $visiblename;
1456 if (is_array($req_capability)) {
1457 $this->req_capability = $req_capability;
1458 } else {
1459 $this->req_capability = array($req_capability);
1461 $this->hidden = $hidden;
1462 $this->context = $context;
1466 * see admin_category
1468 * @param string $name
1469 * @param bool $findpath
1470 * @return mixed Object (this) if name == this->name, else returns null
1472 public function locate($name, $findpath=false) {
1473 if ($this->name == $name) {
1474 if ($findpath) {
1475 $this->visiblepath = array($this->visiblename);
1476 $this->path = array($this->name);
1478 return $this;
1479 } else {
1480 $return = NULL;
1481 return $return;
1486 * Search string in settings page.
1488 * @param string $query
1489 * @return array
1491 public function search($query) {
1492 $found = array();
1494 foreach ($this->settings as $setting) {
1495 if ($setting->is_related($query)) {
1496 $found[] = $setting;
1500 if ($found) {
1501 $result = new stdClass();
1502 $result->page = $this;
1503 $result->settings = $found;
1504 return array($this->name => $result);
1507 $found = false;
1508 if (strpos(strtolower($this->name), $query) !== false) {
1509 $found = true;
1510 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1511 $found = true;
1513 if ($found) {
1514 $result = new stdClass();
1515 $result->page = $this;
1516 $result->settings = array();
1517 return array($this->name => $result);
1518 } else {
1519 return array();
1524 * This function always returns false, required by interface
1526 * @param string $name
1527 * @return bool Always false
1529 public function prune($name) {
1530 return false;
1534 * adds an admin_setting to this admin_settingpage
1536 * 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
1537 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1539 * @param object $setting is the admin_setting object you want to add
1540 * @return bool true if successful, false if not
1542 public function add($setting) {
1543 if (!($setting instanceof admin_setting)) {
1544 debugging('error - not a setting instance');
1545 return false;
1548 $name = $setting->name;
1549 if ($setting->plugin) {
1550 $name = $setting->plugin . $name;
1552 $this->settings->{$name} = $setting;
1553 return true;
1557 * Hide the named setting if the specified condition is matched.
1559 * @param string $settingname
1560 * @param string $dependenton
1561 * @param string $condition
1562 * @param string $value
1564 public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1565 $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1567 // Reformat the dependency name to the plugin | name format used in the display.
1568 $dependenton = str_replace('/', ' | ', $dependenton);
1570 // Let the setting know, so it can be displayed underneath.
1571 $findname = str_replace('/', '', $settingname);
1572 foreach ($this->settings as $name => $setting) {
1573 if ($name === $findname) {
1574 $setting->add_dependent_on($dependenton);
1580 * see admin_externalpage
1582 * @return bool Returns true for yes false for no
1584 public function check_access() {
1585 global $CFG;
1586 $context = empty($this->context) ? context_system::instance() : $this->context;
1587 foreach($this->req_capability as $cap) {
1588 if (has_capability($cap, $context)) {
1589 return true;
1592 return false;
1596 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1597 * @return string Returns an XHTML string
1599 public function output_html() {
1600 $adminroot = admin_get_root();
1601 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1602 foreach($this->settings as $setting) {
1603 $fullname = $setting->get_full_name();
1604 if (array_key_exists($fullname, $adminroot->errors)) {
1605 $data = $adminroot->errors[$fullname]->data;
1606 } else {
1607 $data = $setting->get_setting();
1608 // do not use defaults if settings not available - upgrade settings handles the defaults!
1610 $return .= $setting->output_html($data);
1612 $return .= '</fieldset>';
1613 return $return;
1617 * Is this settings page hidden in admin tree block?
1619 * @return bool True if hidden
1621 public function is_hidden() {
1622 return $this->hidden;
1626 * Show we display Save button at the page bottom?
1627 * @return bool
1629 public function show_save() {
1630 foreach($this->settings as $setting) {
1631 if (empty($setting->nosave)) {
1632 return true;
1635 return false;
1639 * Should any of the settings on this page be shown / hidden based on conditions?
1640 * @return bool
1642 public function has_dependencies() {
1643 return (bool)$this->dependencies;
1647 * Format the setting show/hide conditions ready to initialise the page javascript
1648 * @return array
1650 public function get_dependencies_for_javascript() {
1651 if (!$this->has_dependencies()) {
1652 return [];
1654 return admin_settingdependency::prepare_for_javascript($this->dependencies);
1660 * Admin settings class. Only exists on setting pages.
1661 * Read & write happens at this level; no authentication.
1663 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1665 abstract class admin_setting {
1666 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1667 public $name;
1668 /** @var string localised name */
1669 public $visiblename;
1670 /** @var string localised long description in Markdown format */
1671 public $description;
1672 /** @var mixed Can be string or array of string */
1673 public $defaultsetting;
1674 /** @var string */
1675 public $updatedcallback;
1676 /** @var mixed can be String or Null. Null means main config table */
1677 public $plugin; // null means main config table
1678 /** @var bool true indicates this setting does not actually save anything, just information */
1679 public $nosave = false;
1680 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1681 public $affectsmodinfo = false;
1682 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1683 private $flags = array();
1684 /** @var bool Whether this field must be forced LTR. */
1685 private $forceltr = null;
1686 /** @var array list of other settings that may cause this setting to be hidden */
1687 private $dependenton = [];
1690 * Constructor
1691 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1692 * or 'myplugin/mysetting' for ones in config_plugins.
1693 * @param string $visiblename localised name
1694 * @param string $description localised long description
1695 * @param mixed $defaultsetting string or array depending on implementation
1697 public function __construct($name, $visiblename, $description, $defaultsetting) {
1698 $this->parse_setting_name($name);
1699 $this->visiblename = $visiblename;
1700 $this->description = $description;
1701 $this->defaultsetting = $defaultsetting;
1705 * Generic function to add a flag to this admin setting.
1707 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1708 * @param bool $default - The default for the flag
1709 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1710 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1712 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1713 if (empty($this->flags[$shortname])) {
1714 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1715 } else {
1716 $this->flags[$shortname]->set_options($enabled, $default);
1721 * Set the enabled options flag on this admin setting.
1723 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1724 * @param bool $default - The default for the flag
1726 public function set_enabled_flag_options($enabled, $default) {
1727 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1731 * Set the advanced options flag on this admin setting.
1733 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1734 * @param bool $default - The default for the flag
1736 public function set_advanced_flag_options($enabled, $default) {
1737 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1742 * Set the locked options flag on this admin setting.
1744 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1745 * @param bool $default - The default for the flag
1747 public function set_locked_flag_options($enabled, $default) {
1748 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1752 * Set the required options flag on this admin setting.
1754 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1755 * @param bool $default - The default for the flag.
1757 public function set_required_flag_options($enabled, $default) {
1758 $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1762 * Get the currently saved value for a setting flag
1764 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1765 * @return bool
1767 public function get_setting_flag_value(admin_setting_flag $flag) {
1768 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1769 if (!isset($value)) {
1770 $value = $flag->get_default();
1773 return !empty($value);
1777 * Get the list of defaults for the flags on this setting.
1779 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1781 public function get_setting_flag_defaults(& $defaults) {
1782 foreach ($this->flags as $flag) {
1783 if ($flag->is_enabled() && $flag->get_default()) {
1784 $defaults[] = $flag->get_displayname();
1790 * Output the input fields for the advanced and locked flags on this setting.
1792 * @param bool $adv - The current value of the advanced flag.
1793 * @param bool $locked - The current value of the locked flag.
1794 * @return string $output - The html for the flags.
1796 public function output_setting_flags() {
1797 $output = '';
1799 foreach ($this->flags as $flag) {
1800 if ($flag->is_enabled()) {
1801 $output .= $flag->output_setting_flag($this);
1805 if (!empty($output)) {
1806 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1808 return $output;
1812 * Write the values of the flags for this admin setting.
1814 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1815 * @return bool - true if successful.
1817 public function write_setting_flags($data) {
1818 $result = true;
1819 foreach ($this->flags as $flag) {
1820 $result = $result && $flag->write_setting_flag($this, $data);
1822 return $result;
1826 * Set up $this->name and potentially $this->plugin
1828 * Set up $this->name and possibly $this->plugin based on whether $name looks
1829 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1830 * on the names, that is, output a developer debug warning if the name
1831 * contains anything other than [a-zA-Z0-9_]+.
1833 * @param string $name the setting name passed in to the constructor.
1835 private function parse_setting_name($name) {
1836 $bits = explode('/', $name);
1837 if (count($bits) > 2) {
1838 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1840 $this->name = array_pop($bits);
1841 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1842 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1844 if (!empty($bits)) {
1845 $this->plugin = array_pop($bits);
1846 if ($this->plugin === 'moodle') {
1847 $this->plugin = null;
1848 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1849 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1855 * Returns the fullname prefixed by the plugin
1856 * @return string
1858 public function get_full_name() {
1859 return 's_'.$this->plugin.'_'.$this->name;
1863 * Returns the ID string based on plugin and name
1864 * @return string
1866 public function get_id() {
1867 return 'id_s_'.$this->plugin.'_'.$this->name;
1871 * @param bool $affectsmodinfo If true, changes to this setting will
1872 * cause the course cache to be rebuilt
1874 public function set_affects_modinfo($affectsmodinfo) {
1875 $this->affectsmodinfo = $affectsmodinfo;
1879 * Returns the config if possible
1881 * @return mixed returns config if successful else null
1883 public function config_read($name) {
1884 global $CFG;
1885 if (!empty($this->plugin)) {
1886 $value = get_config($this->plugin, $name);
1887 return $value === false ? NULL : $value;
1889 } else {
1890 if (isset($CFG->$name)) {
1891 return $CFG->$name;
1892 } else {
1893 return NULL;
1899 * Used to set a config pair and log change
1901 * @param string $name
1902 * @param mixed $value Gets converted to string if not null
1903 * @return bool Write setting to config table
1905 public function config_write($name, $value) {
1906 global $DB, $USER, $CFG;
1908 if ($this->nosave) {
1909 return true;
1912 // make sure it is a real change
1913 $oldvalue = get_config($this->plugin, $name);
1914 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1915 $value = is_null($value) ? null : (string)$value;
1917 if ($oldvalue === $value) {
1918 return true;
1921 // store change
1922 set_config($name, $value, $this->plugin);
1924 // Some admin settings affect course modinfo
1925 if ($this->affectsmodinfo) {
1926 // Clear course cache for all courses
1927 rebuild_course_cache(0, true);
1930 $this->add_to_config_log($name, $oldvalue, $value);
1932 return true; // BC only
1936 * Log config changes if necessary.
1937 * @param string $name
1938 * @param string $oldvalue
1939 * @param string $value
1941 protected function add_to_config_log($name, $oldvalue, $value) {
1942 add_to_config_log($name, $oldvalue, $value, $this->plugin);
1946 * Returns current value of this setting
1947 * @return mixed array or string depending on instance, NULL means not set yet
1949 public abstract function get_setting();
1952 * Returns default setting if exists
1953 * @return mixed array or string depending on instance; NULL means no default, user must supply
1955 public function get_defaultsetting() {
1956 $adminroot = admin_get_root(false, false);
1957 if (!empty($adminroot->custom_defaults)) {
1958 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1959 if (isset($adminroot->custom_defaults[$plugin])) {
1960 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1961 return $adminroot->custom_defaults[$plugin][$this->name];
1965 return $this->defaultsetting;
1969 * Store new setting
1971 * @param mixed $data string or array, must not be NULL
1972 * @return string empty string if ok, string error message otherwise
1974 public abstract function write_setting($data);
1977 * Return part of form with setting
1978 * This function should always be overwritten
1980 * @param mixed $data array or string depending on setting
1981 * @param string $query
1982 * @return string
1984 public function output_html($data, $query='') {
1985 // should be overridden
1986 return;
1990 * Function called if setting updated - cleanup, cache reset, etc.
1991 * @param string $functionname Sets the function name
1992 * @return void
1994 public function set_updatedcallback($functionname) {
1995 $this->updatedcallback = $functionname;
1999 * Execute postupdatecallback if necessary.
2000 * @param mixed $original original value before write_setting()
2001 * @return bool true if changed, false if not.
2003 public function post_write_settings($original) {
2004 // Comparison must work for arrays too.
2005 if (serialize($original) === serialize($this->get_setting())) {
2006 return false;
2009 $callbackfunction = $this->updatedcallback;
2010 if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2011 $callbackfunction($this->get_full_name());
2013 return true;
2017 * Is setting related to query text - used when searching
2018 * @param string $query
2019 * @return bool
2021 public function is_related($query) {
2022 if (strpos(strtolower($this->name), $query) !== false) {
2023 return true;
2025 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2026 return true;
2028 if (strpos(core_text::strtolower($this->description), $query) !== false) {
2029 return true;
2031 $current = $this->get_setting();
2032 if (!is_null($current)) {
2033 if (is_string($current)) {
2034 if (strpos(core_text::strtolower($current), $query) !== false) {
2035 return true;
2039 $default = $this->get_defaultsetting();
2040 if (!is_null($default)) {
2041 if (is_string($default)) {
2042 if (strpos(core_text::strtolower($default), $query) !== false) {
2043 return true;
2047 return false;
2051 * Get whether this should be displayed in LTR mode.
2053 * @return bool|null
2055 public function get_force_ltr() {
2056 return $this->forceltr;
2060 * Set whether to force LTR or not.
2062 * @param bool $value True when forced, false when not force, null when unknown.
2064 public function set_force_ltr($value) {
2065 $this->forceltr = $value;
2069 * Add a setting to the list of those that could cause this one to be hidden
2070 * @param string $dependenton
2072 public function add_dependent_on($dependenton) {
2073 $this->dependenton[] = $dependenton;
2077 * Get a list of the settings that could cause this one to be hidden.
2078 * @return array
2080 public function get_dependent_on() {
2081 return $this->dependenton;
2086 * An additional option that can be applied to an admin setting.
2087 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2089 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2091 class admin_setting_flag {
2092 /** @var bool Flag to indicate if this option can be toggled for this setting */
2093 private $enabled = false;
2094 /** @var bool Flag to indicate if this option defaults to true or false */
2095 private $default = false;
2096 /** @var string Short string used to create setting name - e.g. 'adv' */
2097 private $shortname = '';
2098 /** @var string String used as the label for this flag */
2099 private $displayname = '';
2100 /** @const Checkbox for this flag is displayed in admin page */
2101 const ENABLED = true;
2102 /** @const Checkbox for this flag is not displayed in admin page */
2103 const DISABLED = false;
2106 * Constructor
2108 * @param bool $enabled Can this option can be toggled.
2109 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2110 * @param bool $default The default checked state for this setting option.
2111 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2112 * @param string $displayname The displayname of this flag. Used as a label for the flag.
2114 public function __construct($enabled, $default, $shortname, $displayname) {
2115 $this->shortname = $shortname;
2116 $this->displayname = $displayname;
2117 $this->set_options($enabled, $default);
2121 * Update the values of this setting options class
2123 * @param bool $enabled Can this option can be toggled.
2124 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2125 * @param bool $default The default checked state for this setting option.
2127 public function set_options($enabled, $default) {
2128 $this->enabled = $enabled;
2129 $this->default = $default;
2133 * Should this option appear in the interface and be toggleable?
2135 * @return bool Is it enabled?
2137 public function is_enabled() {
2138 return $this->enabled;
2142 * Should this option be checked by default?
2144 * @return bool Is it on by default?
2146 public function get_default() {
2147 return $this->default;
2151 * Return the short name for this flag. e.g. 'adv' or 'locked'
2153 * @return string
2155 public function get_shortname() {
2156 return $this->shortname;
2160 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2162 * @return string
2164 public function get_displayname() {
2165 return $this->displayname;
2169 * Save the submitted data for this flag - or set it to the default if $data is null.
2171 * @param admin_setting $setting - The admin setting for this flag
2172 * @param array $data - The data submitted from the form or null to set the default value for new installs.
2173 * @return bool
2175 public function write_setting_flag(admin_setting $setting, $data) {
2176 $result = true;
2177 if ($this->is_enabled()) {
2178 if (!isset($data)) {
2179 $value = $this->get_default();
2180 } else {
2181 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2183 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2186 return $result;
2191 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2193 * @param admin_setting $setting - The admin setting for this flag
2194 * @return string - The html for the checkbox.
2196 public function output_setting_flag(admin_setting $setting) {
2197 global $OUTPUT;
2199 $value = $setting->get_setting_flag_value($this);
2201 $context = new stdClass();
2202 $context->id = $setting->get_id() . '_' . $this->get_shortname();
2203 $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
2204 $context->value = 1;
2205 $context->checked = $value ? true : false;
2206 $context->label = $this->get_displayname();
2208 return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2214 * No setting - just heading and text.
2216 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2218 class admin_setting_heading extends admin_setting {
2221 * not a setting, just text
2222 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2223 * @param string $heading heading
2224 * @param string $information text in box
2226 public function __construct($name, $heading, $information) {
2227 $this->nosave = true;
2228 parent::__construct($name, $heading, $information, '');
2232 * Always returns true
2233 * @return bool Always returns true
2235 public function get_setting() {
2236 return true;
2240 * Always returns true
2241 * @return bool Always returns true
2243 public function get_defaultsetting() {
2244 return true;
2248 * Never write settings
2249 * @return string Always returns an empty string
2251 public function write_setting($data) {
2252 // do not write any setting
2253 return '';
2257 * Returns an HTML string
2258 * @return string Returns an HTML string
2260 public function output_html($data, $query='') {
2261 global $OUTPUT;
2262 $context = new stdClass();
2263 $context->title = $this->visiblename;
2264 $context->description = $this->description;
2265 $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2266 return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2271 * No setting - just name and description in same row.
2273 * @copyright 2018 onwards Amaia Anabitarte
2274 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2276 class admin_setting_description extends admin_setting {
2279 * Not a setting, just text
2281 * @param string $name
2282 * @param string $visiblename
2283 * @param string $description
2285 public function __construct($name, $visiblename, $description) {
2286 $this->nosave = true;
2287 parent::__construct($name, $visiblename, $description, '');
2291 * Always returns true
2293 * @return bool Always returns true
2295 public function get_setting() {
2296 return true;
2300 * Always returns true
2302 * @return bool Always returns true
2304 public function get_defaultsetting() {
2305 return true;
2309 * Never write settings
2311 * @param mixed $data Gets converted to str for comparison against yes value
2312 * @return string Always returns an empty string
2314 public function write_setting($data) {
2315 // Do not write any setting.
2316 return '';
2320 * Returns an HTML string
2322 * @param string $data
2323 * @param string $query
2324 * @return string Returns an HTML string
2326 public function output_html($data, $query='') {
2327 global $OUTPUT;
2329 $context = new stdClass();
2330 $context->title = $this->visiblename;
2331 $context->description = $this->description;
2333 return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2340 * The most flexible setting, the user enters text.
2342 * This type of field should be used for config settings which are using
2343 * English words and are not localised (passwords, database name, list of values, ...).
2345 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2347 class admin_setting_configtext extends admin_setting {
2349 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
2350 public $paramtype;
2351 /** @var int default field size */
2352 public $size;
2355 * Config text constructor
2357 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2358 * @param string $visiblename localised
2359 * @param string $description long localised info
2360 * @param string $defaultsetting
2361 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2362 * @param int $size default field size
2364 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2365 $this->paramtype = $paramtype;
2366 if (!is_null($size)) {
2367 $this->size = $size;
2368 } else {
2369 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
2371 parent::__construct($name, $visiblename, $description, $defaultsetting);
2375 * Get whether this should be displayed in LTR mode.
2377 * Try to guess from the PARAM type unless specifically set.
2379 public function get_force_ltr() {
2380 $forceltr = parent::get_force_ltr();
2381 if ($forceltr === null) {
2382 return !is_rtl_compatible($this->paramtype);
2384 return $forceltr;
2388 * Return the setting
2390 * @return mixed returns config if successful else null
2392 public function get_setting() {
2393 return $this->config_read($this->name);
2396 public function write_setting($data) {
2397 if ($this->paramtype === PARAM_INT and $data === '') {
2398 // do not complain if '' used instead of 0
2399 $data = 0;
2401 // $data is a string
2402 $validated = $this->validate($data);
2403 if ($validated !== true) {
2404 return $validated;
2406 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2410 * Validate data before storage
2411 * @param string data
2412 * @return mixed true if ok string if error found
2414 public function validate($data) {
2415 // allow paramtype to be a custom regex if it is the form of /pattern/
2416 if (preg_match('#^/.*/$#', $this->paramtype)) {
2417 if (preg_match($this->paramtype, $data)) {
2418 return true;
2419 } else {
2420 return get_string('validateerror', 'admin');
2423 } else if ($this->paramtype === PARAM_RAW) {
2424 return true;
2426 } else {
2427 $cleaned = clean_param($data, $this->paramtype);
2428 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2429 return true;
2430 } else {
2431 return get_string('validateerror', 'admin');
2437 * Return an XHTML string for the setting
2438 * @return string Returns an XHTML string
2440 public function output_html($data, $query='') {
2441 global $OUTPUT;
2443 $default = $this->get_defaultsetting();
2444 $context = (object) [
2445 'size' => $this->size,
2446 'id' => $this->get_id(),
2447 'name' => $this->get_full_name(),
2448 'value' => $data,
2449 'forceltr' => $this->get_force_ltr(),
2451 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2453 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2458 * Text input with a maximum length constraint.
2460 * @copyright 2015 onwards Ankit Agarwal
2461 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2463 class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2465 /** @var int maximum number of chars allowed. */
2466 protected $maxlength;
2469 * Config text constructor
2471 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2472 * or 'myplugin/mysetting' for ones in config_plugins.
2473 * @param string $visiblename localised
2474 * @param string $description long localised info
2475 * @param string $defaultsetting
2476 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2477 * @param int $size default field size
2478 * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2480 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2481 $size=null, $maxlength = 0) {
2482 $this->maxlength = $maxlength;
2483 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2487 * Validate data before storage
2489 * @param string $data data
2490 * @return mixed true if ok string if error found
2492 public function validate($data) {
2493 $parentvalidation = parent::validate($data);
2494 if ($parentvalidation === true) {
2495 if ($this->maxlength > 0) {
2496 // Max length check.
2497 $length = core_text::strlen($data);
2498 if ($length > $this->maxlength) {
2499 return get_string('maximumchars', 'moodle', $this->maxlength);
2501 return true;
2502 } else {
2503 return true; // No max length check needed.
2505 } else {
2506 return $parentvalidation;
2512 * General text area without html editor.
2514 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2516 class admin_setting_configtextarea extends admin_setting_configtext {
2517 private $rows;
2518 private $cols;
2521 * @param string $name
2522 * @param string $visiblename
2523 * @param string $description
2524 * @param mixed $defaultsetting string or array
2525 * @param mixed $paramtype
2526 * @param string $cols The number of columns to make the editor
2527 * @param string $rows The number of rows to make the editor
2529 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2530 $this->rows = $rows;
2531 $this->cols = $cols;
2532 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2536 * Returns an XHTML string for the editor
2538 * @param string $data
2539 * @param string $query
2540 * @return string XHTML string for the editor
2542 public function output_html($data, $query='') {
2543 global $OUTPUT;
2545 $default = $this->get_defaultsetting();
2546 $defaultinfo = $default;
2547 if (!is_null($default) and $default !== '') {
2548 $defaultinfo = "\n".$default;
2551 $context = (object) [
2552 'cols' => $this->cols,
2553 'rows' => $this->rows,
2554 'id' => $this->get_id(),
2555 'name' => $this->get_full_name(),
2556 'value' => $data,
2557 'forceltr' => $this->get_force_ltr(),
2559 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2561 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2566 * General text area with html editor.
2568 class admin_setting_confightmleditor extends admin_setting_configtextarea {
2571 * @param string $name
2572 * @param string $visiblename
2573 * @param string $description
2574 * @param mixed $defaultsetting string or array
2575 * @param mixed $paramtype
2577 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2578 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2579 $this->set_force_ltr(false);
2580 editors_head_setup();
2584 * Returns an XHTML string for the editor
2586 * @param string $data
2587 * @param string $query
2588 * @return string XHTML string for the editor
2590 public function output_html($data, $query='') {
2591 $editor = editors_get_preferred_editor(FORMAT_HTML);
2592 $editor->set_text($data);
2593 $editor->use_editor($this->get_id(), array('noclean'=>true));
2594 return parent::output_html($data, $query);
2598 * Checks if data has empty html.
2600 * @param string $data
2601 * @return string Empty when no errors.
2603 public function write_setting($data) {
2604 if (trim(html_to_text($data)) === '') {
2605 $data = '';
2607 return parent::write_setting($data);
2613 * Password field, allows unmasking of password
2615 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2617 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2620 * Constructor
2621 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2622 * @param string $visiblename localised
2623 * @param string $description long localised info
2624 * @param string $defaultsetting default password
2626 public function __construct($name, $visiblename, $description, $defaultsetting) {
2627 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2631 * Log config changes if necessary.
2632 * @param string $name
2633 * @param string $oldvalue
2634 * @param string $value
2636 protected function add_to_config_log($name, $oldvalue, $value) {
2637 if ($value !== '') {
2638 $value = '********';
2640 if ($oldvalue !== '' and $oldvalue !== null) {
2641 $oldvalue = '********';
2643 parent::add_to_config_log($name, $oldvalue, $value);
2647 * Returns HTML for the field.
2649 * @param string $data Value for the field
2650 * @param string $query Passed as final argument for format_admin_setting
2651 * @return string Rendered HTML
2653 public function output_html($data, $query='') {
2654 global $OUTPUT, $CFG;
2655 $forced = false;
2656 if (empty($this->plugin)) {
2657 if (array_key_exists($this->name, $CFG->config_php_settings)) {
2658 $forced = true;
2660 } else {
2661 if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
2662 and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
2663 $forced = true;
2666 $context = (object) [
2667 'id' => $this->get_id(),
2668 'name' => $this->get_full_name(),
2669 'size' => $this->size,
2670 'value' => $forced ? null : $data,
2671 'forceltr' => $this->get_force_ltr(),
2672 'forced' => $forced
2674 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2675 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2680 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2682 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2683 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2685 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2688 * Constructor
2690 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2691 * @param string $visiblename localised
2692 * @param string $description long localised info
2693 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2695 public function __construct($name, $visiblename, $description, $defaultsetting) {
2696 parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2697 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2702 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2703 * Note: Only advanced makes sense right now - locked does not.
2705 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2707 class admin_setting_configempty extends admin_setting_configtext {
2710 * @param string $name
2711 * @param string $visiblename
2712 * @param string $description
2714 public function __construct($name, $visiblename, $description) {
2715 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2719 * Returns an XHTML string for the hidden field
2721 * @param string $data
2722 * @param string $query
2723 * @return string XHTML string for the editor
2725 public function output_html($data, $query='') {
2726 global $OUTPUT;
2728 $context = (object) [
2729 'id' => $this->get_id(),
2730 'name' => $this->get_full_name()
2732 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2734 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2740 * Path to directory
2742 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2744 class admin_setting_configfile extends admin_setting_configtext {
2746 * Constructor
2747 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2748 * @param string $visiblename localised
2749 * @param string $description long localised info
2750 * @param string $defaultdirectory default directory location
2752 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2753 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2757 * Returns XHTML for the field
2759 * Returns XHTML for the field and also checks whether the file
2760 * specified in $data exists using file_exists()
2762 * @param string $data File name and path to use in value attr
2763 * @param string $query
2764 * @return string XHTML field
2766 public function output_html($data, $query='') {
2767 global $CFG, $OUTPUT;
2769 $default = $this->get_defaultsetting();
2770 $context = (object) [
2771 'id' => $this->get_id(),
2772 'name' => $this->get_full_name(),
2773 'size' => $this->size,
2774 'value' => $data,
2775 'showvalidity' => !empty($data),
2776 'valid' => $data && file_exists($data),
2777 'readonly' => !empty($CFG->preventexecpath),
2778 'forceltr' => $this->get_force_ltr(),
2781 if ($context->readonly) {
2782 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2785 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2787 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2791 * Checks if execpatch has been disabled in config.php
2793 public function write_setting($data) {
2794 global $CFG;
2795 if (!empty($CFG->preventexecpath)) {
2796 if ($this->get_setting() === null) {
2797 // Use default during installation.
2798 $data = $this->get_defaultsetting();
2799 if ($data === null) {
2800 $data = '';
2802 } else {
2803 return '';
2806 return parent::write_setting($data);
2813 * Path to executable file
2815 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2817 class admin_setting_configexecutable extends admin_setting_configfile {
2820 * Returns an XHTML field
2822 * @param string $data This is the value for the field
2823 * @param string $query
2824 * @return string XHTML field
2826 public function output_html($data, $query='') {
2827 global $CFG, $OUTPUT;
2828 $default = $this->get_defaultsetting();
2829 require_once("$CFG->libdir/filelib.php");
2831 $context = (object) [
2832 'id' => $this->get_id(),
2833 'name' => $this->get_full_name(),
2834 'size' => $this->size,
2835 'value' => $data,
2836 'showvalidity' => !empty($data),
2837 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
2838 'readonly' => !empty($CFG->preventexecpath),
2839 'forceltr' => $this->get_force_ltr()
2842 if (!empty($CFG->preventexecpath)) {
2843 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2846 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
2848 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2854 * Path to directory
2856 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2858 class admin_setting_configdirectory extends admin_setting_configfile {
2861 * Returns an XHTML field
2863 * @param string $data This is the value for the field
2864 * @param string $query
2865 * @return string XHTML
2867 public function output_html($data, $query='') {
2868 global $CFG, $OUTPUT;
2869 $default = $this->get_defaultsetting();
2871 $context = (object) [
2872 'id' => $this->get_id(),
2873 'name' => $this->get_full_name(),
2874 'size' => $this->size,
2875 'value' => $data,
2876 'showvalidity' => !empty($data),
2877 'valid' => $data && file_exists($data) && is_dir($data),
2878 'readonly' => !empty($CFG->preventexecpath),
2879 'forceltr' => $this->get_force_ltr()
2882 if (!empty($CFG->preventexecpath)) {
2883 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2886 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
2888 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2894 * Checkbox
2896 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2898 class admin_setting_configcheckbox extends admin_setting {
2899 /** @var string Value used when checked */
2900 public $yes;
2901 /** @var string Value used when not checked */
2902 public $no;
2905 * Constructor
2906 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2907 * @param string $visiblename localised
2908 * @param string $description long localised info
2909 * @param string $defaultsetting
2910 * @param string $yes value used when checked
2911 * @param string $no value used when not checked
2913 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2914 parent::__construct($name, $visiblename, $description, $defaultsetting);
2915 $this->yes = (string)$yes;
2916 $this->no = (string)$no;
2920 * Retrieves the current setting using the objects name
2922 * @return string
2924 public function get_setting() {
2925 return $this->config_read($this->name);
2929 * Sets the value for the setting
2931 * Sets the value for the setting to either the yes or no values
2932 * of the object by comparing $data to yes
2934 * @param mixed $data Gets converted to str for comparison against yes value
2935 * @return string empty string or error
2937 public function write_setting($data) {
2938 if ((string)$data === $this->yes) { // convert to strings before comparison
2939 $data = $this->yes;
2940 } else {
2941 $data = $this->no;
2943 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2947 * Returns an XHTML checkbox field
2949 * @param string $data If $data matches yes then checkbox is checked
2950 * @param string $query
2951 * @return string XHTML field
2953 public function output_html($data, $query='') {
2954 global $OUTPUT;
2956 $context = (object) [
2957 'id' => $this->get_id(),
2958 'name' => $this->get_full_name(),
2959 'no' => $this->no,
2960 'value' => $this->yes,
2961 'checked' => (string) $data === $this->yes,
2964 $default = $this->get_defaultsetting();
2965 if (!is_null($default)) {
2966 if ((string)$default === $this->yes) {
2967 $defaultinfo = get_string('checkboxyes', 'admin');
2968 } else {
2969 $defaultinfo = get_string('checkboxno', 'admin');
2971 } else {
2972 $defaultinfo = NULL;
2975 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
2977 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2983 * Multiple checkboxes, each represents different value, stored in csv format
2985 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2987 class admin_setting_configmulticheckbox extends admin_setting {
2988 /** @var array Array of choices value=>label */
2989 public $choices;
2992 * Constructor: uses parent::__construct
2994 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2995 * @param string $visiblename localised
2996 * @param string $description long localised info
2997 * @param array $defaultsetting array of selected
2998 * @param array $choices array of $value=>$label for each checkbox
3000 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3001 $this->choices = $choices;
3002 parent::__construct($name, $visiblename, $description, $defaultsetting);
3006 * This public function may be used in ancestors for lazy loading of choices
3008 * @todo Check if this function is still required content commented out only returns true
3009 * @return bool true if loaded, false if error
3011 public function load_choices() {
3013 if (is_array($this->choices)) {
3014 return true;
3016 .... load choices here
3018 return true;
3022 * Is setting related to query text - used when searching
3024 * @param string $query
3025 * @return bool true on related, false on not or failure
3027 public function is_related($query) {
3028 if (!$this->load_choices() or empty($this->choices)) {
3029 return false;
3031 if (parent::is_related($query)) {
3032 return true;
3035 foreach ($this->choices as $desc) {
3036 if (strpos(core_text::strtolower($desc), $query) !== false) {
3037 return true;
3040 return false;
3044 * Returns the current setting if it is set
3046 * @return mixed null if null, else an array
3048 public function get_setting() {
3049 $result = $this->config_read($this->name);
3051 if (is_null($result)) {
3052 return NULL;
3054 if ($result === '') {
3055 return array();
3057 $enabled = explode(',', $result);
3058 $setting = array();
3059 foreach ($enabled as $option) {
3060 $setting[$option] = 1;
3062 return $setting;
3066 * Saves the setting(s) provided in $data
3068 * @param array $data An array of data, if not array returns empty str
3069 * @return mixed empty string on useless data or bool true=success, false=failed
3071 public function write_setting($data) {
3072 if (!is_array($data)) {
3073 return ''; // ignore it
3075 if (!$this->load_choices() or empty($this->choices)) {
3076 return '';
3078 unset($data['xxxxx']);
3079 $result = array();
3080 foreach ($data as $key => $value) {
3081 if ($value and array_key_exists($key, $this->choices)) {
3082 $result[] = $key;
3085 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3089 * Returns XHTML field(s) as required by choices
3091 * Relies on data being an array should data ever be another valid vartype with
3092 * acceptable value this may cause a warning/error
3093 * if (!is_array($data)) would fix the problem
3095 * @todo Add vartype handling to ensure $data is an array
3097 * @param array $data An array of checked values
3098 * @param string $query
3099 * @return string XHTML field
3101 public function output_html($data, $query='') {
3102 global $OUTPUT;
3104 if (!$this->load_choices() or empty($this->choices)) {
3105 return '';
3108 $default = $this->get_defaultsetting();
3109 if (is_null($default)) {
3110 $default = array();
3112 if (is_null($data)) {
3113 $data = array();
3116 $context = (object) [
3117 'id' => $this->get_id(),
3118 'name' => $this->get_full_name(),
3121 $options = array();
3122 $defaults = array();
3123 foreach ($this->choices as $key => $description) {
3124 if (!empty($default[$key])) {
3125 $defaults[] = $description;
3128 $options[] = [
3129 'key' => $key,
3130 'checked' => !empty($data[$key]),
3131 'label' => highlightfast($query, $description)
3135 if (is_null($default)) {
3136 $defaultinfo = null;
3137 } else if (!empty($defaults)) {
3138 $defaultinfo = implode(', ', $defaults);
3139 } else {
3140 $defaultinfo = get_string('none');
3143 $context->options = $options;
3144 $context->hasoptions = !empty($options);
3146 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3148 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3155 * Multiple checkboxes 2, value stored as string 00101011
3157 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3159 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3162 * Returns the setting if set
3164 * @return mixed null if not set, else an array of set settings
3166 public function get_setting() {
3167 $result = $this->config_read($this->name);
3168 if (is_null($result)) {
3169 return NULL;
3171 if (!$this->load_choices()) {
3172 return NULL;
3174 $result = str_pad($result, count($this->choices), '0');
3175 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3176 $setting = array();
3177 foreach ($this->choices as $key=>$unused) {
3178 $value = array_shift($result);
3179 if ($value) {
3180 $setting[$key] = 1;
3183 return $setting;
3187 * Save setting(s) provided in $data param
3189 * @param array $data An array of settings to save
3190 * @return mixed empty string for bad data or bool true=>success, false=>error
3192 public function write_setting($data) {
3193 if (!is_array($data)) {
3194 return ''; // ignore it
3196 if (!$this->load_choices() or empty($this->choices)) {
3197 return '';
3199 $result = '';
3200 foreach ($this->choices as $key=>$unused) {
3201 if (!empty($data[$key])) {
3202 $result .= '1';
3203 } else {
3204 $result .= '0';
3207 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3213 * Select one value from list
3215 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3217 class admin_setting_configselect extends admin_setting {
3218 /** @var array Array of choices value=>label */
3219 public $choices;
3220 /** @var array Array of choices grouped using optgroups */
3221 public $optgroups;
3224 * Constructor
3225 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3226 * @param string $visiblename localised
3227 * @param string $description long localised info
3228 * @param string|int $defaultsetting
3229 * @param array $choices array of $value=>$label for each selection
3231 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3232 // Look for optgroup and single options.
3233 if (is_array($choices)) {
3234 $this->choices = [];
3235 foreach ($choices as $key => $val) {
3236 if (is_array($val)) {
3237 $this->optgroups[$key] = $val;
3238 $this->choices = array_merge($this->choices, $val);
3239 } else {
3240 $this->choices[$key] = $val;
3245 parent::__construct($name, $visiblename, $description, $defaultsetting);
3249 * This function may be used in ancestors for lazy loading of choices
3251 * Override this method if loading of choices is expensive, such
3252 * as when it requires multiple db requests.
3254 * @return bool true if loaded, false if error
3256 public function load_choices() {
3258 if (is_array($this->choices)) {
3259 return true;
3261 .... load choices here
3263 return true;
3267 * Check if this is $query is related to a choice
3269 * @param string $query
3270 * @return bool true if related, false if not
3272 public function is_related($query) {
3273 if (parent::is_related($query)) {
3274 return true;
3276 if (!$this->load_choices()) {
3277 return false;
3279 foreach ($this->choices as $key=>$value) {
3280 if (strpos(core_text::strtolower($key), $query) !== false) {
3281 return true;
3283 if (strpos(core_text::strtolower($value), $query) !== false) {
3284 return true;
3287 return false;
3291 * Return the setting
3293 * @return mixed returns config if successful else null
3295 public function get_setting() {
3296 return $this->config_read($this->name);
3300 * Save a setting
3302 * @param string $data
3303 * @return string empty of error string
3305 public function write_setting($data) {
3306 if (!$this->load_choices() or empty($this->choices)) {
3307 return '';
3309 if (!array_key_exists($data, $this->choices)) {
3310 return ''; // ignore it
3313 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3317 * Returns XHTML select field
3319 * Ensure the options are loaded, and generate the XHTML for the select
3320 * element and any warning message. Separating this out from output_html
3321 * makes it easier to subclass this class.
3323 * @param string $data the option to show as selected.
3324 * @param string $current the currently selected option in the database, null if none.
3325 * @param string $default the default selected option.
3326 * @return array the HTML for the select element, and a warning message.
3327 * @deprecated since Moodle 3.2
3329 public function output_select_html($data, $current, $default, $extraname = '') {
3330 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3334 * Returns XHTML select field and wrapping div(s)
3336 * @see output_select_html()
3338 * @param string $data the option to show as selected
3339 * @param string $query
3340 * @return string XHTML field and wrapping div
3342 public function output_html($data, $query='') {
3343 global $OUTPUT;
3345 $default = $this->get_defaultsetting();
3346 $current = $this->get_setting();
3348 if (!$this->load_choices() || empty($this->choices)) {
3349 return '';
3352 $context = (object) [
3353 'id' => $this->get_id(),
3354 'name' => $this->get_full_name(),
3357 if (!is_null($default) && array_key_exists($default, $this->choices)) {
3358 $defaultinfo = $this->choices[$default];
3359 } else {
3360 $defaultinfo = NULL;
3363 // Warnings.
3364 $warning = '';
3365 if ($current === null) {
3366 // First run.
3367 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3368 // No warning.
3369 } else if (!array_key_exists($current, $this->choices)) {
3370 $warning = get_string('warningcurrentsetting', 'admin', $current);
3371 if (!is_null($default) && $data == $current) {
3372 $data = $default; // Use default instead of first value when showing the form.
3376 $options = [];
3377 $template = 'core_admin/setting_configselect';
3379 if (!empty($this->optgroups)) {
3380 $optgroups = [];
3381 foreach ($this->optgroups as $label => $choices) {
3382 $optgroup = array('label' => $label, 'options' => []);
3383 foreach ($choices as $value => $name) {
3384 $optgroup['options'][] = [
3385 'value' => $value,
3386 'name' => $name,
3387 'selected' => (string) $value == $data
3389 unset($this->choices[$value]);
3391 $optgroups[] = $optgroup;
3393 $context->options = $options;
3394 $context->optgroups = $optgroups;
3395 $template = 'core_admin/setting_configselect_optgroup';
3398 foreach ($this->choices as $value => $name) {
3399 $options[] = [
3400 'value' => $value,
3401 'name' => $name,
3402 'selected' => (string) $value == $data
3405 $context->options = $options;
3407 $element = $OUTPUT->render_from_template($template, $context);
3409 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3415 * Select multiple items from list
3417 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3419 class admin_setting_configmultiselect extends admin_setting_configselect {
3421 * Constructor
3422 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3423 * @param string $visiblename localised
3424 * @param string $description long localised info
3425 * @param array $defaultsetting array of selected items
3426 * @param array $choices array of $value=>$label for each list item
3428 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3429 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3433 * Returns the select setting(s)
3435 * @return mixed null or array. Null if no settings else array of setting(s)
3437 public function get_setting() {
3438 $result = $this->config_read($this->name);
3439 if (is_null($result)) {
3440 return NULL;
3442 if ($result === '') {
3443 return array();
3445 return explode(',', $result);
3449 * Saves setting(s) provided through $data
3451 * Potential bug in the works should anyone call with this function
3452 * using a vartype that is not an array
3454 * @param array $data
3456 public function write_setting($data) {
3457 if (!is_array($data)) {
3458 return ''; //ignore it
3460 if (!$this->load_choices() or empty($this->choices)) {
3461 return '';
3464 unset($data['xxxxx']);
3466 $save = array();
3467 foreach ($data as $value) {
3468 if (!array_key_exists($value, $this->choices)) {
3469 continue; // ignore it
3471 $save[] = $value;
3474 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3478 * Is setting related to query text - used when searching
3480 * @param string $query
3481 * @return bool true if related, false if not
3483 public function is_related($query) {
3484 if (!$this->load_choices() or empty($this->choices)) {
3485 return false;
3487 if (parent::is_related($query)) {
3488 return true;
3491 foreach ($this->choices as $desc) {
3492 if (strpos(core_text::strtolower($desc), $query) !== false) {
3493 return true;
3496 return false;
3500 * Returns XHTML multi-select field
3502 * @todo Add vartype handling to ensure $data is an array
3503 * @param array $data Array of values to select by default
3504 * @param string $query
3505 * @return string XHTML multi-select field
3507 public function output_html($data, $query='') {
3508 global $OUTPUT;
3510 if (!$this->load_choices() or empty($this->choices)) {
3511 return '';
3514 $default = $this->get_defaultsetting();
3515 if (is_null($default)) {
3516 $default = array();
3518 if (is_null($data)) {
3519 $data = array();
3522 $context = (object) [
3523 'id' => $this->get_id(),
3524 'name' => $this->get_full_name(),
3525 'size' => min(10, count($this->choices))
3528 $defaults = [];
3529 $options = [];
3530 $template = 'core_admin/setting_configmultiselect';
3532 if (!empty($this->optgroups)) {
3533 $optgroups = [];
3534 foreach ($this->optgroups as $label => $choices) {
3535 $optgroup = array('label' => $label, 'options' => []);
3536 foreach ($choices as $value => $name) {
3537 if (in_array($value, $default)) {
3538 $defaults[] = $name;
3540 $optgroup['options'][] = [
3541 'value' => $value,
3542 'name' => $name,
3543 'selected' => in_array($value, $data)
3545 unset($this->choices[$value]);
3547 $optgroups[] = $optgroup;
3549 $context->optgroups = $optgroups;
3550 $template = 'core_admin/setting_configmultiselect_optgroup';
3553 foreach ($this->choices as $value => $name) {
3554 if (in_array($value, $default)) {
3555 $defaults[] = $name;
3557 $options[] = [
3558 'value' => $value,
3559 'name' => $name,
3560 'selected' => in_array($value, $data)
3563 $context->options = $options;
3565 if (is_null($default)) {
3566 $defaultinfo = NULL;
3567 } if (!empty($defaults)) {
3568 $defaultinfo = implode(', ', $defaults);
3569 } else {
3570 $defaultinfo = get_string('none');
3573 $element = $OUTPUT->render_from_template($template, $context);
3575 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3580 * Time selector
3582 * This is a liiitle bit messy. we're using two selects, but we're returning
3583 * them as an array named after $name (so we only use $name2 internally for the setting)
3585 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3587 class admin_setting_configtime extends admin_setting {
3588 /** @var string Used for setting second select (minutes) */
3589 public $name2;
3592 * Constructor
3593 * @param string $hoursname setting for hours
3594 * @param string $minutesname setting for hours
3595 * @param string $visiblename localised
3596 * @param string $description long localised info
3597 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3599 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3600 $this->name2 = $minutesname;
3601 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3605 * Get the selected time
3607 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3609 public function get_setting() {
3610 $result1 = $this->config_read($this->name);
3611 $result2 = $this->config_read($this->name2);
3612 if (is_null($result1) or is_null($result2)) {
3613 return NULL;
3616 return array('h' => $result1, 'm' => $result2);
3620 * Store the time (hours and minutes)
3622 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3623 * @return bool true if success, false if not
3625 public function write_setting($data) {
3626 if (!is_array($data)) {
3627 return '';
3630 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3631 return ($result ? '' : get_string('errorsetting', 'admin'));
3635 * Returns XHTML time select fields
3637 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3638 * @param string $query
3639 * @return string XHTML time select fields and wrapping div(s)
3641 public function output_html($data, $query='') {
3642 global $OUTPUT;
3644 $default = $this->get_defaultsetting();
3645 if (is_array($default)) {
3646 $defaultinfo = $default['h'].':'.$default['m'];
3647 } else {
3648 $defaultinfo = NULL;
3651 $context = (object) [
3652 'id' => $this->get_id(),
3653 'name' => $this->get_full_name(),
3654 'hours' => array_map(function($i) use ($data) {
3655 return [
3656 'value' => $i,
3657 'name' => $i,
3658 'selected' => $i == $data['h']
3660 }, range(0, 23)),
3661 'minutes' => array_map(function($i) use ($data) {
3662 return [
3663 'value' => $i,
3664 'name' => $i,
3665 'selected' => $i == $data['m']
3667 }, range(0, 59, 5))
3670 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3672 return format_admin_setting($this, $this->visiblename, $element, $this->description,
3673 $this->get_id() . 'h', '', $defaultinfo, $query);
3680 * Seconds duration setting.
3682 * @copyright 2012 Petr Skoda (http://skodak.org)
3683 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3685 class admin_setting_configduration extends admin_setting {
3687 /** @var int default duration unit */
3688 protected $defaultunit;
3691 * Constructor
3692 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3693 * or 'myplugin/mysetting' for ones in config_plugins.
3694 * @param string $visiblename localised name
3695 * @param string $description localised long description
3696 * @param mixed $defaultsetting string or array depending on implementation
3697 * @param int $defaultunit - day, week, etc. (in seconds)
3699 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3700 if (is_number($defaultsetting)) {
3701 $defaultsetting = self::parse_seconds($defaultsetting);
3703 $units = self::get_units();
3704 if (isset($units[$defaultunit])) {
3705 $this->defaultunit = $defaultunit;
3706 } else {
3707 $this->defaultunit = 86400;
3709 parent::__construct($name, $visiblename, $description, $defaultsetting);
3713 * Returns selectable units.
3714 * @static
3715 * @return array
3717 protected static function get_units() {
3718 return array(
3719 604800 => get_string('weeks'),
3720 86400 => get_string('days'),
3721 3600 => get_string('hours'),
3722 60 => get_string('minutes'),
3723 1 => get_string('seconds'),
3728 * Converts seconds to some more user friendly string.
3729 * @static
3730 * @param int $seconds
3731 * @return string
3733 protected static function get_duration_text($seconds) {
3734 if (empty($seconds)) {
3735 return get_string('none');
3737 $data = self::parse_seconds($seconds);
3738 switch ($data['u']) {
3739 case (60*60*24*7):
3740 return get_string('numweeks', '', $data['v']);
3741 case (60*60*24):
3742 return get_string('numdays', '', $data['v']);
3743 case (60*60):
3744 return get_string('numhours', '', $data['v']);
3745 case (60):
3746 return get_string('numminutes', '', $data['v']);
3747 default:
3748 return get_string('numseconds', '', $data['v']*$data['u']);
3753 * Finds suitable units for given duration.
3754 * @static
3755 * @param int $seconds
3756 * @return array
3758 protected static function parse_seconds($seconds) {
3759 foreach (self::get_units() as $unit => $unused) {
3760 if ($seconds % $unit === 0) {
3761 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3764 return array('v'=>(int)$seconds, 'u'=>1);
3768 * Get the selected duration as array.
3770 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3772 public function get_setting() {
3773 $seconds = $this->config_read($this->name);
3774 if (is_null($seconds)) {
3775 return null;
3778 return self::parse_seconds($seconds);
3782 * Store the duration as seconds.
3784 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3785 * @return bool true if success, false if not
3787 public function write_setting($data) {
3788 if (!is_array($data)) {
3789 return '';
3792 $seconds = (int)($data['v']*$data['u']);
3793 if ($seconds < 0) {
3794 return get_string('errorsetting', 'admin');
3797 $result = $this->config_write($this->name, $seconds);
3798 return ($result ? '' : get_string('errorsetting', 'admin'));
3802 * Returns duration text+select fields.
3804 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3805 * @param string $query
3806 * @return string duration text+select fields and wrapping div(s)
3808 public function output_html($data, $query='') {
3809 global $OUTPUT;
3811 $default = $this->get_defaultsetting();
3812 if (is_number($default)) {
3813 $defaultinfo = self::get_duration_text($default);
3814 } else if (is_array($default)) {
3815 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3816 } else {
3817 $defaultinfo = null;
3820 $inputid = $this->get_id() . 'v';
3821 $units = self::get_units();
3822 $defaultunit = $this->defaultunit;
3824 $context = (object) [
3825 'id' => $this->get_id(),
3826 'name' => $this->get_full_name(),
3827 'value' => $data['v'],
3828 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
3829 return [
3830 'value' => $unit,
3831 'name' => $units[$unit],
3832 'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
3834 }, array_keys($units))
3837 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
3839 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
3845 * Seconds duration setting with an advanced checkbox, that controls a additional
3846 * $name.'_adv' setting.
3848 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3849 * @copyright 2014 The Open University
3851 class admin_setting_configduration_with_advanced extends admin_setting_configduration {
3853 * Constructor
3854 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3855 * or 'myplugin/mysetting' for ones in config_plugins.
3856 * @param string $visiblename localised name
3857 * @param string $description localised long description
3858 * @param array $defaultsetting array of int value, and bool whether it is
3859 * is advanced by default.
3860 * @param int $defaultunit - day, week, etc. (in seconds)
3862 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3863 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
3864 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
3870 * Used to validate a textarea used for ip addresses
3872 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3873 * @copyright 2011 Petr Skoda (http://skodak.org)
3875 class admin_setting_configiplist extends admin_setting_configtextarea {
3878 * Validate the contents of the textarea as IP addresses
3880 * Used to validate a new line separated list of IP addresses collected from
3881 * a textarea control
3883 * @param string $data A list of IP Addresses separated by new lines
3884 * @return mixed bool true for success or string:error on failure
3886 public function validate($data) {
3887 if(!empty($data)) {
3888 $lines = explode("\n", $data);
3889 } else {
3890 return true;
3892 $result = true;
3893 $badips = array();
3894 foreach ($lines as $line) {
3895 $tokens = explode('#', $line);
3896 $ip = trim($tokens[0]);
3897 if (empty($ip)) {
3898 continue;
3900 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3901 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3902 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3903 } else {
3904 $result = false;
3905 $badips[] = $ip;
3908 if($result) {
3909 return true;
3910 } else {
3911 return get_string('validateiperror', 'admin', join(', ', $badips));
3917 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
3919 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3920 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
3922 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
3925 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
3926 * Used to validate a new line separated list of entries collected from a textarea control.
3928 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
3929 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
3930 * via the get_setting() method, which has been overriden.
3932 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
3933 * @return mixed bool true for success or string:error on failure
3935 public function validate($data) {
3936 if (empty($data)) {
3937 return true;
3939 $entries = explode("\n", $data);
3940 $badentries = [];
3942 foreach ($entries as $key => $entry) {
3943 $entry = trim($entry);
3944 if (empty($entry)) {
3945 return get_string('validateemptylineerror', 'admin');
3948 // Validate each string entry against the supported formats.
3949 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
3950 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
3951 || \core\ip_utils::is_domain_matching_pattern($entry)) {
3952 continue;
3955 // Otherwise, the entry is invalid.
3956 $badentries[] = $entry;
3959 if ($badentries) {
3960 return get_string('validateerrorlist', 'admin', join(', ', $badentries));
3962 return true;
3966 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
3968 * @param string $data the setting data, as sent from the web form.
3969 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
3971 protected function ace_encode($data) {
3972 if (empty($data)) {
3973 return $data;
3975 $entries = explode("\n", $data);
3976 foreach ($entries as $key => $entry) {
3977 $entry = trim($entry);
3978 // This regex matches any string that has non-ascii character.
3979 if (preg_match('/[^\x00-\x7f]/', $entry)) {
3980 // If we can convert the unicode string to an idn, do so.
3981 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
3982 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
3983 $entries[$key] = $val ? $val : $entry;
3986 return implode("\n", $entries);
3990 * Decode any ascii-encoded domain names back to their utf-8 representation for display.
3992 * @param string $data the setting data, as found in the database.
3993 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
3995 protected function ace_decode($data) {
3996 $entries = explode("\n", $data);
3997 foreach ($entries as $key => $entry) {
3998 $entry = trim($entry);
3999 if (strpos($entry, 'xn--') !== false) {
4000 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4003 return implode("\n", $entries);
4007 * Override, providing utf8-decoding for ascii-encoded IDN strings.
4009 * @return mixed returns punycode-converted setting string if successful, else null.
4011 public function get_setting() {
4012 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4013 $data = $this->config_read($this->name);
4014 if (function_exists('idn_to_utf8') && !is_null($data)) {
4015 $data = $this->ace_decode($data);
4017 return $data;
4021 * Override, providing ascii-encoding for utf8 (native) IDN strings.
4023 * @param string $data
4024 * @return string
4026 public function write_setting($data) {
4027 if ($this->paramtype === PARAM_INT and $data === '') {
4028 // Do not complain if '' used instead of 0.
4029 $data = 0;
4032 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4033 if (function_exists('idn_to_ascii')) {
4034 $data = $this->ace_encode($data);
4037 $validated = $this->validate($data);
4038 if ($validated !== true) {
4039 return $validated;
4041 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4046 * Used to validate a textarea used for port numbers.
4048 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4049 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4051 class admin_setting_configportlist extends admin_setting_configtextarea {
4054 * Validate the contents of the textarea as port numbers.
4055 * Used to validate a new line separated list of ports collected from a textarea control.
4057 * @param string $data A list of ports separated by new lines
4058 * @return mixed bool true for success or string:error on failure
4060 public function validate($data) {
4061 if (empty($data)) {
4062 return true;
4064 $ports = explode("\n", $data);
4065 $badentries = [];
4066 foreach ($ports as $port) {
4067 $port = trim($port);
4068 if (empty($port)) {
4069 return get_string('validateemptylineerror', 'admin');
4072 // Is the string a valid integer number?
4073 if (strval(intval($port)) !== $port || intval($port) <= 0) {
4074 $badentries[] = $port;
4077 if ($badentries) {
4078 return get_string('validateerrorlist', 'admin', $badentries);
4080 return true;
4086 * An admin setting for selecting one or more users who have a capability
4087 * in the system context
4089 * An admin setting for selecting one or more users, who have a particular capability
4090 * in the system context. Warning, make sure the list will never be too long. There is
4091 * no paging or searching of this list.
4093 * To correctly get a list of users from this config setting, you need to call the
4094 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4096 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4098 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4099 /** @var string The capabilities name */
4100 protected $capability;
4101 /** @var int include admin users too */
4102 protected $includeadmins;
4105 * Constructor.
4107 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4108 * @param string $visiblename localised name
4109 * @param string $description localised long description
4110 * @param array $defaultsetting array of usernames
4111 * @param string $capability string capability name.
4112 * @param bool $includeadmins include administrators
4114 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4115 $this->capability = $capability;
4116 $this->includeadmins = $includeadmins;
4117 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4121 * Load all of the uses who have the capability into choice array
4123 * @return bool Always returns true
4125 function load_choices() {
4126 if (is_array($this->choices)) {
4127 return true;
4129 list($sort, $sortparams) = users_order_by_sql('u');
4130 if (!empty($sortparams)) {
4131 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4132 'This is unexpected, and a problem because there is no way to pass these ' .
4133 'parameters to get_users_by_capability. See MDL-34657.');
4135 $userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
4136 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4137 $this->choices = array(
4138 '$@NONE@$' => get_string('nobody'),
4139 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4141 if ($this->includeadmins) {
4142 $admins = get_admins();
4143 foreach ($admins as $user) {
4144 $this->choices[$user->id] = fullname($user);
4147 if (is_array($users)) {
4148 foreach ($users as $user) {
4149 $this->choices[$user->id] = fullname($user);
4152 return true;
4156 * Returns the default setting for class
4158 * @return mixed Array, or string. Empty string if no default
4160 public function get_defaultsetting() {
4161 $this->load_choices();
4162 $defaultsetting = parent::get_defaultsetting();
4163 if (empty($defaultsetting)) {
4164 return array('$@NONE@$');
4165 } else if (array_key_exists($defaultsetting, $this->choices)) {
4166 return $defaultsetting;
4167 } else {
4168 return '';
4173 * Returns the current setting
4175 * @return mixed array or string
4177 public function get_setting() {
4178 $result = parent::get_setting();
4179 if ($result === null) {
4180 // this is necessary for settings upgrade
4181 return null;
4183 if (empty($result)) {
4184 $result = array('$@NONE@$');
4186 return $result;
4190 * Save the chosen setting provided as $data
4192 * @param array $data
4193 * @return mixed string or array
4195 public function write_setting($data) {
4196 // If all is selected, remove any explicit options.
4197 if (in_array('$@ALL@$', $data)) {
4198 $data = array('$@ALL@$');
4200 // None never needs to be written to the DB.
4201 if (in_array('$@NONE@$', $data)) {
4202 unset($data[array_search('$@NONE@$', $data)]);
4204 return parent::write_setting($data);
4210 * Special checkbox for calendar - resets SESSION vars.
4212 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4214 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4216 * Calls the parent::__construct with default values
4218 * name => calendar_adminseesall
4219 * visiblename => get_string('adminseesall', 'admin')
4220 * description => get_string('helpadminseesall', 'admin')
4221 * defaultsetting => 0
4223 public function __construct() {
4224 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4225 get_string('helpadminseesall', 'admin'), '0');
4229 * Stores the setting passed in $data
4231 * @param mixed gets converted to string for comparison
4232 * @return string empty string or error message
4234 public function write_setting($data) {
4235 global $SESSION;
4236 return parent::write_setting($data);
4241 * Special select for settings that are altered in setup.php and can not be altered on the fly
4243 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4245 class admin_setting_special_selectsetup extends admin_setting_configselect {
4247 * Reads the setting directly from the database
4249 * @return mixed
4251 public function get_setting() {
4252 // read directly from db!
4253 return get_config(NULL, $this->name);
4257 * Save the setting passed in $data
4259 * @param string $data The setting to save
4260 * @return string empty or error message
4262 public function write_setting($data) {
4263 global $CFG;
4264 // do not change active CFG setting!
4265 $current = $CFG->{$this->name};
4266 $result = parent::write_setting($data);
4267 $CFG->{$this->name} = $current;
4268 return $result;
4274 * Special select for frontpage - stores data in course table
4276 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4278 class admin_setting_sitesetselect extends admin_setting_configselect {
4280 * Returns the site name for the selected site
4282 * @see get_site()
4283 * @return string The site name of the selected site
4285 public function get_setting() {
4286 $site = course_get_format(get_site())->get_course();
4287 return $site->{$this->name};
4291 * Updates the database and save the setting
4293 * @param string data
4294 * @return string empty or error message
4296 public function write_setting($data) {
4297 global $DB, $SITE, $COURSE;
4298 if (!in_array($data, array_keys($this->choices))) {
4299 return get_string('errorsetting', 'admin');
4301 $record = new stdClass();
4302 $record->id = SITEID;
4303 $temp = $this->name;
4304 $record->$temp = $data;
4305 $record->timemodified = time();
4307 course_get_format($SITE)->update_course_format_options($record);
4308 $DB->update_record('course', $record);
4310 // Reset caches.
4311 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4312 if ($SITE->id == $COURSE->id) {
4313 $COURSE = $SITE;
4315 format_base::reset_course_cache($SITE->id);
4317 return '';
4324 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4325 * block to hidden.
4327 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4329 class admin_setting_bloglevel extends admin_setting_configselect {
4331 * Updates the database and save the setting
4333 * @param string data
4334 * @return string empty or error message
4336 public function write_setting($data) {
4337 global $DB, $CFG;
4338 if ($data == 0) {
4339 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4340 foreach ($blogblocks as $block) {
4341 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4343 } else {
4344 // reenable all blocks only when switching from disabled blogs
4345 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4346 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4347 foreach ($blogblocks as $block) {
4348 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4352 return parent::write_setting($data);
4358 * Special select - lists on the frontpage - hacky
4360 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4362 class admin_setting_courselist_frontpage extends admin_setting {
4363 /** @var array Array of choices value=>label */
4364 public $choices;
4367 * Construct override, requires one param
4369 * @param bool $loggedin Is the user logged in
4371 public function __construct($loggedin) {
4372 global $CFG;
4373 require_once($CFG->dirroot.'/course/lib.php');
4374 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
4375 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4376 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4377 $defaults = array(FRONTPAGEALLCOURSELIST);
4378 parent::__construct($name, $visiblename, $description, $defaults);
4382 * Loads the choices available
4384 * @return bool always returns true
4386 public function load_choices() {
4387 if (is_array($this->choices)) {
4388 return true;
4390 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
4391 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4392 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4393 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4394 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4395 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
4396 'none' => get_string('none'));
4397 if ($this->name === 'frontpage') {
4398 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4400 return true;
4404 * Returns the selected settings
4406 * @param mixed array or setting or null
4408 public function get_setting() {
4409 $result = $this->config_read($this->name);
4410 if (is_null($result)) {
4411 return NULL;
4413 if ($result === '') {
4414 return array();
4416 return explode(',', $result);
4420 * Save the selected options
4422 * @param array $data
4423 * @return mixed empty string (data is not an array) or bool true=success false=failure
4425 public function write_setting($data) {
4426 if (!is_array($data)) {
4427 return '';
4429 $this->load_choices();
4430 $save = array();
4431 foreach($data as $datum) {
4432 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4433 continue;
4435 $save[$datum] = $datum; // no duplicates
4437 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4441 * Return XHTML select field and wrapping div
4443 * @todo Add vartype handling to make sure $data is an array
4444 * @param array $data Array of elements to select by default
4445 * @return string XHTML select field and wrapping div
4447 public function output_html($data, $query='') {
4448 global $OUTPUT;
4450 $this->load_choices();
4451 $currentsetting = array();
4452 foreach ($data as $key) {
4453 if ($key != 'none' and array_key_exists($key, $this->choices)) {
4454 $currentsetting[] = $key; // already selected first
4458 $context = (object) [
4459 'id' => $this->get_id(),
4460 'name' => $this->get_full_name(),
4463 $options = $this->choices;
4464 $selects = [];
4465 for ($i = 0; $i < count($this->choices) - 1; $i++) {
4466 if (!array_key_exists($i, $currentsetting)) {
4467 $currentsetting[$i] = 'none';
4469 $selects[] = [
4470 'key' => $i,
4471 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4472 return [
4473 'name' => $options[$option],
4474 'value' => $option,
4475 'selected' => $currentsetting[$i] == $option
4477 }, array_keys($options))
4480 $context->selects = $selects;
4482 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4484 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4490 * Special checkbox for frontpage - stores data in course table
4492 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4494 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4496 * Returns the current sites name
4498 * @return string
4500 public function get_setting() {
4501 $site = course_get_format(get_site())->get_course();
4502 return $site->{$this->name};
4506 * Save the selected setting
4508 * @param string $data The selected site
4509 * @return string empty string or error message
4511 public function write_setting($data) {
4512 global $DB, $SITE, $COURSE;
4513 $record = new stdClass();
4514 $record->id = $SITE->id;
4515 $record->{$this->name} = ($data == '1' ? 1 : 0);
4516 $record->timemodified = time();
4518 course_get_format($SITE)->update_course_format_options($record);
4519 $DB->update_record('course', $record);
4521 // Reset caches.
4522 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4523 if ($SITE->id == $COURSE->id) {
4524 $COURSE = $SITE;
4526 format_base::reset_course_cache($SITE->id);
4528 return '';
4533 * Special text for frontpage - stores data in course table.
4534 * Empty string means not set here. Manual setting is required.
4536 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4538 class admin_setting_sitesettext extends admin_setting_configtext {
4541 * Constructor.
4543 public function __construct() {
4544 call_user_func_array(['parent', '__construct'], func_get_args());
4545 $this->set_force_ltr(false);
4549 * Return the current setting
4551 * @return mixed string or null
4553 public function get_setting() {
4554 $site = course_get_format(get_site())->get_course();
4555 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4559 * Validate the selected data
4561 * @param string $data The selected value to validate
4562 * @return mixed true or message string
4564 public function validate($data) {
4565 global $DB, $SITE;
4566 $cleaned = clean_param($data, PARAM_TEXT);
4567 if ($cleaned === '') {
4568 return get_string('required');
4570 if ($this->name ==='shortname' &&
4571 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4572 return get_string('shortnametaken', 'error', $data);
4574 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4575 return true;
4576 } else {
4577 return get_string('validateerror', 'admin');
4582 * Save the selected setting
4584 * @param string $data The selected value
4585 * @return string empty or error message
4587 public function write_setting($data) {
4588 global $DB, $SITE, $COURSE;
4589 $data = trim($data);
4590 $validated = $this->validate($data);
4591 if ($validated !== true) {
4592 return $validated;
4595 $record = new stdClass();
4596 $record->id = $SITE->id;
4597 $record->{$this->name} = $data;
4598 $record->timemodified = time();
4600 course_get_format($SITE)->update_course_format_options($record);
4601 $DB->update_record('course', $record);
4603 // Reset caches.
4604 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4605 if ($SITE->id == $COURSE->id) {
4606 $COURSE = $SITE;
4608 format_base::reset_course_cache($SITE->id);
4610 return '';
4616 * Special text editor for site description.
4618 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4620 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
4623 * Calls parent::__construct with specific arguments
4625 public function __construct() {
4626 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
4627 PARAM_RAW, 60, 15);
4631 * Return the current setting
4632 * @return string The current setting
4634 public function get_setting() {
4635 $site = course_get_format(get_site())->get_course();
4636 return $site->{$this->name};
4640 * Save the new setting
4642 * @param string $data The new value to save
4643 * @return string empty or error message
4645 public function write_setting($data) {
4646 global $DB, $SITE, $COURSE;
4647 $record = new stdClass();
4648 $record->id = $SITE->id;
4649 $record->{$this->name} = $data;
4650 $record->timemodified = time();
4652 course_get_format($SITE)->update_course_format_options($record);
4653 $DB->update_record('course', $record);
4655 // Reset caches.
4656 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4657 if ($SITE->id == $COURSE->id) {
4658 $COURSE = $SITE;
4660 format_base::reset_course_cache($SITE->id);
4662 return '';
4668 * Administration interface for emoticon_manager settings.
4670 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4672 class admin_setting_emoticons extends admin_setting {
4675 * Calls parent::__construct with specific args
4677 public function __construct() {
4678 global $CFG;
4680 $manager = get_emoticon_manager();
4681 $defaults = $this->prepare_form_data($manager->default_emoticons());
4682 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
4686 * Return the current setting(s)
4688 * @return array Current settings array
4690 public function get_setting() {
4691 global $CFG;
4693 $manager = get_emoticon_manager();
4695 $config = $this->config_read($this->name);
4696 if (is_null($config)) {
4697 return null;
4700 $config = $manager->decode_stored_config($config);
4701 if (is_null($config)) {
4702 return null;
4705 return $this->prepare_form_data($config);
4709 * Save selected settings
4711 * @param array $data Array of settings to save
4712 * @return bool
4714 public function write_setting($data) {
4716 $manager = get_emoticon_manager();
4717 $emoticons = $this->process_form_data($data);
4719 if ($emoticons === false) {
4720 return false;
4723 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
4724 return ''; // success
4725 } else {
4726 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
4731 * Return XHTML field(s) for options
4733 * @param array $data Array of options to set in HTML
4734 * @return string XHTML string for the fields and wrapping div(s)
4736 public function output_html($data, $query='') {
4737 global $OUTPUT;
4739 $context = (object) [
4740 'name' => $this->get_full_name(),
4741 'emoticons' => [],
4742 'forceltr' => true,
4745 $i = 0;
4746 foreach ($data as $field => $value) {
4748 // When $i == 0: text.
4749 // When $i == 1: imagename.
4750 // When $i == 2: imagecomponent.
4751 // When $i == 3: altidentifier.
4752 // When $i == 4: altcomponent.
4753 $fields[$i] = (object) [
4754 'field' => $field,
4755 'value' => $value,
4756 'index' => $i
4758 $i++;
4760 if ($i > 4) {
4761 $icon = null;
4762 if (!empty($fields[1]->value)) {
4763 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
4764 $alt = get_string($fields[3]->value, $fields[4]->value);
4765 } else {
4766 $alt = $fields[0]->value;
4768 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
4770 $context->emoticons[] = [
4771 'fields' => $fields,
4772 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
4774 $fields = [];
4775 $i = 0;
4779 $context->reseturl = new moodle_url('/admin/resetemoticons.php');
4780 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
4781 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
4785 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
4787 * @see self::process_form_data()
4788 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4789 * @return array of form fields and their values
4791 protected function prepare_form_data(array $emoticons) {
4793 $form = array();
4794 $i = 0;
4795 foreach ($emoticons as $emoticon) {
4796 $form['text'.$i] = $emoticon->text;
4797 $form['imagename'.$i] = $emoticon->imagename;
4798 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
4799 $form['altidentifier'.$i] = $emoticon->altidentifier;
4800 $form['altcomponent'.$i] = $emoticon->altcomponent;
4801 $i++;
4803 // add one more blank field set for new object
4804 $form['text'.$i] = '';
4805 $form['imagename'.$i] = '';
4806 $form['imagecomponent'.$i] = '';
4807 $form['altidentifier'.$i] = '';
4808 $form['altcomponent'.$i] = '';
4810 return $form;
4814 * Converts the data from admin settings form into an array of emoticon objects
4816 * @see self::prepare_form_data()
4817 * @param array $data array of admin form fields and values
4818 * @return false|array of emoticon objects
4820 protected function process_form_data(array $form) {
4822 $count = count($form); // number of form field values
4824 if ($count % 5) {
4825 // we must get five fields per emoticon object
4826 return false;
4829 $emoticons = array();
4830 for ($i = 0; $i < $count / 5; $i++) {
4831 $emoticon = new stdClass();
4832 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
4833 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
4834 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
4835 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
4836 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
4838 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
4839 // prevent from breaking http://url.addresses by accident
4840 $emoticon->text = '';
4843 if (strlen($emoticon->text) < 2) {
4844 // do not allow single character emoticons
4845 $emoticon->text = '';
4848 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
4849 // emoticon text must contain some non-alphanumeric character to prevent
4850 // breaking HTML tags
4851 $emoticon->text = '';
4854 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
4855 $emoticons[] = $emoticon;
4858 return $emoticons;
4865 * Special setting for limiting of the list of available languages.
4867 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4869 class admin_setting_langlist extends admin_setting_configtext {
4871 * Calls parent::__construct with specific arguments
4873 public function __construct() {
4874 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4878 * Save the new setting
4880 * @param string $data The new setting
4881 * @return bool
4883 public function write_setting($data) {
4884 $return = parent::write_setting($data);
4885 get_string_manager()->reset_caches();
4886 return $return;
4892 * Selection of one of the recognised countries using the list
4893 * returned by {@link get_list_of_countries()}.
4895 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4897 class admin_settings_country_select extends admin_setting_configselect {
4898 protected $includeall;
4899 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
4900 $this->includeall = $includeall;
4901 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
4905 * Lazy-load the available choices for the select box
4907 public function load_choices() {
4908 global $CFG;
4909 if (is_array($this->choices)) {
4910 return true;
4912 $this->choices = array_merge(
4913 array('0' => get_string('choosedots')),
4914 get_string_manager()->get_list_of_countries($this->includeall));
4915 return true;
4921 * admin_setting_configselect for the default number of sections in a course,
4922 * simply so we can lazy-load the choices.
4924 * @copyright 2011 The Open University
4925 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4927 class admin_settings_num_course_sections extends admin_setting_configselect {
4928 public function __construct($name, $visiblename, $description, $defaultsetting) {
4929 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
4932 /** Lazy-load the available choices for the select box */
4933 public function load_choices() {
4934 $max = get_config('moodlecourse', 'maxsections');
4935 if (!isset($max) || !is_numeric($max)) {
4936 $max = 52;
4938 for ($i = 0; $i <= $max; $i++) {
4939 $this->choices[$i] = "$i";
4941 return true;
4947 * Course category selection
4949 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4951 class admin_settings_coursecat_select extends admin_setting_configselect {
4953 * Calls parent::__construct with specific arguments
4955 public function __construct($name, $visiblename, $description, $defaultsetting) {
4956 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4960 * Load the available choices for the select box
4962 * @return bool
4964 public function load_choices() {
4965 global $CFG;
4966 require_once($CFG->dirroot.'/course/lib.php');
4967 if (is_array($this->choices)) {
4968 return true;
4970 $this->choices = make_categories_options();
4971 return true;
4977 * Special control for selecting days to backup
4979 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4981 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
4983 * Calls parent::__construct with specific arguments
4985 public function __construct() {
4986 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
4987 $this->plugin = 'backup';
4991 * Load the available choices for the select box
4993 * @return bool Always returns true
4995 public function load_choices() {
4996 if (is_array($this->choices)) {
4997 return true;
4999 $this->choices = array();
5000 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5001 foreach ($days as $day) {
5002 $this->choices[$day] = get_string($day, 'calendar');
5004 return true;
5009 * Special setting for backup auto destination.
5011 * @package core
5012 * @subpackage admin
5013 * @copyright 2014 Frédéric Massart - FMCorz.net
5014 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5016 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5019 * Calls parent::__construct with specific arguments.
5021 public function __construct() {
5022 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5026 * Check if the directory must be set, depending on backup/backup_auto_storage.
5028 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5029 * there will be conflicts if this validation happens before the other one.
5031 * @param string $data Form data.
5032 * @return string Empty when no errors.
5034 public function write_setting($data) {
5035 $storage = (int) get_config('backup', 'backup_auto_storage');
5036 if ($storage !== 0) {
5037 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5038 // The directory must exist and be writable.
5039 return get_string('backuperrorinvaliddestination');
5042 return parent::write_setting($data);
5048 * Special debug setting
5050 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5052 class admin_setting_special_debug extends admin_setting_configselect {
5054 * Calls parent::__construct with specific arguments
5056 public function __construct() {
5057 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5061 * Load the available choices for the select box
5063 * @return bool
5065 public function load_choices() {
5066 if (is_array($this->choices)) {
5067 return true;
5069 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
5070 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
5071 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
5072 DEBUG_ALL => get_string('debugall', 'admin'),
5073 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5074 return true;
5080 * Special admin control
5082 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5084 class admin_setting_special_calendar_weekend extends admin_setting {
5086 * Calls parent::__construct with specific arguments
5088 public function __construct() {
5089 $name = 'calendar_weekend';
5090 $visiblename = get_string('calendar_weekend', 'admin');
5091 $description = get_string('helpweekenddays', 'admin');
5092 $default = array ('0', '6'); // Saturdays and Sundays
5093 parent::__construct($name, $visiblename, $description, $default);
5097 * Gets the current settings as an array
5099 * @return mixed Null if none, else array of settings
5101 public function get_setting() {
5102 $result = $this->config_read($this->name);
5103 if (is_null($result)) {
5104 return NULL;
5106 if ($result === '') {
5107 return array();
5109 $settings = array();
5110 for ($i=0; $i<7; $i++) {
5111 if ($result & (1 << $i)) {
5112 $settings[] = $i;
5115 return $settings;
5119 * Save the new settings
5121 * @param array $data Array of new settings
5122 * @return bool
5124 public function write_setting($data) {
5125 if (!is_array($data)) {
5126 return '';
5128 unset($data['xxxxx']);
5129 $result = 0;
5130 foreach($data as $index) {
5131 $result |= 1 << $index;
5133 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5137 * Return XHTML to display the control
5139 * @param array $data array of selected days
5140 * @param string $query
5141 * @return string XHTML for display (field + wrapping div(s)
5143 public function output_html($data, $query='') {
5144 global $OUTPUT;
5146 // The order matters very much because of the implied numeric keys.
5147 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5148 $context = (object) [
5149 'name' => $this->get_full_name(),
5150 'id' => $this->get_id(),
5151 'days' => array_map(function($index) use ($days, $data) {
5152 return [
5153 'index' => $index,
5154 'label' => get_string($days[$index], 'calendar'),
5155 'checked' => in_array($index, $data)
5157 }, array_keys($days))
5160 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5162 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5169 * Admin setting that allows a user to pick a behaviour.
5171 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5173 class admin_setting_question_behaviour extends admin_setting_configselect {
5175 * @param string $name name of config variable
5176 * @param string $visiblename display name
5177 * @param string $description description
5178 * @param string $default default.
5180 public function __construct($name, $visiblename, $description, $default) {
5181 parent::__construct($name, $visiblename, $description, $default, null);
5185 * Load list of behaviours as choices
5186 * @return bool true => success, false => error.
5188 public function load_choices() {
5189 global $CFG;
5190 require_once($CFG->dirroot . '/question/engine/lib.php');
5191 $this->choices = question_engine::get_behaviour_options('');
5192 return true;
5198 * Admin setting that allows a user to pick appropriate roles for something.
5200 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5202 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5203 /** @var array Array of capabilities which identify roles */
5204 private $types;
5207 * @param string $name Name of config variable
5208 * @param string $visiblename Display name
5209 * @param string $description Description
5210 * @param array $types Array of archetypes which identify
5211 * roles that will be enabled by default.
5213 public function __construct($name, $visiblename, $description, $types) {
5214 parent::__construct($name, $visiblename, $description, NULL, NULL);
5215 $this->types = $types;
5219 * Load roles as choices
5221 * @return bool true=>success, false=>error
5223 public function load_choices() {
5224 global $CFG, $DB;
5225 if (during_initial_install()) {
5226 return false;
5228 if (is_array($this->choices)) {
5229 return true;
5231 if ($roles = get_all_roles()) {
5232 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5233 return true;
5234 } else {
5235 return false;
5240 * Return the default setting for this control
5242 * @return array Array of default settings
5244 public function get_defaultsetting() {
5245 global $CFG;
5247 if (during_initial_install()) {
5248 return null;
5250 $result = array();
5251 foreach($this->types as $archetype) {
5252 if ($caproles = get_archetype_roles($archetype)) {
5253 foreach ($caproles as $caprole) {
5254 $result[$caprole->id] = 1;
5258 return $result;
5264 * Admin setting that is a list of installed filter plugins.
5266 * @copyright 2015 The Open University
5267 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5269 class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5272 * Constructor
5274 * @param string $name unique ascii name, either 'mysetting' for settings
5275 * that in config, or 'myplugin/mysetting' for ones in config_plugins.
5276 * @param string $visiblename localised name
5277 * @param string $description localised long description
5278 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5280 public function __construct($name, $visiblename, $description, $default) {
5281 if (empty($default)) {
5282 $default = array();
5284 $this->load_choices();
5285 foreach ($default as $plugin) {
5286 if (!isset($this->choices[$plugin])) {
5287 unset($default[$plugin]);
5290 parent::__construct($name, $visiblename, $description, $default, null);
5293 public function load_choices() {
5294 if (is_array($this->choices)) {
5295 return true;
5297 $this->choices = array();
5299 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5300 $this->choices[$plugin] = filter_get_name($plugin);
5302 return true;
5308 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5310 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5312 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5314 * Constructor
5315 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5316 * @param string $visiblename localised
5317 * @param string $description long localised info
5318 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5319 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5320 * @param int $size default field size
5322 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5323 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5324 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5330 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5332 * @copyright 2009 Petr Skoda (http://skodak.org)
5333 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5335 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5338 * Constructor
5339 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5340 * @param string $visiblename localised
5341 * @param string $description long localised info
5342 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5343 * @param string $yes value used when checked
5344 * @param string $no value used when not checked
5346 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5347 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5348 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5355 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5357 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5359 * @copyright 2010 Sam Hemelryk
5360 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5362 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5364 * Constructor
5365 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5366 * @param string $visiblename localised
5367 * @param string $description long localised info
5368 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5369 * @param string $yes value used when checked
5370 * @param string $no value used when not checked
5372 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5373 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5374 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5381 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5383 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5385 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5387 * Calls parent::__construct with specific arguments
5389 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5390 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5391 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5397 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5399 * @copyright 2017 Marina Glancy
5400 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5402 class admin_setting_configselect_with_lock extends admin_setting_configselect {
5404 * Constructor
5405 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5406 * or 'myplugin/mysetting' for ones in config_plugins.
5407 * @param string $visiblename localised
5408 * @param string $description long localised info
5409 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5410 * @param array $choices array of $value=>$label for each selection
5412 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5413 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5414 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5420 * Graded roles in gradebook
5422 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5424 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5426 * Calls parent::__construct with specific arguments
5428 public function __construct() {
5429 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5430 get_string('configgradebookroles', 'admin'),
5431 array('student'));
5438 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5440 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5442 * Saves the new settings passed in $data
5444 * @param string $data
5445 * @return mixed string or Array
5447 public function write_setting($data) {
5448 global $CFG, $DB;
5450 $oldvalue = $this->config_read($this->name);
5451 $return = parent::write_setting($data);
5452 $newvalue = $this->config_read($this->name);
5454 if ($oldvalue !== $newvalue) {
5455 // force full regrading
5456 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5459 return $return;
5465 * Which roles to show on course description page
5467 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5469 class admin_setting_special_coursecontact extends admin_setting_pickroles {
5471 * Calls parent::__construct with specific arguments
5473 public function __construct() {
5474 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
5475 get_string('coursecontact_desc', 'admin'),
5476 array('editingteacher'));
5477 $this->set_updatedcallback(function (){
5478 cache::make('core', 'coursecontacts')->purge();
5486 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5488 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
5490 * Calls parent::__construct with specific arguments
5492 public function __construct() {
5493 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
5494 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
5498 * Old syntax of class constructor. Deprecated in PHP7.
5500 * @deprecated since Moodle 3.1
5502 public function admin_setting_special_gradelimiting() {
5503 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
5504 self::__construct();
5508 * Force site regrading
5510 function regrade_all() {
5511 global $CFG;
5512 require_once("$CFG->libdir/gradelib.php");
5513 grade_force_site_regrading();
5517 * Saves the new settings
5519 * @param mixed $data
5520 * @return string empty string or error message
5522 function write_setting($data) {
5523 $previous = $this->get_setting();
5525 if ($previous === null) {
5526 if ($data) {
5527 $this->regrade_all();
5529 } else {
5530 if ($data != $previous) {
5531 $this->regrade_all();
5534 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
5540 * Special setting for $CFG->grade_minmaxtouse.
5542 * @package core
5543 * @copyright 2015 Frédéric Massart - FMCorz.net
5544 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5546 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
5549 * Constructor.
5551 public function __construct() {
5552 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
5553 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
5554 array(
5555 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
5556 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
5562 * Saves the new setting.
5564 * @param mixed $data
5565 * @return string empty string or error message
5567 function write_setting($data) {
5568 global $CFG;
5570 $previous = $this->get_setting();
5571 $result = parent::write_setting($data);
5573 // If saved and the value has changed.
5574 if (empty($result) && $previous != $data) {
5575 require_once($CFG->libdir . '/gradelib.php');
5576 grade_force_site_regrading();
5579 return $result;
5586 * Primary grade export plugin - has state tracking.
5588 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5590 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
5592 * Calls parent::__construct with specific arguments
5594 public function __construct() {
5595 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
5596 get_string('configgradeexport', 'admin'), array(), NULL);
5600 * Load the available choices for the multicheckbox
5602 * @return bool always returns true
5604 public function load_choices() {
5605 if (is_array($this->choices)) {
5606 return true;
5608 $this->choices = array();
5610 if ($plugins = core_component::get_plugin_list('gradeexport')) {
5611 foreach($plugins as $plugin => $unused) {
5612 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
5615 return true;
5621 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
5623 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5625 class admin_setting_special_gradepointdefault extends admin_setting_configtext {
5627 * Config gradepointmax constructor
5629 * @param string $name Overidden by "gradepointmax"
5630 * @param string $visiblename Overridden by "gradepointmax" language string.
5631 * @param string $description Overridden by "gradepointmax_help" language string.
5632 * @param string $defaultsetting Not used, overridden by 100.
5633 * @param mixed $paramtype Overridden by PARAM_INT.
5634 * @param int $size Overridden by 5.
5636 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5637 $name = 'gradepointdefault';
5638 $visiblename = get_string('gradepointdefault', 'grades');
5639 $description = get_string('gradepointdefault_help', 'grades');
5640 $defaultsetting = 100;
5641 $paramtype = PARAM_INT;
5642 $size = 5;
5643 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5647 * Validate data before storage
5648 * @param string $data The submitted data
5649 * @return bool|string true if ok, string if error found
5651 public function validate($data) {
5652 global $CFG;
5653 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
5654 return true;
5655 } else {
5656 return get_string('gradepointdefault_validateerror', 'grades');
5663 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
5665 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5667 class admin_setting_special_gradepointmax extends admin_setting_configtext {
5670 * Config gradepointmax constructor
5672 * @param string $name Overidden by "gradepointmax"
5673 * @param string $visiblename Overridden by "gradepointmax" language string.
5674 * @param string $description Overridden by "gradepointmax_help" language string.
5675 * @param string $defaultsetting Not used, overridden by 100.
5676 * @param mixed $paramtype Overridden by PARAM_INT.
5677 * @param int $size Overridden by 5.
5679 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5680 $name = 'gradepointmax';
5681 $visiblename = get_string('gradepointmax', 'grades');
5682 $description = get_string('gradepointmax_help', 'grades');
5683 $defaultsetting = 100;
5684 $paramtype = PARAM_INT;
5685 $size = 5;
5686 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5690 * Save the selected setting
5692 * @param string $data The selected site
5693 * @return string empty string or error message
5695 public function write_setting($data) {
5696 if ($data === '') {
5697 $data = (int)$this->defaultsetting;
5698 } else {
5699 $data = $data;
5701 return parent::write_setting($data);
5705 * Validate data before storage
5706 * @param string $data The submitted data
5707 * @return bool|string true if ok, string if error found
5709 public function validate($data) {
5710 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
5711 return true;
5712 } else {
5713 return get_string('gradepointmax_validateerror', 'grades');
5718 * Return an XHTML string for the setting
5719 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5720 * @param string $query search query to be highlighted
5721 * @return string XHTML to display control
5723 public function output_html($data, $query = '') {
5724 global $OUTPUT;
5726 $default = $this->get_defaultsetting();
5727 $context = (object) [
5728 'size' => $this->size,
5729 'id' => $this->get_id(),
5730 'name' => $this->get_full_name(),
5731 'value' => $data,
5732 'attributes' => [
5733 'maxlength' => 5
5735 'forceltr' => $this->get_force_ltr()
5737 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
5739 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
5745 * Grade category settings
5747 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5749 class admin_setting_gradecat_combo extends admin_setting {
5750 /** @var array Array of choices */
5751 public $choices;
5754 * Sets choices and calls parent::__construct with passed arguments
5755 * @param string $name
5756 * @param string $visiblename
5757 * @param string $description
5758 * @param mixed $defaultsetting string or array depending on implementation
5759 * @param array $choices An array of choices for the control
5761 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5762 $this->choices = $choices;
5763 parent::__construct($name, $visiblename, $description, $defaultsetting);
5767 * Return the current setting(s) array
5769 * @return array Array of value=>xx, forced=>xx, adv=>xx
5771 public function get_setting() {
5772 global $CFG;
5774 $value = $this->config_read($this->name);
5775 $flag = $this->config_read($this->name.'_flag');
5777 if (is_null($value) or is_null($flag)) {
5778 return NULL;
5781 $flag = (int)$flag;
5782 $forced = (boolean)(1 & $flag); // first bit
5783 $adv = (boolean)(2 & $flag); // second bit
5785 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
5789 * Save the new settings passed in $data
5791 * @todo Add vartype handling to ensure $data is array
5792 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5793 * @return string empty or error message
5795 public function write_setting($data) {
5796 global $CFG;
5798 $value = $data['value'];
5799 $forced = empty($data['forced']) ? 0 : 1;
5800 $adv = empty($data['adv']) ? 0 : 2;
5801 $flag = ($forced | $adv); //bitwise or
5803 if (!in_array($value, array_keys($this->choices))) {
5804 return 'Error setting ';
5807 $oldvalue = $this->config_read($this->name);
5808 $oldflag = (int)$this->config_read($this->name.'_flag');
5809 $oldforced = (1 & $oldflag); // first bit
5811 $result1 = $this->config_write($this->name, $value);
5812 $result2 = $this->config_write($this->name.'_flag', $flag);
5814 // force regrade if needed
5815 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
5816 require_once($CFG->libdir.'/gradelib.php');
5817 grade_category::updated_forced_settings();
5820 if ($result1 and $result2) {
5821 return '';
5822 } else {
5823 return get_string('errorsetting', 'admin');
5828 * Return XHTML to display the field and wrapping div
5830 * @todo Add vartype handling to ensure $data is array
5831 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5832 * @param string $query
5833 * @return string XHTML to display control
5835 public function output_html($data, $query='') {
5836 global $OUTPUT;
5838 $value = $data['value'];
5840 $default = $this->get_defaultsetting();
5841 if (!is_null($default)) {
5842 $defaultinfo = array();
5843 if (isset($this->choices[$default['value']])) {
5844 $defaultinfo[] = $this->choices[$default['value']];
5846 if (!empty($default['forced'])) {
5847 $defaultinfo[] = get_string('force');
5849 if (!empty($default['adv'])) {
5850 $defaultinfo[] = get_string('advanced');
5852 $defaultinfo = implode(', ', $defaultinfo);
5854 } else {
5855 $defaultinfo = NULL;
5858 $options = $this->choices;
5859 $context = (object) [
5860 'id' => $this->get_id(),
5861 'name' => $this->get_full_name(),
5862 'forced' => !empty($data['forced']),
5863 'advanced' => !empty($data['adv']),
5864 'options' => array_map(function($option) use ($options, $value) {
5865 return [
5866 'value' => $option,
5867 'name' => $options[$option],
5868 'selected' => $option == $value
5870 }, array_keys($options)),
5873 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
5875 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
5881 * Selection of grade report in user profiles
5883 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5885 class admin_setting_grade_profilereport extends admin_setting_configselect {
5887 * Calls parent::__construct with specific arguments
5889 public function __construct() {
5890 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
5894 * Loads an array of choices for the configselect control
5896 * @return bool always return true
5898 public function load_choices() {
5899 if (is_array($this->choices)) {
5900 return true;
5902 $this->choices = array();
5904 global $CFG;
5905 require_once($CFG->libdir.'/gradelib.php');
5907 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
5908 if (file_exists($plugindir.'/lib.php')) {
5909 require_once($plugindir.'/lib.php');
5910 $functionname = 'grade_report_'.$plugin.'_profilereport';
5911 if (function_exists($functionname)) {
5912 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
5916 return true;
5921 * Provides a selection of grade reports to be used for "grades".
5923 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
5924 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5926 class admin_setting_my_grades_report extends admin_setting_configselect {
5929 * Calls parent::__construct with specific arguments.
5931 public function __construct() {
5932 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
5933 new lang_string('mygrades_desc', 'grades'), 'overview', null);
5937 * Loads an array of choices for the configselect control.
5939 * @return bool always returns true.
5941 public function load_choices() {
5942 global $CFG; // Remove this line and behold the horror of behat test failures!
5943 $this->choices = array();
5944 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
5945 if (file_exists($plugindir . '/lib.php')) {
5946 require_once($plugindir . '/lib.php');
5947 // Check to see if the class exists. Check the correct plugin convention first.
5948 if (class_exists('gradereport_' . $plugin)) {
5949 $classname = 'gradereport_' . $plugin;
5950 } else if (class_exists('grade_report_' . $plugin)) {
5951 // We are using the old plugin naming convention.
5952 $classname = 'grade_report_' . $plugin;
5953 } else {
5954 continue;
5956 if ($classname::supports_mygrades()) {
5957 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
5961 // Add an option to specify an external url.
5962 $this->choices['external'] = get_string('externalurl', 'grades');
5963 return true;
5968 * Special class for register auth selection
5970 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5972 class admin_setting_special_registerauth extends admin_setting_configselect {
5974 * Calls parent::__construct with specific arguments
5976 public function __construct() {
5977 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
5981 * Returns the default option
5983 * @return string empty or default option
5985 public function get_defaultsetting() {
5986 $this->load_choices();
5987 $defaultsetting = parent::get_defaultsetting();
5988 if (array_key_exists($defaultsetting, $this->choices)) {
5989 return $defaultsetting;
5990 } else {
5991 return '';
5996 * Loads the possible choices for the array
5998 * @return bool always returns true
6000 public function load_choices() {
6001 global $CFG;
6003 if (is_array($this->choices)) {
6004 return true;
6006 $this->choices = array();
6007 $this->choices[''] = get_string('disable');
6009 $authsenabled = get_enabled_auth_plugins(true);
6011 foreach ($authsenabled as $auth) {
6012 $authplugin = get_auth_plugin($auth);
6013 if (!$authplugin->can_signup()) {
6014 continue;
6016 // Get the auth title (from core or own auth lang files)
6017 $authtitle = $authplugin->get_title();
6018 $this->choices[$auth] = $authtitle;
6020 return true;
6026 * General plugins manager
6028 class admin_page_pluginsoverview extends admin_externalpage {
6031 * Sets basic information about the external page
6033 public function __construct() {
6034 global $CFG;
6035 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6036 "$CFG->wwwroot/$CFG->admin/plugins.php");
6041 * Module manage page
6043 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6045 class admin_page_managemods extends admin_externalpage {
6047 * Calls parent::__construct with specific arguments
6049 public function __construct() {
6050 global $CFG;
6051 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6055 * Try to find the specified module
6057 * @param string $query The module to search for
6058 * @return array
6060 public function search($query) {
6061 global $CFG, $DB;
6062 if ($result = parent::search($query)) {
6063 return $result;
6066 $found = false;
6067 if ($modules = $DB->get_records('modules')) {
6068 foreach ($modules as $module) {
6069 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6070 continue;
6072 if (strpos($module->name, $query) !== false) {
6073 $found = true;
6074 break;
6076 $strmodulename = get_string('modulename', $module->name);
6077 if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6078 $found = true;
6079 break;
6083 if ($found) {
6084 $result = new stdClass();
6085 $result->page = $this;
6086 $result->settings = array();
6087 return array($this->name => $result);
6088 } else {
6089 return array();
6096 * Special class for enrol plugins management.
6098 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6099 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6101 class admin_setting_manageenrols extends admin_setting {
6103 * Calls parent::__construct with specific arguments
6105 public function __construct() {
6106 $this->nosave = true;
6107 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6111 * Always returns true, does nothing
6113 * @return true
6115 public function get_setting() {
6116 return true;
6120 * Always returns true, does nothing
6122 * @return true
6124 public function get_defaultsetting() {
6125 return true;
6129 * Always returns '', does not write anything
6131 * @return string Always returns ''
6133 public function write_setting($data) {
6134 // do not write any setting
6135 return '';
6139 * Checks if $query is one of the available enrol plugins
6141 * @param string $query The string to search for
6142 * @return bool Returns true if found, false if not
6144 public function is_related($query) {
6145 if (parent::is_related($query)) {
6146 return true;
6149 $query = core_text::strtolower($query);
6150 $enrols = enrol_get_plugins(false);
6151 foreach ($enrols as $name=>$enrol) {
6152 $localised = get_string('pluginname', 'enrol_'.$name);
6153 if (strpos(core_text::strtolower($name), $query) !== false) {
6154 return true;
6156 if (strpos(core_text::strtolower($localised), $query) !== false) {
6157 return true;
6160 return false;
6164 * Builds the XHTML to display the control
6166 * @param string $data Unused
6167 * @param string $query
6168 * @return string
6170 public function output_html($data, $query='') {
6171 global $CFG, $OUTPUT, $DB, $PAGE;
6173 // Display strings.
6174 $strup = get_string('up');
6175 $strdown = get_string('down');
6176 $strsettings = get_string('settings');
6177 $strenable = get_string('enable');
6178 $strdisable = get_string('disable');
6179 $struninstall = get_string('uninstallplugin', 'core_admin');
6180 $strusage = get_string('enrolusage', 'enrol');
6181 $strversion = get_string('version');
6182 $strtest = get_string('testsettings', 'core_enrol');
6184 $pluginmanager = core_plugin_manager::instance();
6186 $enrols_available = enrol_get_plugins(false);
6187 $active_enrols = enrol_get_plugins(true);
6189 $allenrols = array();
6190 foreach ($active_enrols as $key=>$enrol) {
6191 $allenrols[$key] = true;
6193 foreach ($enrols_available as $key=>$enrol) {
6194 $allenrols[$key] = true;
6196 // Now find all borked plugins and at least allow then to uninstall.
6197 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6198 foreach ($condidates as $candidate) {
6199 if (empty($allenrols[$candidate])) {
6200 $allenrols[$candidate] = true;
6204 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6205 $return .= $OUTPUT->box_start('generalbox enrolsui');
6207 $table = new html_table();
6208 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6209 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6210 $table->id = 'courseenrolmentplugins';
6211 $table->attributes['class'] = 'admintable generaltable';
6212 $table->data = array();
6214 // Iterate through enrol plugins and add to the display table.
6215 $updowncount = 1;
6216 $enrolcount = count($active_enrols);
6217 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6218 $printed = array();
6219 foreach($allenrols as $enrol => $unused) {
6220 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6221 $version = get_config('enrol_'.$enrol, 'version');
6222 if ($version === false) {
6223 $version = '';
6226 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6227 $name = get_string('pluginname', 'enrol_'.$enrol);
6228 } else {
6229 $name = $enrol;
6231 // Usage.
6232 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6233 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6234 $usage = "$ci / $cp";
6236 // Hide/show links.
6237 $class = '';
6238 if (isset($active_enrols[$enrol])) {
6239 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6240 $hideshow = "<a href=\"$aurl\">";
6241 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6242 $enabled = true;
6243 $displayname = $name;
6244 } else if (isset($enrols_available[$enrol])) {
6245 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6246 $hideshow = "<a href=\"$aurl\">";
6247 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6248 $enabled = false;
6249 $displayname = $name;
6250 $class = 'dimmed_text';
6251 } else {
6252 $hideshow = '';
6253 $enabled = false;
6254 $displayname = '<span class="notifyproblem">'.$name.'</span>';
6256 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6257 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6258 } else {
6259 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6262 // Up/down link (only if enrol is enabled).
6263 $updown = '';
6264 if ($enabled) {
6265 if ($updowncount > 1) {
6266 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6267 $updown .= "<a href=\"$aurl\">";
6268 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6269 } else {
6270 $updown .= $OUTPUT->spacer() . '&nbsp;';
6272 if ($updowncount < $enrolcount) {
6273 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6274 $updown .= "<a href=\"$aurl\">";
6275 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6276 } else {
6277 $updown .= $OUTPUT->spacer() . '&nbsp;';
6279 ++$updowncount;
6282 // Add settings link.
6283 if (!$version) {
6284 $settings = '';
6285 } else if ($surl = $plugininfo->get_settings_url()) {
6286 $settings = html_writer::link($surl, $strsettings);
6287 } else {
6288 $settings = '';
6291 // Add uninstall info.
6292 $uninstall = '';
6293 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6294 $uninstall = html_writer::link($uninstallurl, $struninstall);
6297 $test = '';
6298 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6299 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
6300 $test = html_writer::link($testsettingsurl, $strtest);
6303 // Add a row to the table.
6304 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6305 if ($class) {
6306 $row->attributes['class'] = $class;
6308 $table->data[] = $row;
6310 $printed[$enrol] = true;
6313 $return .= html_writer::table($table);
6314 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6315 $return .= $OUTPUT->box_end();
6316 return highlight($query, $return);
6322 * Blocks manage page
6324 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6326 class admin_page_manageblocks extends admin_externalpage {
6328 * Calls parent::__construct with specific arguments
6330 public function __construct() {
6331 global $CFG;
6332 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6336 * Search for a specific block
6338 * @param string $query The string to search for
6339 * @return array
6341 public function search($query) {
6342 global $CFG, $DB;
6343 if ($result = parent::search($query)) {
6344 return $result;
6347 $found = false;
6348 if ($blocks = $DB->get_records('block')) {
6349 foreach ($blocks as $block) {
6350 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6351 continue;
6353 if (strpos($block->name, $query) !== false) {
6354 $found = true;
6355 break;
6357 $strblockname = get_string('pluginname', 'block_'.$block->name);
6358 if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6359 $found = true;
6360 break;
6364 if ($found) {
6365 $result = new stdClass();
6366 $result->page = $this;
6367 $result->settings = array();
6368 return array($this->name => $result);
6369 } else {
6370 return array();
6376 * Message outputs configuration
6378 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6380 class admin_page_managemessageoutputs extends admin_externalpage {
6382 * Calls parent::__construct with specific arguments
6384 public function __construct() {
6385 global $CFG;
6386 parent::__construct('managemessageoutputs',
6387 get_string('defaultmessageoutputs', 'message'),
6388 new moodle_url('/admin/message.php')
6393 * Search for a specific message processor
6395 * @param string $query The string to search for
6396 * @return array
6398 public function search($query) {
6399 global $CFG, $DB;
6400 if ($result = parent::search($query)) {
6401 return $result;
6404 $found = false;
6405 if ($processors = get_message_processors()) {
6406 foreach ($processors as $processor) {
6407 if (!$processor->available) {
6408 continue;
6410 if (strpos($processor->name, $query) !== false) {
6411 $found = true;
6412 break;
6414 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6415 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6416 $found = true;
6417 break;
6421 if ($found) {
6422 $result = new stdClass();
6423 $result->page = $this;
6424 $result->settings = array();
6425 return array($this->name => $result);
6426 } else {
6427 return array();
6433 * Default message outputs configuration
6435 * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6436 * @todo MDL-64866 This will be deleted in Moodle 4.1.
6438 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6440 class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
6442 * Calls parent::__construct with specific arguments
6444 * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6445 * @todo MDL-64866 This will be deleted in Moodle 4.1.
6447 public function __construct() {
6448 global $CFG;
6450 debugging('admin_page_defaultmessageoutputs class is deprecated. Please use admin_page_managemessageoutputs instead.',
6451 DEBUG_DEVELOPER);
6453 admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
6459 * Manage question behaviours page
6461 * @copyright 2011 The Open University
6462 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6464 class admin_page_manageqbehaviours extends admin_externalpage {
6466 * Constructor
6468 public function __construct() {
6469 global $CFG;
6470 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
6471 new moodle_url('/admin/qbehaviours.php'));
6475 * Search question behaviours for the specified string
6477 * @param string $query The string to search for in question behaviours
6478 * @return array
6480 public function search($query) {
6481 global $CFG;
6482 if ($result = parent::search($query)) {
6483 return $result;
6486 $found = false;
6487 require_once($CFG->dirroot . '/question/engine/lib.php');
6488 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
6489 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
6490 $query) !== false) {
6491 $found = true;
6492 break;
6495 if ($found) {
6496 $result = new stdClass();
6497 $result->page = $this;
6498 $result->settings = array();
6499 return array($this->name => $result);
6500 } else {
6501 return array();
6508 * Question type manage page
6510 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6512 class admin_page_manageqtypes extends admin_externalpage {
6514 * Calls parent::__construct with specific arguments
6516 public function __construct() {
6517 global $CFG;
6518 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
6519 new moodle_url('/admin/qtypes.php'));
6523 * Search question types for the specified string
6525 * @param string $query The string to search for in question types
6526 * @return array
6528 public function search($query) {
6529 global $CFG;
6530 if ($result = parent::search($query)) {
6531 return $result;
6534 $found = false;
6535 require_once($CFG->dirroot . '/question/engine/bank.php');
6536 foreach (question_bank::get_all_qtypes() as $qtype) {
6537 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
6538 $found = true;
6539 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 class admin_page_manageportfolios extends admin_externalpage {
6556 * Calls parent::__construct with specific arguments
6558 public function __construct() {
6559 global $CFG;
6560 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
6561 "$CFG->wwwroot/$CFG->admin/portfolio.php");
6565 * Searches page for the specified string.
6566 * @param string $query The string to search for
6567 * @return bool True if it is found on this page
6569 public function search($query) {
6570 global $CFG;
6571 if ($result = parent::search($query)) {
6572 return $result;
6575 $found = false;
6576 $portfolios = core_component::get_plugin_list('portfolio');
6577 foreach ($portfolios as $p => $dir) {
6578 if (strpos($p, $query) !== false) {
6579 $found = true;
6580 break;
6583 if (!$found) {
6584 foreach (portfolio_instances(false, false) as $instance) {
6585 $title = $instance->get('name');
6586 if (strpos(core_text::strtolower($title), $query) !== false) {
6587 $found = true;
6588 break;
6593 if ($found) {
6594 $result = new stdClass();
6595 $result->page = $this;
6596 $result->settings = array();
6597 return array($this->name => $result);
6598 } else {
6599 return array();
6605 class admin_page_managerepositories extends admin_externalpage {
6607 * Calls parent::__construct with specific arguments
6609 public function __construct() {
6610 global $CFG;
6611 parent::__construct('managerepositories', get_string('manage',
6612 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
6616 * Searches page for the specified string.
6617 * @param string $query The string to search for
6618 * @return bool True if it is found on this page
6620 public function search($query) {
6621 global $CFG;
6622 if ($result = parent::search($query)) {
6623 return $result;
6626 $found = false;
6627 $repositories= core_component::get_plugin_list('repository');
6628 foreach ($repositories as $p => $dir) {
6629 if (strpos($p, $query) !== false) {
6630 $found = true;
6631 break;
6634 if (!$found) {
6635 foreach (repository::get_types() as $instance) {
6636 $title = $instance->get_typename();
6637 if (strpos(core_text::strtolower($title), $query) !== false) {
6638 $found = true;
6639 break;
6644 if ($found) {
6645 $result = new stdClass();
6646 $result->page = $this;
6647 $result->settings = array();
6648 return array($this->name => $result);
6649 } else {
6650 return array();
6657 * Special class for authentication administration.
6659 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6661 class admin_setting_manageauths extends admin_setting {
6663 * Calls parent::__construct with specific arguments
6665 public function __construct() {
6666 $this->nosave = true;
6667 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
6671 * Always returns true
6673 * @return true
6675 public function get_setting() {
6676 return true;
6680 * Always returns true
6682 * @return true
6684 public function get_defaultsetting() {
6685 return true;
6689 * Always returns '' and doesn't write anything
6691 * @return string Always returns ''
6693 public function write_setting($data) {
6694 // do not write any setting
6695 return '';
6699 * Search to find if Query is related to auth plugin
6701 * @param string $query The string to search for
6702 * @return bool true for related false for not
6704 public function is_related($query) {
6705 if (parent::is_related($query)) {
6706 return true;
6709 $authsavailable = core_component::get_plugin_list('auth');
6710 foreach ($authsavailable as $auth => $dir) {
6711 if (strpos($auth, $query) !== false) {
6712 return true;
6714 $authplugin = get_auth_plugin($auth);
6715 $authtitle = $authplugin->get_title();
6716 if (strpos(core_text::strtolower($authtitle), $query) !== false) {
6717 return true;
6720 return false;
6724 * Return XHTML to display control
6726 * @param mixed $data Unused
6727 * @param string $query
6728 * @return string highlight
6730 public function output_html($data, $query='') {
6731 global $CFG, $OUTPUT, $DB;
6733 // display strings
6734 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
6735 'settings', 'edit', 'name', 'enable', 'disable',
6736 'up', 'down', 'none', 'users'));
6737 $txt->updown = "$txt->up/$txt->down";
6738 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
6739 $txt->testsettings = get_string('testsettings', 'core_auth');
6741 $authsavailable = core_component::get_plugin_list('auth');
6742 get_enabled_auth_plugins(true); // fix the list of enabled auths
6743 if (empty($CFG->auth)) {
6744 $authsenabled = array();
6745 } else {
6746 $authsenabled = explode(',', $CFG->auth);
6749 // construct the display array, with enabled auth plugins at the top, in order
6750 $displayauths = array();
6751 $registrationauths = array();
6752 $registrationauths[''] = $txt->disable;
6753 $authplugins = array();
6754 foreach ($authsenabled as $auth) {
6755 $authplugin = get_auth_plugin($auth);
6756 $authplugins[$auth] = $authplugin;
6757 /// Get the auth title (from core or own auth lang files)
6758 $authtitle = $authplugin->get_title();
6759 /// Apply titles
6760 $displayauths[$auth] = $authtitle;
6761 if ($authplugin->can_signup()) {
6762 $registrationauths[$auth] = $authtitle;
6766 foreach ($authsavailable as $auth => $dir) {
6767 if (array_key_exists($auth, $displayauths)) {
6768 continue; //already in the list
6770 $authplugin = get_auth_plugin($auth);
6771 $authplugins[$auth] = $authplugin;
6772 /// Get the auth title (from core or own auth lang files)
6773 $authtitle = $authplugin->get_title();
6774 /// Apply titles
6775 $displayauths[$auth] = $authtitle;
6776 if ($authplugin->can_signup()) {
6777 $registrationauths[$auth] = $authtitle;
6781 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
6782 $return .= $OUTPUT->box_start('generalbox authsui');
6784 $table = new html_table();
6785 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
6786 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6787 $table->data = array();
6788 $table->attributes['class'] = 'admintable generaltable';
6789 $table->id = 'manageauthtable';
6791 //add always enabled plugins first
6792 $displayname = $displayauths['manual'];
6793 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
6794 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
6795 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
6796 $displayname = $displayauths['nologin'];
6797 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
6798 $table->data[] = array($displayname, $usercount, '', '', '', '', '');
6801 // iterate through auth plugins and add to the display table
6802 $updowncount = 1;
6803 $authcount = count($authsenabled);
6804 $url = "auth.php?sesskey=" . sesskey();
6805 foreach ($displayauths as $auth => $name) {
6806 if ($auth == 'manual' or $auth == 'nologin') {
6807 continue;
6809 $class = '';
6810 // hide/show link
6811 if (in_array($auth, $authsenabled)) {
6812 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
6813 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
6814 $enabled = true;
6815 $displayname = $name;
6817 else {
6818 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
6819 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
6820 $enabled = false;
6821 $displayname = $name;
6822 $class = 'dimmed_text';
6825 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
6827 // up/down link (only if auth is enabled)
6828 $updown = '';
6829 if ($enabled) {
6830 if ($updowncount > 1) {
6831 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
6832 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
6834 else {
6835 $updown .= $OUTPUT->spacer() . '&nbsp;';
6837 if ($updowncount < $authcount) {
6838 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
6839 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
6841 else {
6842 $updown .= $OUTPUT->spacer() . '&nbsp;';
6844 ++ $updowncount;
6847 // settings link
6848 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
6849 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
6850 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
6851 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
6852 } else {
6853 $settings = '';
6856 // Uninstall link.
6857 $uninstall = '';
6858 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
6859 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
6862 $test = '';
6863 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
6864 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
6865 $test = html_writer::link($testurl, $txt->testsettings);
6868 // Add a row to the table.
6869 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
6870 if ($class) {
6871 $row->attributes['class'] = $class;
6873 $table->data[] = $row;
6875 $return .= html_writer::table($table);
6876 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
6877 $return .= $OUTPUT->box_end();
6878 return highlight($query, $return);
6884 * Special class for authentication administration.
6886 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6888 class admin_setting_manageeditors extends admin_setting {
6890 * Calls parent::__construct with specific arguments
6892 public function __construct() {
6893 $this->nosave = true;
6894 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
6898 * Always returns true, does nothing
6900 * @return true
6902 public function get_setting() {
6903 return true;
6907 * Always returns true, does nothing
6909 * @return true
6911 public function get_defaultsetting() {
6912 return true;
6916 * Always returns '', does not write anything
6918 * @return string Always returns ''
6920 public function write_setting($data) {
6921 // do not write any setting
6922 return '';
6926 * Checks if $query is one of the available editors
6928 * @param string $query The string to search for
6929 * @return bool Returns true if found, false if not
6931 public function is_related($query) {
6932 if (parent::is_related($query)) {
6933 return true;
6936 $editors_available = editors_get_available();
6937 foreach ($editors_available as $editor=>$editorstr) {
6938 if (strpos($editor, $query) !== false) {
6939 return true;
6941 if (strpos(core_text::strtolower($editorstr), $query) !== false) {
6942 return true;
6945 return false;
6949 * Builds the XHTML to display the control
6951 * @param string $data Unused
6952 * @param string $query
6953 * @return string
6955 public function output_html($data, $query='') {
6956 global $CFG, $OUTPUT;
6958 // display strings
6959 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
6960 'up', 'down', 'none'));
6961 $struninstall = get_string('uninstallplugin', 'core_admin');
6963 $txt->updown = "$txt->up/$txt->down";
6965 $editors_available = editors_get_available();
6966 $active_editors = explode(',', $CFG->texteditors);
6968 $active_editors = array_reverse($active_editors);
6969 foreach ($active_editors as $key=>$editor) {
6970 if (empty($editors_available[$editor])) {
6971 unset($active_editors[$key]);
6972 } else {
6973 $name = $editors_available[$editor];
6974 unset($editors_available[$editor]);
6975 $editors_available[$editor] = $name;
6978 if (empty($active_editors)) {
6979 //$active_editors = array('textarea');
6981 $editors_available = array_reverse($editors_available, true);
6982 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
6983 $return .= $OUTPUT->box_start('generalbox editorsui');
6985 $table = new html_table();
6986 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
6987 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6988 $table->id = 'editormanagement';
6989 $table->attributes['class'] = 'admintable generaltable';
6990 $table->data = array();
6992 // iterate through auth plugins and add to the display table
6993 $updowncount = 1;
6994 $editorcount = count($active_editors);
6995 $url = "editors.php?sesskey=" . sesskey();
6996 foreach ($editors_available as $editor => $name) {
6997 // hide/show link
6998 $class = '';
6999 if (in_array($editor, $active_editors)) {
7000 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
7001 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7002 $enabled = true;
7003 $displayname = $name;
7005 else {
7006 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
7007 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7008 $enabled = false;
7009 $displayname = $name;
7010 $class = 'dimmed_text';
7013 // up/down link (only if auth is enabled)
7014 $updown = '';
7015 if ($enabled) {
7016 if ($updowncount > 1) {
7017 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
7018 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7020 else {
7021 $updown .= $OUTPUT->spacer() . '&nbsp;';
7023 if ($updowncount < $editorcount) {
7024 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
7025 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7027 else {
7028 $updown .= $OUTPUT->spacer() . '&nbsp;';
7030 ++ $updowncount;
7033 // settings link
7034 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
7035 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
7036 $settings = "<a href='$eurl'>{$txt->settings}</a>";
7037 } else {
7038 $settings = '';
7041 $uninstall = '';
7042 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
7043 $uninstall = html_writer::link($uninstallurl, $struninstall);
7046 // Add a row to the table.
7047 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7048 if ($class) {
7049 $row->attributes['class'] = $class;
7051 $table->data[] = $row;
7053 $return .= html_writer::table($table);
7054 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
7055 $return .= $OUTPUT->box_end();
7056 return highlight($query, $return);
7061 * Special class for antiviruses administration.
7063 * @copyright 2015 Ruslan Kabalin, Lancaster University.
7064 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7066 class admin_setting_manageantiviruses extends admin_setting {
7068 * Calls parent::__construct with specific arguments
7070 public function __construct() {
7071 $this->nosave = true;
7072 parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7076 * Always returns true, does nothing
7078 * @return true
7080 public function get_setting() {
7081 return true;
7085 * Always returns true, does nothing
7087 * @return true
7089 public function get_defaultsetting() {
7090 return true;
7094 * Always returns '', does not write anything
7096 * @param string $data Unused
7097 * @return string Always returns ''
7099 public function write_setting($data) {
7100 // Do not write any setting.
7101 return '';
7105 * Checks if $query is one of the available editors
7107 * @param string $query The string to search for
7108 * @return bool Returns true if found, false if not
7110 public function is_related($query) {
7111 if (parent::is_related($query)) {
7112 return true;
7115 $antivirusesavailable = \core\antivirus\manager::get_available();
7116 foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7117 if (strpos($antivirus, $query) !== false) {
7118 return true;
7120 if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7121 return true;
7124 return false;
7128 * Builds the XHTML to display the control
7130 * @param string $data Unused
7131 * @param string $query
7132 * @return string
7134 public function output_html($data, $query='') {
7135 global $CFG, $OUTPUT;
7137 // Display strings.
7138 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7139 'up', 'down', 'none'));
7140 $struninstall = get_string('uninstallplugin', 'core_admin');
7142 $txt->updown = "$txt->up/$txt->down";
7144 $antivirusesavailable = \core\antivirus\manager::get_available();
7145 $activeantiviruses = explode(',', $CFG->antiviruses);
7147 $activeantiviruses = array_reverse($activeantiviruses);
7148 foreach ($activeantiviruses as $key => $antivirus) {
7149 if (empty($antivirusesavailable[$antivirus])) {
7150 unset($activeantiviruses[$key]);
7151 } else {
7152 $name = $antivirusesavailable[$antivirus];
7153 unset($antivirusesavailable[$antivirus]);
7154 $antivirusesavailable[$antivirus] = $name;
7157 $antivirusesavailable = array_reverse($antivirusesavailable, true);
7158 $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7159 $return .= $OUTPUT->box_start('generalbox antivirusesui');
7161 $table = new html_table();
7162 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7163 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7164 $table->id = 'antivirusmanagement';
7165 $table->attributes['class'] = 'admintable generaltable';
7166 $table->data = array();
7168 // Iterate through auth plugins and add to the display table.
7169 $updowncount = 1;
7170 $antiviruscount = count($activeantiviruses);
7171 $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7172 foreach ($antivirusesavailable as $antivirus => $name) {
7173 // Hide/show link.
7174 $class = '';
7175 if (in_array($antivirus, $activeantiviruses)) {
7176 $hideshowurl = $baseurl;
7177 $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7178 $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7179 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7180 $enabled = true;
7181 $displayname = $name;
7182 } else {
7183 $hideshowurl = $baseurl;
7184 $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7185 $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7186 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7187 $enabled = false;
7188 $displayname = $name;
7189 $class = 'dimmed_text';
7192 // Up/down link.
7193 $updown = '';
7194 if ($enabled) {
7195 if ($updowncount > 1) {
7196 $updownurl = $baseurl;
7197 $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7198 $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7199 $updown = html_writer::link($updownurl, $updownimg);
7200 } else {
7201 $updownimg = $OUTPUT->spacer();
7203 if ($updowncount < $antiviruscount) {
7204 $updownurl = $baseurl;
7205 $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7206 $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7207 $updown = html_writer::link($updownurl, $updownimg);
7208 } else {
7209 $updownimg = $OUTPUT->spacer();
7211 ++ $updowncount;
7214 // Settings link.
7215 if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7216 $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7217 $settings = html_writer::link($eurl, $txt->settings);
7218 } else {
7219 $settings = '';
7222 $uninstall = '';
7223 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7224 $uninstall = html_writer::link($uninstallurl, $struninstall);
7227 // Add a row to the table.
7228 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7229 if ($class) {
7230 $row->attributes['class'] = $class;
7232 $table->data[] = $row;
7234 $return .= html_writer::table($table);
7235 $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7236 $return .= $OUTPUT->box_end();
7237 return highlight($query, $return);
7242 * Special class for license administration.
7244 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7246 class admin_setting_managelicenses extends admin_setting {
7248 * Calls parent::__construct with specific arguments
7250 public function __construct() {
7251 $this->nosave = true;
7252 parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', '');
7256 * Always returns true, does nothing
7258 * @return true
7260 public function get_setting() {
7261 return true;
7265 * Always returns true, does nothing
7267 * @return true
7269 public function get_defaultsetting() {
7270 return true;
7274 * Always returns '', does not write anything
7276 * @return string Always returns ''
7278 public function write_setting($data) {
7279 // do not write any setting
7280 return '';
7284 * Builds the XHTML to display the control
7286 * @param string $data Unused
7287 * @param string $query
7288 * @return string
7290 public function output_html($data, $query='') {
7291 global $CFG, $OUTPUT;
7292 require_once($CFG->libdir . '/licenselib.php');
7293 $url = "licenses.php?sesskey=" . sesskey();
7295 // display strings
7296 $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none'));
7297 $licenses = license_manager::get_licenses();
7299 $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true);
7301 $return .= $OUTPUT->box_start('generalbox editorsui');
7303 $table = new html_table();
7304 $table->head = array($txt->name, $txt->enable);
7305 $table->colclasses = array('leftalign', 'centeralign');
7306 $table->id = 'availablelicenses';
7307 $table->attributes['class'] = 'admintable generaltable';
7308 $table->data = array();
7310 foreach ($licenses as $value) {
7311 $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank'));
7313 if ($value->enabled == 1) {
7314 $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
7315 $OUTPUT->pix_icon('t/hide', get_string('disable')));
7316 } else {
7317 $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
7318 $OUTPUT->pix_icon('t/show', get_string('enable')));
7321 if ($value->shortname == $CFG->sitedefaultlicense) {
7322 $displayname .= ' '.$OUTPUT->pix_icon('t/locked', get_string('default'));
7323 $hideshow = '';
7326 $enabled = true;
7328 $table->data[] =array($displayname, $hideshow);
7330 $return .= html_writer::table($table);
7331 $return .= $OUTPUT->box_end();
7332 return highlight($query, $return);
7337 * Course formats manager. Allows to enable/disable formats and jump to settings
7339 class admin_setting_manageformats extends admin_setting {
7342 * Calls parent::__construct with specific arguments
7344 public function __construct() {
7345 $this->nosave = true;
7346 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7350 * Always returns true
7352 * @return true
7354 public function get_setting() {
7355 return true;
7359 * Always returns true
7361 * @return true
7363 public function get_defaultsetting() {
7364 return true;
7368 * Always returns '' and doesn't write anything
7370 * @param mixed $data string or array, must not be NULL
7371 * @return string Always returns ''
7373 public function write_setting($data) {
7374 // do not write any setting
7375 return '';
7379 * Search to find if Query is related to format plugin
7381 * @param string $query The string to search for
7382 * @return bool true for related false for not
7384 public function is_related($query) {
7385 if (parent::is_related($query)) {
7386 return true;
7388 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7389 foreach ($formats as $format) {
7390 if (strpos($format->component, $query) !== false ||
7391 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7392 return true;
7395 return false;
7399 * Return XHTML to display control
7401 * @param mixed $data Unused
7402 * @param string $query
7403 * @return string highlight
7405 public function output_html($data, $query='') {
7406 global $CFG, $OUTPUT;
7407 $return = '';
7408 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7409 $return .= $OUTPUT->box_start('generalbox formatsui');
7411 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7413 // display strings
7414 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7415 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7416 $txt->updown = "$txt->up/$txt->down";
7418 $table = new html_table();
7419 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7420 $table->align = array('left', 'center', 'center', 'center', 'center');
7421 $table->attributes['class'] = 'manageformattable generaltable admintable';
7422 $table->data = array();
7424 $cnt = 0;
7425 $defaultformat = get_config('moodlecourse', 'format');
7426 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7427 foreach ($formats as $format) {
7428 $url = new moodle_url('/admin/courseformats.php',
7429 array('sesskey' => sesskey(), 'format' => $format->name));
7430 $isdefault = '';
7431 $class = '';
7432 if ($format->is_enabled()) {
7433 $strformatname = $format->displayname;
7434 if ($defaultformat === $format->name) {
7435 $hideshow = $txt->default;
7436 } else {
7437 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7438 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7440 } else {
7441 $strformatname = $format->displayname;
7442 $class = 'dimmed_text';
7443 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7444 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7446 $updown = '';
7447 if ($cnt) {
7448 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7449 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7450 } else {
7451 $updown .= $spacer;
7453 if ($cnt < count($formats) - 1) {
7454 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7455 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7456 } else {
7457 $updown .= $spacer;
7459 $cnt++;
7460 $settings = '';
7461 if ($format->get_settings_url()) {
7462 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7464 $uninstall = '';
7465 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7466 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7468 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7469 if ($class) {
7470 $row->attributes['class'] = $class;
7472 $table->data[] = $row;
7474 $return .= html_writer::table($table);
7475 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7476 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7477 $return .= $OUTPUT->box_end();
7478 return highlight($query, $return);
7483 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7485 * @package core
7486 * @copyright 2018 Toni Barbera
7487 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7489 class admin_setting_managecustomfields extends admin_setting {
7492 * Calls parent::__construct with specific arguments
7494 public function __construct() {
7495 $this->nosave = true;
7496 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7500 * Always returns true
7502 * @return true
7504 public function get_setting() {
7505 return true;
7509 * Always returns true
7511 * @return true
7513 public function get_defaultsetting() {
7514 return true;
7518 * Always returns '' and doesn't write anything
7520 * @param mixed $data string or array, must not be NULL
7521 * @return string Always returns ''
7523 public function write_setting($data) {
7524 // Do not write any setting.
7525 return '';
7529 * Search to find if Query is related to format plugin
7531 * @param string $query The string to search for
7532 * @return bool true for related false for not
7534 public function is_related($query) {
7535 if (parent::is_related($query)) {
7536 return true;
7538 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7539 foreach ($formats as $format) {
7540 if (strpos($format->component, $query) !== false ||
7541 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7542 return true;
7545 return false;
7549 * Return XHTML to display control
7551 * @param mixed $data Unused
7552 * @param string $query
7553 * @return string highlight
7555 public function output_html($data, $query='') {
7556 global $CFG, $OUTPUT;
7557 $return = '';
7558 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7559 $return .= $OUTPUT->box_start('generalbox customfieldsui');
7561 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7563 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7564 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7565 $txt->updown = "$txt->up/$txt->down";
7567 $table = new html_table();
7568 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7569 $table->align = array('left', 'center', 'center', 'center');
7570 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7571 $table->data = array();
7573 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7574 foreach ($fields as $field) {
7575 $url = new moodle_url('/admin/customfields.php',
7576 array('sesskey' => sesskey(), 'field' => $field->name));
7578 if ($field->is_enabled()) {
7579 $strfieldname = $field->displayname;
7580 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7581 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7582 } else {
7583 $strfieldname = $field->displayname;
7584 $class = 'dimmed_text';
7585 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7586 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7588 $settings = '';
7589 if ($field->get_settings_url()) {
7590 $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7592 $uninstall = '';
7593 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7594 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7596 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7597 $table->data[] = $row;
7599 $return .= html_writer::table($table);
7600 $return .= $OUTPUT->box_end();
7601 return highlight($query, $return);
7606 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7608 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
7609 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7611 class admin_setting_managedataformats extends admin_setting {
7614 * Calls parent::__construct with specific arguments
7616 public function __construct() {
7617 $this->nosave = true;
7618 parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7622 * Always returns true
7624 * @return true
7626 public function get_setting() {
7627 return true;
7631 * Always returns true
7633 * @return true
7635 public function get_defaultsetting() {
7636 return true;
7640 * Always returns '' and doesn't write anything
7642 * @param mixed $data string or array, must not be NULL
7643 * @return string Always returns ''
7645 public function write_setting($data) {
7646 // Do not write any setting.
7647 return '';
7651 * Search to find if Query is related to format plugin
7653 * @param string $query The string to search for
7654 * @return bool true for related false for not
7656 public function is_related($query) {
7657 if (parent::is_related($query)) {
7658 return true;
7660 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7661 foreach ($formats as $format) {
7662 if (strpos($format->component, $query) !== false ||
7663 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7664 return true;
7667 return false;
7671 * Return XHTML to display control
7673 * @param mixed $data Unused
7674 * @param string $query
7675 * @return string highlight
7677 public function output_html($data, $query='') {
7678 global $CFG, $OUTPUT;
7679 $return = '';
7681 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7683 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7684 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7685 $txt->updown = "$txt->up/$txt->down";
7687 $table = new html_table();
7688 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7689 $table->align = array('left', 'center', 'center', 'center', 'center');
7690 $table->attributes['class'] = 'manageformattable generaltable admintable';
7691 $table->data = array();
7693 $cnt = 0;
7694 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7695 $totalenabled = 0;
7696 foreach ($formats as $format) {
7697 if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7698 $totalenabled++;
7701 foreach ($formats as $format) {
7702 $status = $format->get_status();
7703 $url = new moodle_url('/admin/dataformats.php',
7704 array('sesskey' => sesskey(), 'name' => $format->name));
7706 $class = '';
7707 if ($format->is_enabled()) {
7708 $strformatname = $format->displayname;
7709 if ($totalenabled == 1&& $format->is_enabled()) {
7710 $hideshow = '';
7711 } else {
7712 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7713 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7715 } else {
7716 $class = 'dimmed_text';
7717 $strformatname = $format->displayname;
7718 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7719 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7722 $updown = '';
7723 if ($cnt) {
7724 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7725 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7726 } else {
7727 $updown .= $spacer;
7729 if ($cnt < count($formats) - 1) {
7730 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7731 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7732 } else {
7733 $updown .= $spacer;
7736 $uninstall = '';
7737 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
7738 $uninstall = get_string('status_missing', 'core_plugin');
7739 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
7740 $uninstall = get_string('status_new', 'core_plugin');
7741 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
7742 if ($totalenabled != 1 || !$format->is_enabled()) {
7743 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7747 $settings = '';
7748 if ($format->get_settings_url()) {
7749 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7752 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7753 if ($class) {
7754 $row->attributes['class'] = $class;
7756 $table->data[] = $row;
7757 $cnt++;
7759 $return .= html_writer::table($table);
7760 return highlight($query, $return);
7765 * Special class for filter administration.
7767 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7769 class admin_page_managefilters extends admin_externalpage {
7771 * Calls parent::__construct with specific arguments
7773 public function __construct() {
7774 global $CFG;
7775 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
7779 * Searches all installed filters for specified filter
7781 * @param string $query The filter(string) to search for
7782 * @param string $query
7784 public function search($query) {
7785 global $CFG;
7786 if ($result = parent::search($query)) {
7787 return $result;
7790 $found = false;
7791 $filternames = filter_get_all_installed();
7792 foreach ($filternames as $path => $strfiltername) {
7793 if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
7794 $found = true;
7795 break;
7797 if (strpos($path, $query) !== false) {
7798 $found = true;
7799 break;
7803 if ($found) {
7804 $result = new stdClass;
7805 $result->page = $this;
7806 $result->settings = array();
7807 return array($this->name => $result);
7808 } else {
7809 return array();
7815 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
7816 * Requires a get_rank method on the plugininfo class for sorting.
7818 * @copyright 2017 Damyon Wiese
7819 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7821 abstract class admin_setting_manage_plugins extends admin_setting {
7824 * Get the admin settings section name (just a unique string)
7826 * @return string
7828 public function get_section_name() {
7829 return 'manage' . $this->get_plugin_type() . 'plugins';
7833 * Get the admin settings section title (use get_string).
7835 * @return string
7837 abstract public function get_section_title();
7840 * Get the type of plugin to manage.
7842 * @return string
7844 abstract public function get_plugin_type();
7847 * Get the name of the second column.
7849 * @return string
7851 public function get_info_column_name() {
7852 return '';
7856 * Get the type of plugin to manage.
7858 * @param plugininfo The plugin info class.
7859 * @return string
7861 abstract public function get_info_column($plugininfo);
7864 * Calls parent::__construct with specific arguments
7866 public function __construct() {
7867 $this->nosave = true;
7868 parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
7872 * Always returns true, does nothing
7874 * @return true
7876 public function get_setting() {
7877 return true;
7881 * Always returns true, does nothing
7883 * @return true
7885 public function get_defaultsetting() {
7886 return true;
7890 * Always returns '', does not write anything
7892 * @param mixed $data
7893 * @return string Always returns ''
7895 public function write_setting($data) {
7896 // Do not write any setting.
7897 return '';
7901 * Checks if $query is one of the available plugins of this type
7903 * @param string $query The string to search for
7904 * @return bool Returns true if found, false if not
7906 public function is_related($query) {
7907 if (parent::is_related($query)) {
7908 return true;
7911 $query = core_text::strtolower($query);
7912 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
7913 foreach ($plugins as $name => $plugin) {
7914 $localised = $plugin->displayname;
7915 if (strpos(core_text::strtolower($name), $query) !== false) {
7916 return true;
7918 if (strpos(core_text::strtolower($localised), $query) !== false) {
7919 return true;
7922 return false;
7926 * The URL for the management page for this plugintype.
7928 * @return moodle_url
7930 protected function get_manage_url() {
7931 return new moodle_url('/admin/updatesetting.php');
7935 * Builds the HTML to display the control.
7937 * @param string $data Unused
7938 * @param string $query
7939 * @return string
7941 public function output_html($data, $query = '') {
7942 global $CFG, $OUTPUT, $DB, $PAGE;
7944 $context = (object) [
7945 'manageurl' => new moodle_url($this->get_manage_url(), [
7946 'type' => $this->get_plugin_type(),
7947 'sesskey' => sesskey(),
7949 'infocolumnname' => $this->get_info_column_name(),
7950 'plugins' => [],
7953 $pluginmanager = core_plugin_manager::instance();
7954 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
7955 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
7956 $plugins = array_merge($enabled, $allplugins);
7957 foreach ($plugins as $key => $plugin) {
7958 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
7960 $pluginkey = (object) [
7961 'plugin' => $plugin->displayname,
7962 'enabled' => $plugin->is_enabled(),
7963 'togglelink' => '',
7964 'moveuplink' => '',
7965 'movedownlink' => '',
7966 'settingslink' => $plugin->get_settings_url(),
7967 'uninstalllink' => '',
7968 'info' => '',
7971 // Enable/Disable link.
7972 $togglelink = new moodle_url($pluginlink);
7973 if ($plugin->is_enabled()) {
7974 $toggletarget = false;
7975 $togglelink->param('action', 'disable');
7977 if (count($context->plugins)) {
7978 // This is not the first plugin.
7979 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
7982 if (count($enabled) > count($context->plugins) + 1) {
7983 // This is not the last plugin.
7984 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
7987 $pluginkey->info = $this->get_info_column($plugin);
7988 } else {
7989 $toggletarget = true;
7990 $togglelink->param('action', 'enable');
7993 $pluginkey->toggletarget = $toggletarget;
7994 $pluginkey->togglelink = $togglelink;
7996 $frankenstyle = $plugin->type . '_' . $plugin->name;
7997 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
7998 // This plugin supports uninstallation.
7999 $pluginkey->uninstalllink = $uninstalllink;
8002 if (!empty($this->get_info_column_name())) {
8003 // This plugintype has an info column.
8004 $pluginkey->info = $this->get_info_column($plugin);
8007 $context->plugins[] = $pluginkey;
8010 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8011 return highlight($query, $str);
8016 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8017 * Requires a get_rank method on the plugininfo class for sorting.
8019 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8022 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8023 public function get_section_title() {
8024 return get_string('type_fileconverter_plural', 'plugin');
8027 public function get_plugin_type() {
8028 return 'fileconverter';
8031 public function get_info_column_name() {
8032 return get_string('supportedconversions', 'plugin');
8035 public function get_info_column($plugininfo) {
8036 return $plugininfo->get_supported_conversions();
8041 * Special class for media player plugins management.
8043 * @copyright 2016 Marina Glancy
8044 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8046 class admin_setting_managemediaplayers extends admin_setting {
8048 * Calls parent::__construct with specific arguments
8050 public function __construct() {
8051 $this->nosave = true;
8052 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8056 * Always returns true, does nothing
8058 * @return true
8060 public function get_setting() {
8061 return true;
8065 * Always returns true, does nothing
8067 * @return true
8069 public function get_defaultsetting() {
8070 return true;
8074 * Always returns '', does not write anything
8076 * @param mixed $data
8077 * @return string Always returns ''
8079 public function write_setting($data) {
8080 // Do not write any setting.
8081 return '';
8085 * Checks if $query is one of the available enrol plugins
8087 * @param string $query The string to search for
8088 * @return bool Returns true if found, false if not
8090 public function is_related($query) {
8091 if (parent::is_related($query)) {
8092 return true;
8095 $query = core_text::strtolower($query);
8096 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8097 foreach ($plugins as $name => $plugin) {
8098 $localised = $plugin->displayname;
8099 if (strpos(core_text::strtolower($name), $query) !== false) {
8100 return true;
8102 if (strpos(core_text::strtolower($localised), $query) !== false) {
8103 return true;
8106 return false;
8110 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8111 * @return \core\plugininfo\media[]
8113 protected function get_sorted_plugins() {
8114 $pluginmanager = core_plugin_manager::instance();
8116 $plugins = $pluginmanager->get_plugins_of_type('media');
8117 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8119 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8120 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8122 $order = array_values($enabledplugins);
8123 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8125 $sortedplugins = array();
8126 foreach ($order as $name) {
8127 $sortedplugins[$name] = $plugins[$name];
8130 return $sortedplugins;
8134 * Builds the XHTML to display the control
8136 * @param string $data Unused
8137 * @param string $query
8138 * @return string
8140 public function output_html($data, $query='') {
8141 global $CFG, $OUTPUT, $DB, $PAGE;
8143 // Display strings.
8144 $strup = get_string('up');
8145 $strdown = get_string('down');
8146 $strsettings = get_string('settings');
8147 $strenable = get_string('enable');
8148 $strdisable = get_string('disable');
8149 $struninstall = get_string('uninstallplugin', 'core_admin');
8150 $strversion = get_string('version');
8151 $strname = get_string('name');
8152 $strsupports = get_string('supports', 'core_media');
8154 $pluginmanager = core_plugin_manager::instance();
8156 $plugins = $this->get_sorted_plugins();
8157 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8159 $return = $OUTPUT->box_start('generalbox mediaplayersui');
8161 $table = new html_table();
8162 $table->head = array($strname, $strsupports, $strversion,
8163 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8164 $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8165 'centeralign', 'centeralign', 'centeralign', 'centeralign');
8166 $table->id = 'mediaplayerplugins';
8167 $table->attributes['class'] = 'admintable generaltable';
8168 $table->data = array();
8170 // Iterate through media plugins and add to the display table.
8171 $updowncount = 1;
8172 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8173 $printed = array();
8174 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8176 $usedextensions = [];
8177 foreach ($plugins as $name => $plugin) {
8178 $url->param('media', $name);
8179 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8180 $version = $plugininfo->versiondb;
8181 $supports = $plugininfo->supports($usedextensions);
8183 // Hide/show links.
8184 $class = '';
8185 if (!$plugininfo->is_installed_and_upgraded()) {
8186 $hideshow = '';
8187 $enabled = false;
8188 $displayname = '<span class="notifyproblem">'.$name.'</span>';
8189 } else {
8190 $enabled = $plugininfo->is_enabled();
8191 if ($enabled) {
8192 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8193 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8194 } else {
8195 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8196 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8197 $class = 'dimmed_text';
8199 $displayname = $plugin->displayname;
8200 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8201 $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8204 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8205 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8206 } else {
8207 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8210 // Up/down link (only if enrol is enabled).
8211 $updown = '';
8212 if ($enabled) {
8213 if ($updowncount > 1) {
8214 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8215 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8216 } else {
8217 $updown = $spacer;
8219 if ($updowncount < count($enabledplugins)) {
8220 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8221 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8222 } else {
8223 $updown .= $spacer;
8225 ++$updowncount;
8228 $uninstall = '';
8229 $status = $plugininfo->get_status();
8230 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8231 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8233 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8234 $uninstall = get_string('status_new', 'core_plugin');
8235 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8236 $uninstall .= html_writer::link($uninstallurl, $struninstall);
8239 $settings = '';
8240 if ($plugininfo->get_settings_url()) {
8241 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8244 // Add a row to the table.
8245 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8246 if ($class) {
8247 $row->attributes['class'] = $class;
8249 $table->data[] = $row;
8251 $printed[$name] = true;
8254 $return .= html_writer::table($table);
8255 $return .= $OUTPUT->box_end();
8256 return highlight($query, $return);
8262 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8264 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
8265 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8267 class admin_setting_managecontentbankcontenttypes extends admin_setting {
8270 * Calls parent::__construct with specific arguments
8272 public function __construct() {
8273 $this->nosave = true;
8274 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8278 * Always returns true
8280 * @return true
8282 public function get_setting() {
8283 return true;
8287 * Always returns true
8289 * @return true
8291 public function get_defaultsetting() {
8292 return true;
8296 * Always returns '' and doesn't write anything
8298 * @param mixed $data string or array, must not be NULL
8299 * @return string Always returns ''
8301 public function write_setting($data) {
8302 // Do not write any setting.
8303 return '';
8307 * Search to find if Query is related to content bank plugin
8309 * @param string $query The string to search for
8310 * @return bool true for related false for not
8312 public function is_related($query) {
8313 if (parent::is_related($query)) {
8314 return true;
8316 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8317 foreach ($types as $type) {
8318 if (strpos($type->component, $query) !== false ||
8319 strpos(core_text::strtolower($type->displayname), $query) !== false) {
8320 return true;
8323 return false;
8327 * Return XHTML to display control
8329 * @param mixed $data Unused
8330 * @param string $query
8331 * @return string highlight
8333 public function output_html($data, $query='') {
8334 global $CFG, $OUTPUT;
8335 $return = '';
8337 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8338 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8339 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8341 $table = new html_table();
8342 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8343 $table->align = array('left', 'center', 'center', 'center', 'center');
8344 $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8345 $table->data = array();
8346 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8348 $totalenabled = 0;
8349 $count = 0;
8350 foreach ($types as $type) {
8351 if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8352 $totalenabled++;
8356 foreach ($types as $type) {
8357 $url = new moodle_url('/admin/contentbank.php',
8358 array('sesskey' => sesskey(), 'name' => $type->name));
8360 $class = '';
8361 $strtypename = $type->displayname;
8362 if ($type->is_enabled()) {
8363 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8364 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8365 } else {
8366 $class = 'dimmed_text';
8367 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8368 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8371 $updown = '';
8372 if ($count) {
8373 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8374 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8375 } else {
8376 $updown .= $spacer;
8378 if ($count < count($types) - 1) {
8379 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8380 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8381 } else {
8382 $updown .= $spacer;
8385 $settings = '';
8386 if ($type->get_settings_url()) {
8387 $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8390 $uninstall = '';
8391 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8392 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8395 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8396 if ($class) {
8397 $row->attributes['class'] = $class;
8399 $table->data[] = $row;
8400 $count++;
8402 $return .= html_writer::table($table);
8403 return highlight($query, $return);
8408 * Initialise admin page - this function does require login and permission
8409 * checks specified in page definition.
8411 * This function must be called on each admin page before other code.
8413 * @global moodle_page $PAGE
8415 * @param string $section name of page
8416 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8417 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8418 * added to the turn blocks editing on/off form, so this page reloads correctly.
8419 * @param string $actualurl if the actual page being viewed is not the normal one for this
8420 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8421 * @param array $options Additional options that can be specified for page setup.
8422 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8424 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8425 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8427 $PAGE->set_context(null); // hack - set context to something, by default to system context
8429 $site = get_site();
8430 require_login(null, false);
8432 if (!empty($options['pagelayout'])) {
8433 // A specific page layout has been requested.
8434 $PAGE->set_pagelayout($options['pagelayout']);
8435 } else if ($section === 'upgradesettings') {
8436 $PAGE->set_pagelayout('maintenance');
8437 } else {
8438 $PAGE->set_pagelayout('admin');
8441 $adminroot = admin_get_root(false, false); // settings not required for external pages
8442 $extpage = $adminroot->locate($section, true);
8444 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8445 // The requested section isn't in the admin tree
8446 // It could be because the user has inadequate capapbilities or because the section doesn't exist
8447 if (!has_capability('moodle/site:config', context_system::instance())) {
8448 // The requested section could depend on a different capability
8449 // but most likely the user has inadequate capabilities
8450 print_error('accessdenied', 'admin');
8451 } else {
8452 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8456 // this eliminates our need to authenticate on the actual pages
8457 if (!$extpage->check_access()) {
8458 print_error('accessdenied', 'admin');
8459 die;
8462 navigation_node::require_admin_tree();
8464 // $PAGE->set_extra_button($extrabutton); TODO
8466 if (!$actualurl) {
8467 $actualurl = $extpage->url;
8470 $PAGE->set_url($actualurl, $extraurlparams);
8471 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8472 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8475 if (empty($SITE->fullname) || empty($SITE->shortname)) {
8476 // During initial install.
8477 $strinstallation = get_string('installation', 'install');
8478 $strsettings = get_string('settings');
8479 $PAGE->navbar->add($strsettings);
8480 $PAGE->set_title($strinstallation);
8481 $PAGE->set_heading($strinstallation);
8482 $PAGE->set_cacheable(false);
8483 return;
8486 // Locate the current item on the navigation and make it active when found.
8487 $path = $extpage->path;
8488 $node = $PAGE->settingsnav;
8489 while ($node && count($path) > 0) {
8490 $node = $node->get(array_pop($path));
8492 if ($node) {
8493 $node->make_active();
8496 // Normal case.
8497 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8498 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8499 $USER->editing = $adminediting;
8502 $visiblepathtosection = array_reverse($extpage->visiblepath);
8504 if ($PAGE->user_allowed_editing()) {
8505 if ($PAGE->user_is_editing()) {
8506 $caption = get_string('blockseditoff');
8507 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8508 } else {
8509 $caption = get_string('blocksediton');
8510 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8512 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8515 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
8516 $PAGE->set_heading($SITE->fullname);
8518 // prevent caching in nav block
8519 $PAGE->navigation->clear_cache();
8523 * Returns the reference to admin tree root
8525 * @return object admin_root object
8527 function admin_get_root($reload=false, $requirefulltree=true) {
8528 global $CFG, $DB, $OUTPUT, $ADMIN;
8530 if (is_null($ADMIN)) {
8531 // create the admin tree!
8532 $ADMIN = new admin_root($requirefulltree);
8535 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8536 $ADMIN->purge_children($requirefulltree);
8539 if (!$ADMIN->loaded) {
8540 // we process this file first to create categories first and in correct order
8541 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8543 // now we process all other files in admin/settings to build the admin tree
8544 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8545 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8546 continue;
8548 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8549 // plugins are loaded last - they may insert pages anywhere
8550 continue;
8552 require($file);
8554 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8556 $ADMIN->loaded = true;
8559 return $ADMIN;
8562 /// settings utility functions
8565 * This function applies default settings.
8566 * Because setting the defaults of some settings can enable other settings,
8567 * this function is called recursively until no more new settings are found.
8569 * @param object $node, NULL means complete tree, null by default
8570 * @param bool $unconditional if true overrides all values with defaults, true by default
8571 * @param array $admindefaultsettings default admin settings to apply. Used recursively
8572 * @param array $settingsoutput The names and values of the changed settings. Used recursively
8573 * @return array $settingsoutput The names and values of the changed settings
8575 function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
8576 $counter = 0;
8578 if (is_null($node)) {
8579 core_plugin_manager::reset_caches();
8580 $node = admin_get_root(true, true);
8581 $counter = count($settingsoutput);
8584 if ($node instanceof admin_category) {
8585 $entries = array_keys($node->children);
8586 foreach ($entries as $entry) {
8587 $settingsoutput = admin_apply_default_settings(
8588 $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
8592 } else if ($node instanceof admin_settingpage) {
8593 foreach ($node->settings as $setting) {
8594 if (!$unconditional && !is_null($setting->get_setting())) {
8595 // Do not override existing defaults.
8596 continue;
8598 $defaultsetting = $setting->get_defaultsetting();
8599 if (is_null($defaultsetting)) {
8600 // No value yet - default maybe applied after admin user creation or in upgradesettings.
8601 continue;
8604 $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
8606 if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
8607 $admindefaultsettings[$settingname] = $settingname;
8608 $settingsoutput[$settingname] = $defaultsetting;
8610 // Set the default for this setting.
8611 $setting->write_setting($defaultsetting);
8612 $setting->write_setting_flags(null);
8613 } else {
8614 unset($admindefaultsettings[$settingname]); // Remove processed settings.
8619 // Call this function recursively until all settings are processed.
8620 if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
8621 $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
8623 // Just in case somebody modifies the list of active plugins directly.
8624 core_plugin_manager::reset_caches();
8626 return $settingsoutput;
8630 * Store changed settings, this function updates the errors variable in $ADMIN
8632 * @param object $formdata from form
8633 * @return int number of changed settings
8635 function admin_write_settings($formdata) {
8636 global $CFG, $SITE, $DB;
8638 $olddbsessions = !empty($CFG->dbsessions);
8639 $formdata = (array)$formdata;
8641 $data = array();
8642 foreach ($formdata as $fullname=>$value) {
8643 if (strpos($fullname, 's_') !== 0) {
8644 continue; // not a config value
8646 $data[$fullname] = $value;
8649 $adminroot = admin_get_root();
8650 $settings = admin_find_write_settings($adminroot, $data);
8652 $count = 0;
8653 foreach ($settings as $fullname=>$setting) {
8654 /** @var $setting admin_setting */
8655 $original = $setting->get_setting();
8656 $error = $setting->write_setting($data[$fullname]);
8657 if ($error !== '') {
8658 $adminroot->errors[$fullname] = new stdClass();
8659 $adminroot->errors[$fullname]->data = $data[$fullname];
8660 $adminroot->errors[$fullname]->id = $setting->get_id();
8661 $adminroot->errors[$fullname]->error = $error;
8662 } else {
8663 $setting->write_setting_flags($data);
8665 if ($setting->post_write_settings($original)) {
8666 $count++;
8670 if ($olddbsessions != !empty($CFG->dbsessions)) {
8671 require_logout();
8674 // Now update $SITE - just update the fields, in case other people have a
8675 // a reference to it (e.g. $PAGE, $COURSE).
8676 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
8677 foreach (get_object_vars($newsite) as $field => $value) {
8678 $SITE->$field = $value;
8681 // now reload all settings - some of them might depend on the changed
8682 admin_get_root(true);
8683 return $count;
8687 * Internal recursive function - finds all settings from submitted form
8689 * @param object $node Instance of admin_category, or admin_settingpage
8690 * @param array $data
8691 * @return array
8693 function admin_find_write_settings($node, $data) {
8694 $return = array();
8696 if (empty($data)) {
8697 return $return;
8700 if ($node instanceof admin_category) {
8701 if ($node->check_access()) {
8702 $entries = array_keys($node->children);
8703 foreach ($entries as $entry) {
8704 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
8708 } else if ($node instanceof admin_settingpage) {
8709 if ($node->check_access()) {
8710 foreach ($node->settings as $setting) {
8711 $fullname = $setting->get_full_name();
8712 if (array_key_exists($fullname, $data)) {
8713 $return[$fullname] = $setting;
8720 return $return;
8724 * Internal function - prints the search results
8726 * @param string $query String to search for
8727 * @return string empty or XHTML
8729 function admin_search_settings_html($query) {
8730 global $CFG, $OUTPUT, $PAGE;
8732 if (core_text::strlen($query) < 2) {
8733 return '';
8735 $query = core_text::strtolower($query);
8737 $adminroot = admin_get_root();
8738 $findings = $adminroot->search($query);
8739 $savebutton = false;
8741 $tpldata = (object) [
8742 'actionurl' => $PAGE->url->out(false),
8743 'results' => [],
8744 'sesskey' => sesskey(),
8747 foreach ($findings as $found) {
8748 $page = $found->page;
8749 $settings = $found->settings;
8750 if ($page->is_hidden()) {
8751 // hidden pages are not displayed in search results
8752 continue;
8755 $heading = highlight($query, $page->visiblename);
8756 $headingurl = null;
8757 if ($page instanceof admin_externalpage) {
8758 $headingurl = new moodle_url($page->url);
8759 } else if ($page instanceof admin_settingpage) {
8760 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
8761 } else {
8762 continue;
8765 // Locate the page in the admin root and populate its visiblepath attribute.
8766 $path = array();
8767 $located = $adminroot->locate($page->name, true);
8768 if ($located) {
8769 foreach ($located->visiblepath as $pathitem) {
8770 array_unshift($path, (string) $pathitem);
8774 $sectionsettings = [];
8775 if (!empty($settings)) {
8776 foreach ($settings as $setting) {
8777 if (empty($setting->nosave)) {
8778 $savebutton = true;
8780 $fullname = $setting->get_full_name();
8781 if (array_key_exists($fullname, $adminroot->errors)) {
8782 $data = $adminroot->errors[$fullname]->data;
8783 } else {
8784 $data = $setting->get_setting();
8785 // do not use defaults if settings not available - upgradesettings handles the defaults!
8787 $sectionsettings[] = $setting->output_html($data, $query);
8791 $tpldata->results[] = (object) [
8792 'title' => $heading,
8793 'path' => $path,
8794 'url' => $headingurl->out(false),
8795 'settings' => $sectionsettings
8799 $tpldata->showsave = $savebutton;
8800 $tpldata->hasresults = !empty($tpldata->results);
8802 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
8806 * Internal function - returns arrays of html pages with uninitialised settings
8808 * @param object $node Instance of admin_category or admin_settingpage
8809 * @return array
8811 function admin_output_new_settings_by_page($node) {
8812 global $OUTPUT;
8813 $return = array();
8815 if ($node instanceof admin_category) {
8816 $entries = array_keys($node->children);
8817 foreach ($entries as $entry) {
8818 $return += admin_output_new_settings_by_page($node->children[$entry]);
8821 } else if ($node instanceof admin_settingpage) {
8822 $newsettings = array();
8823 foreach ($node->settings as $setting) {
8824 if (is_null($setting->get_setting())) {
8825 $newsettings[] = $setting;
8828 if (count($newsettings) > 0) {
8829 $adminroot = admin_get_root();
8830 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
8831 $page .= '<fieldset class="adminsettings">'."\n";
8832 foreach ($newsettings as $setting) {
8833 $fullname = $setting->get_full_name();
8834 if (array_key_exists($fullname, $adminroot->errors)) {
8835 $data = $adminroot->errors[$fullname]->data;
8836 } else {
8837 $data = $setting->get_setting();
8838 if (is_null($data)) {
8839 $data = $setting->get_defaultsetting();
8842 $page .= '<div class="clearer"><!-- --></div>'."\n";
8843 $page .= $setting->output_html($data);
8845 $page .= '</fieldset>';
8846 $return[$node->name] = $page;
8850 return $return;
8854 * Format admin settings
8856 * @param object $setting
8857 * @param string $title label element
8858 * @param string $form form fragment, html code - not highlighted automatically
8859 * @param string $description
8860 * @param mixed $label link label to id, true by default or string being the label to connect it to
8861 * @param string $warning warning text
8862 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
8863 * @param string $query search query to be highlighted
8864 * @return string XHTML
8866 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
8867 global $CFG, $OUTPUT;
8869 $context = (object) [
8870 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
8871 'fullname' => $setting->get_full_name(),
8874 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
8875 if ($label === true) {
8876 $context->labelfor = $setting->get_id();
8877 } else if ($label === false) {
8878 $context->labelfor = '';
8879 } else {
8880 $context->labelfor = $label;
8883 $form .= $setting->output_setting_flags();
8885 $context->warning = $warning;
8886 $context->override = '';
8887 if (empty($setting->plugin)) {
8888 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
8889 $context->override = get_string('configoverride', 'admin');
8891 } else {
8892 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
8893 $context->override = get_string('configoverride', 'admin');
8897 $defaults = array();
8898 if (!is_null($defaultinfo)) {
8899 if ($defaultinfo === '') {
8900 $defaultinfo = get_string('emptysettingvalue', 'admin');
8902 $defaults[] = $defaultinfo;
8905 $context->default = null;
8906 $setting->get_setting_flag_defaults($defaults);
8907 if (!empty($defaults)) {
8908 $defaultinfo = implode(', ', $defaults);
8909 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
8910 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
8914 $context->error = '';
8915 $adminroot = admin_get_root();
8916 if (array_key_exists($context->fullname, $adminroot->errors)) {
8917 $context->error = $adminroot->errors[$context->fullname]->error;
8920 if ($dependenton = $setting->get_dependent_on()) {
8921 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
8924 $context->id = 'admin-' . $setting->name;
8925 $context->title = highlightfast($query, $title);
8926 $context->name = highlightfast($query, $context->name);
8927 $context->description = highlight($query, markdown_to_html($description));
8928 $context->element = $form;
8929 $context->forceltr = $setting->get_force_ltr();
8931 return $OUTPUT->render_from_template('core_admin/setting', $context);
8935 * Based on find_new_settings{@link ()} in upgradesettings.php
8936 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
8938 * @param object $node Instance of admin_category, or admin_settingpage
8939 * @return boolean true if any settings haven't been initialised, false if they all have
8941 function any_new_admin_settings($node) {
8943 if ($node instanceof admin_category) {
8944 $entries = array_keys($node->children);
8945 foreach ($entries as $entry) {
8946 if (any_new_admin_settings($node->children[$entry])) {
8947 return true;
8951 } else if ($node instanceof admin_settingpage) {
8952 foreach ($node->settings as $setting) {
8953 if ($setting->get_setting() === NULL) {
8954 return true;
8959 return false;
8963 * Moved from admin/replace.php so that we can use this in cron
8965 * @param string $search string to look for
8966 * @param string $replace string to replace
8967 * @return bool success or fail
8969 function db_replace($search, $replace) {
8970 global $DB, $CFG, $OUTPUT;
8972 // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
8973 $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log',
8974 'filter_config', 'sessions', 'events_queue', 'repository_instance_config',
8975 'block_instances', '');
8977 // Turn off time limits, sometimes upgrades can be slow.
8978 core_php_time_limit::raise();
8980 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
8981 return false;
8983 foreach ($tables as $table) {
8985 if (in_array($table, $skiptables)) { // Don't process these
8986 continue;
8989 if ($columns = $DB->get_columns($table)) {
8990 $DB->set_debug(true);
8991 foreach ($columns as $column) {
8992 $DB->replace_all_text($table, $column, $search, $replace);
8994 $DB->set_debug(false);
8998 // delete modinfo caches
8999 rebuild_course_cache(0, true);
9001 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9002 $blocks = core_component::get_plugin_list('block');
9003 foreach ($blocks as $blockname=>$fullblock) {
9004 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
9005 continue;
9008 if (!is_readable($fullblock.'/lib.php')) {
9009 continue;
9012 $function = 'block_'.$blockname.'_global_db_replace';
9013 include_once($fullblock.'/lib.php');
9014 if (!function_exists($function)) {
9015 continue;
9018 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9019 $function($search, $replace);
9020 echo $OUTPUT->notification("...finished", 'notifysuccess');
9023 // Trigger an event.
9024 $eventargs = [
9025 'context' => context_system::instance(),
9026 'other' => [
9027 'search' => $search,
9028 'replace' => $replace
9031 $event = \core\event\database_text_field_content_replaced::create($eventargs);
9032 $event->trigger();
9034 purge_all_caches();
9036 return true;
9040 * Manage repository settings
9042 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9044 class admin_setting_managerepository extends admin_setting {
9045 /** @var string */
9046 private $baseurl;
9049 * calls parent::__construct with specific arguments
9051 public function __construct() {
9052 global $CFG;
9053 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9054 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9058 * Always returns true, does nothing
9060 * @return true
9062 public function get_setting() {
9063 return true;
9067 * Always returns true does nothing
9069 * @return true
9071 public function get_defaultsetting() {
9072 return true;
9076 * Always returns s_managerepository
9078 * @return string Always return 's_managerepository'
9080 public function get_full_name() {
9081 return 's_managerepository';
9085 * Always returns '' doesn't do anything
9087 public function write_setting($data) {
9088 $url = $this->baseurl . '&amp;new=' . $data;
9089 return '';
9090 // TODO
9091 // Should not use redirect and exit here
9092 // Find a better way to do this.
9093 // redirect($url);
9094 // exit;
9098 * Searches repository plugins for one that matches $query
9100 * @param string $query The string to search for
9101 * @return bool true if found, false if not
9103 public function is_related($query) {
9104 if (parent::is_related($query)) {
9105 return true;
9108 $repositories= core_component::get_plugin_list('repository');
9109 foreach ($repositories as $p => $dir) {
9110 if (strpos($p, $query) !== false) {
9111 return true;
9114 foreach (repository::get_types() as $instance) {
9115 $title = $instance->get_typename();
9116 if (strpos(core_text::strtolower($title), $query) !== false) {
9117 return true;
9120 return false;
9124 * Helper function that generates a moodle_url object
9125 * relevant to the repository
9128 function repository_action_url($repository) {
9129 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9133 * Builds XHTML to display the control
9135 * @param string $data Unused
9136 * @param string $query
9137 * @return string XHTML
9139 public function output_html($data, $query='') {
9140 global $CFG, $USER, $OUTPUT;
9142 // Get strings that are used
9143 $strshow = get_string('on', 'repository');
9144 $strhide = get_string('off', 'repository');
9145 $strdelete = get_string('disabled', 'repository');
9147 $actionchoicesforexisting = array(
9148 'show' => $strshow,
9149 'hide' => $strhide,
9150 'delete' => $strdelete
9153 $actionchoicesfornew = array(
9154 'newon' => $strshow,
9155 'newoff' => $strhide,
9156 'delete' => $strdelete
9159 $return = '';
9160 $return .= $OUTPUT->box_start('generalbox');
9162 // Set strings that are used multiple times
9163 $settingsstr = get_string('settings');
9164 $disablestr = get_string('disable');
9166 // Table to list plug-ins
9167 $table = new html_table();
9168 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9169 $table->align = array('left', 'center', 'center', 'center', 'center');
9170 $table->data = array();
9172 // Get list of used plug-ins
9173 $repositorytypes = repository::get_types();
9174 if (!empty($repositorytypes)) {
9175 // Array to store plugins being used
9176 $alreadyplugins = array();
9177 $totalrepositorytypes = count($repositorytypes);
9178 $updowncount = 1;
9179 foreach ($repositorytypes as $i) {
9180 $settings = '';
9181 $typename = $i->get_typename();
9182 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9183 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9184 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9186 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9187 // Calculate number of instances in order to display them for the Moodle administrator
9188 if (!empty($instanceoptionnames)) {
9189 $params = array();
9190 $params['context'] = array(context_system::instance());
9191 $params['onlyvisible'] = false;
9192 $params['type'] = $typename;
9193 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9194 // site instances
9195 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9196 $params['context'] = array();
9197 $instances = repository::static_function($typename, 'get_instances', $params);
9198 $courseinstances = array();
9199 $userinstances = array();
9201 foreach ($instances as $instance) {
9202 $repocontext = context::instance_by_id($instance->instance->contextid);
9203 if ($repocontext->contextlevel == CONTEXT_COURSE) {
9204 $courseinstances[] = $instance;
9205 } else if ($repocontext->contextlevel == CONTEXT_USER) {
9206 $userinstances[] = $instance;
9209 // course instances
9210 $instancenumber = count($courseinstances);
9211 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9213 // user private instances
9214 $instancenumber = count($userinstances);
9215 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9216 } else {
9217 $admininstancenumbertext = "";
9218 $courseinstancenumbertext = "";
9219 $userinstancenumbertext = "";
9222 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9224 $settings .= $OUTPUT->container_start('mdl-left');
9225 $settings .= '<br/>';
9226 $settings .= $admininstancenumbertext;
9227 $settings .= '<br/>';
9228 $settings .= $courseinstancenumbertext;
9229 $settings .= '<br/>';
9230 $settings .= $userinstancenumbertext;
9231 $settings .= $OUTPUT->container_end();
9233 // Get the current visibility
9234 if ($i->get_visible()) {
9235 $currentaction = 'show';
9236 } else {
9237 $currentaction = 'hide';
9240 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9242 // Display up/down link
9243 $updown = '';
9244 // Should be done with CSS instead.
9245 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9247 if ($updowncount > 1) {
9248 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9249 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9251 else {
9252 $updown .= $spacer;
9254 if ($updowncount < $totalrepositorytypes) {
9255 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9256 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9258 else {
9259 $updown .= $spacer;
9262 $updowncount++;
9264 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9266 if (!in_array($typename, $alreadyplugins)) {
9267 $alreadyplugins[] = $typename;
9272 // Get all the plugins that exist on disk
9273 $plugins = core_component::get_plugin_list('repository');
9274 if (!empty($plugins)) {
9275 foreach ($plugins as $plugin => $dir) {
9276 // Check that it has not already been listed
9277 if (!in_array($plugin, $alreadyplugins)) {
9278 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9279 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9284 $return .= html_writer::table($table);
9285 $return .= $OUTPUT->box_end();
9286 return highlight($query, $return);
9291 * Special checkbox for enable mobile web service
9292 * If enable then we store the service id of the mobile service into config table
9293 * If disable then we unstore the service id from the config table
9295 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9297 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9298 private $restuse;
9301 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9303 * @return boolean
9305 private function is_protocol_cap_allowed() {
9306 global $DB, $CFG;
9308 // If the $this->restuse variable is not set, it needs to be set.
9309 if (empty($this->restuse) and $this->restuse!==false) {
9310 $params = array();
9311 $params['permission'] = CAP_ALLOW;
9312 $params['roleid'] = $CFG->defaultuserroleid;
9313 $params['capability'] = 'webservice/rest:use';
9314 $this->restuse = $DB->record_exists('role_capabilities', $params);
9317 return $this->restuse;
9321 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9322 * @param type $status true to allow, false to not set
9324 private function set_protocol_cap($status) {
9325 global $CFG;
9326 if ($status and !$this->is_protocol_cap_allowed()) {
9327 //need to allow the cap
9328 $permission = CAP_ALLOW;
9329 $assign = true;
9330 } else if (!$status and $this->is_protocol_cap_allowed()){
9331 //need to disallow the cap
9332 $permission = CAP_INHERIT;
9333 $assign = true;
9335 if (!empty($assign)) {
9336 $systemcontext = context_system::instance();
9337 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9342 * Builds XHTML to display the control.
9343 * The main purpose of this overloading is to display a warning when https
9344 * is not supported by the server
9345 * @param string $data Unused
9346 * @param string $query
9347 * @return string XHTML
9349 public function output_html($data, $query='') {
9350 global $OUTPUT;
9351 $html = parent::output_html($data, $query);
9353 if ((string)$data === $this->yes) {
9354 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9355 foreach ($notifications as $notification) {
9356 $message = get_string($notification[0], $notification[1]);
9357 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9361 return $html;
9365 * Retrieves the current setting using the objects name
9367 * @return string
9369 public function get_setting() {
9370 global $CFG;
9372 // First check if is not set.
9373 $result = $this->config_read($this->name);
9374 if (is_null($result)) {
9375 return null;
9378 // For install cli script, $CFG->defaultuserroleid is not set so return 0
9379 // Or if web services aren't enabled this can't be,
9380 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9381 return 0;
9384 require_once($CFG->dirroot . '/webservice/lib.php');
9385 $webservicemanager = new webservice();
9386 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9387 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9388 return $result;
9389 } else {
9390 return 0;
9395 * Save the selected setting
9397 * @param string $data The selected site
9398 * @return string empty string or error message
9400 public function write_setting($data) {
9401 global $DB, $CFG;
9403 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9404 if (empty($CFG->defaultuserroleid)) {
9405 return '';
9408 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9410 require_once($CFG->dirroot . '/webservice/lib.php');
9411 $webservicemanager = new webservice();
9413 $updateprotocol = false;
9414 if ((string)$data === $this->yes) {
9415 //code run when enable mobile web service
9416 //enable web service systeme if necessary
9417 set_config('enablewebservices', true);
9419 //enable mobile service
9420 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9421 $mobileservice->enabled = 1;
9422 $webservicemanager->update_external_service($mobileservice);
9424 // Enable REST server.
9425 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9427 if (!in_array('rest', $activeprotocols)) {
9428 $activeprotocols[] = 'rest';
9429 $updateprotocol = true;
9432 if ($updateprotocol) {
9433 set_config('webserviceprotocols', implode(',', $activeprotocols));
9436 // Allow rest:use capability for authenticated user.
9437 $this->set_protocol_cap(true);
9439 } else {
9440 //disable web service system if no other services are enabled
9441 $otherenabledservices = $DB->get_records_select('external_services',
9442 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
9443 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
9444 if (empty($otherenabledservices)) {
9445 set_config('enablewebservices', false);
9447 // Also disable REST server.
9448 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9450 $protocolkey = array_search('rest', $activeprotocols);
9451 if ($protocolkey !== false) {
9452 unset($activeprotocols[$protocolkey]);
9453 $updateprotocol = true;
9456 if ($updateprotocol) {
9457 set_config('webserviceprotocols', implode(',', $activeprotocols));
9460 // Disallow rest:use capability for authenticated user.
9461 $this->set_protocol_cap(false);
9464 //disable the mobile service
9465 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9466 $mobileservice->enabled = 0;
9467 $webservicemanager->update_external_service($mobileservice);
9470 return (parent::write_setting($data));
9475 * Special class for management of external services
9477 * @author Petr Skoda (skodak)
9479 class admin_setting_manageexternalservices extends admin_setting {
9481 * Calls parent::__construct with specific arguments
9483 public function __construct() {
9484 $this->nosave = true;
9485 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9489 * Always returns true, does nothing
9491 * @return true
9493 public function get_setting() {
9494 return true;
9498 * Always returns true, does nothing
9500 * @return true
9502 public function get_defaultsetting() {
9503 return true;
9507 * Always returns '', does not write anything
9509 * @return string Always returns ''
9511 public function write_setting($data) {
9512 // do not write any setting
9513 return '';
9517 * Checks if $query is one of the available external services
9519 * @param string $query The string to search for
9520 * @return bool Returns true if found, false if not
9522 public function is_related($query) {
9523 global $DB;
9525 if (parent::is_related($query)) {
9526 return true;
9529 $services = $DB->get_records('external_services', array(), 'id, name');
9530 foreach ($services as $service) {
9531 if (strpos(core_text::strtolower($service->name), $query) !== false) {
9532 return true;
9535 return false;
9539 * Builds the XHTML to display the control
9541 * @param string $data Unused
9542 * @param string $query
9543 * @return string
9545 public function output_html($data, $query='') {
9546 global $CFG, $OUTPUT, $DB;
9548 // display strings
9549 $stradministration = get_string('administration');
9550 $stredit = get_string('edit');
9551 $strservice = get_string('externalservice', 'webservice');
9552 $strdelete = get_string('delete');
9553 $strplugin = get_string('plugin', 'admin');
9554 $stradd = get_string('add');
9555 $strfunctions = get_string('functions', 'webservice');
9556 $strusers = get_string('users');
9557 $strserviceusers = get_string('serviceusers', 'webservice');
9559 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9560 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9561 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9563 // built in services
9564 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9565 $return = "";
9566 if (!empty($services)) {
9567 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9571 $table = new html_table();
9572 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9573 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9574 $table->id = 'builtinservices';
9575 $table->attributes['class'] = 'admintable externalservices generaltable';
9576 $table->data = array();
9578 // iterate through auth plugins and add to the display table
9579 foreach ($services as $service) {
9580 $name = $service->name;
9582 // hide/show link
9583 if ($service->enabled) {
9584 $displayname = "<span>$name</span>";
9585 } else {
9586 $displayname = "<span class=\"dimmed_text\">$name</span>";
9589 $plugin = $service->component;
9591 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9593 if ($service->restrictedusers) {
9594 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9595 } else {
9596 $users = get_string('allusers', 'webservice');
9599 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9601 // add a row to the table
9602 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9604 $return .= html_writer::table($table);
9607 // Custom services
9608 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9609 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9611 $table = new html_table();
9612 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9613 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9614 $table->id = 'customservices';
9615 $table->attributes['class'] = 'admintable externalservices generaltable';
9616 $table->data = array();
9618 // iterate through auth plugins and add to the display table
9619 foreach ($services as $service) {
9620 $name = $service->name;
9622 // hide/show link
9623 if ($service->enabled) {
9624 $displayname = "<span>$name</span>";
9625 } else {
9626 $displayname = "<span class=\"dimmed_text\">$name</span>";
9629 // delete link
9630 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9632 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9634 if ($service->restrictedusers) {
9635 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9636 } else {
9637 $users = get_string('allusers', 'webservice');
9640 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9642 // add a row to the table
9643 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9645 // add new custom service option
9646 $return .= html_writer::table($table);
9648 $return .= '<br />';
9649 // add a token to the table
9650 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9652 return highlight($query, $return);
9657 * Special class for overview of external services
9659 * @author Jerome Mouneyrac
9661 class admin_setting_webservicesoverview extends admin_setting {
9664 * Calls parent::__construct with specific arguments
9666 public function __construct() {
9667 $this->nosave = true;
9668 parent::__construct('webservicesoverviewui',
9669 get_string('webservicesoverview', 'webservice'), '', '');
9673 * Always returns true, does nothing
9675 * @return true
9677 public function get_setting() {
9678 return true;
9682 * Always returns true, does nothing
9684 * @return true
9686 public function get_defaultsetting() {
9687 return true;
9691 * Always returns '', does not write anything
9693 * @return string Always returns ''
9695 public function write_setting($data) {
9696 // do not write any setting
9697 return '';
9701 * Builds the XHTML to display the control
9703 * @param string $data Unused
9704 * @param string $query
9705 * @return string
9707 public function output_html($data, $query='') {
9708 global $CFG, $OUTPUT;
9710 $return = "";
9711 $brtag = html_writer::empty_tag('br');
9713 /// One system controlling Moodle with Token
9714 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
9715 $table = new html_table();
9716 $table->head = array(get_string('step', 'webservice'), get_string('status'),
9717 get_string('description'));
9718 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9719 $table->id = 'onesystemcontrol';
9720 $table->attributes['class'] = 'admintable wsoverview generaltable';
9721 $table->data = array();
9723 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
9724 . $brtag . $brtag;
9726 /// 1. Enable Web Services
9727 $row = array();
9728 $url = new moodle_url("/admin/search.php?query=enablewebservices");
9729 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
9730 array('href' => $url));
9731 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
9732 if ($CFG->enablewebservices) {
9733 $status = get_string('yes');
9735 $row[1] = $status;
9736 $row[2] = get_string('enablewsdescription', 'webservice');
9737 $table->data[] = $row;
9739 /// 2. Enable protocols
9740 $row = array();
9741 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
9742 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
9743 array('href' => $url));
9744 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
9745 //retrieve activated protocol
9746 $active_protocols = empty($CFG->webserviceprotocols) ?
9747 array() : explode(',', $CFG->webserviceprotocols);
9748 if (!empty($active_protocols)) {
9749 $status = "";
9750 foreach ($active_protocols as $protocol) {
9751 $status .= $protocol . $brtag;
9754 $row[1] = $status;
9755 $row[2] = get_string('enableprotocolsdescription', 'webservice');
9756 $table->data[] = $row;
9758 /// 3. Create user account
9759 $row = array();
9760 $url = new moodle_url("/user/editadvanced.php?id=-1");
9761 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
9762 array('href' => $url));
9763 $row[1] = "";
9764 $row[2] = get_string('createuserdescription', 'webservice');
9765 $table->data[] = $row;
9767 /// 4. Add capability to users
9768 $row = array();
9769 $url = new moodle_url("/admin/roles/check.php?contextid=1");
9770 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
9771 array('href' => $url));
9772 $row[1] = "";
9773 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
9774 $table->data[] = $row;
9776 /// 5. Select a web service
9777 $row = array();
9778 $url = new moodle_url("/admin/settings.php?section=externalservices");
9779 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
9780 array('href' => $url));
9781 $row[1] = "";
9782 $row[2] = get_string('createservicedescription', 'webservice');
9783 $table->data[] = $row;
9785 /// 6. Add functions
9786 $row = array();
9787 $url = new moodle_url("/admin/settings.php?section=externalservices");
9788 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
9789 array('href' => $url));
9790 $row[1] = "";
9791 $row[2] = get_string('addfunctionsdescription', 'webservice');
9792 $table->data[] = $row;
9794 /// 7. Add the specific user
9795 $row = array();
9796 $url = new moodle_url("/admin/settings.php?section=externalservices");
9797 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
9798 array('href' => $url));
9799 $row[1] = "";
9800 $row[2] = get_string('selectspecificuserdescription', 'webservice');
9801 $table->data[] = $row;
9803 /// 8. Create token for the specific user
9804 $row = array();
9805 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
9806 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
9807 array('href' => $url));
9808 $row[1] = "";
9809 $row[2] = get_string('createtokenforuserdescription', 'webservice');
9810 $table->data[] = $row;
9812 /// 9. Enable the documentation
9813 $row = array();
9814 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
9815 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
9816 array('href' => $url));
9817 $status = '<span class="warning">' . get_string('no') . '</span>';
9818 if ($CFG->enablewsdocumentation) {
9819 $status = get_string('yes');
9821 $row[1] = $status;
9822 $row[2] = get_string('enabledocumentationdescription', 'webservice');
9823 $table->data[] = $row;
9825 /// 10. Test the service
9826 $row = array();
9827 $url = new moodle_url("/admin/webservice/testclient.php");
9828 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
9829 array('href' => $url));
9830 $row[1] = "";
9831 $row[2] = get_string('testwithtestclientdescription', 'webservice');
9832 $table->data[] = $row;
9834 $return .= html_writer::table($table);
9836 /// Users as clients with token
9837 $return .= $brtag . $brtag . $brtag;
9838 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
9839 $table = new html_table();
9840 $table->head = array(get_string('step', 'webservice'), get_string('status'),
9841 get_string('description'));
9842 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9843 $table->id = 'userasclients';
9844 $table->attributes['class'] = 'admintable wsoverview generaltable';
9845 $table->data = array();
9847 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
9848 $brtag . $brtag;
9850 /// 1. Enable Web Services
9851 $row = array();
9852 $url = new moodle_url("/admin/search.php?query=enablewebservices");
9853 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
9854 array('href' => $url));
9855 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
9856 if ($CFG->enablewebservices) {
9857 $status = get_string('yes');
9859 $row[1] = $status;
9860 $row[2] = get_string('enablewsdescription', 'webservice');
9861 $table->data[] = $row;
9863 /// 2. Enable protocols
9864 $row = array();
9865 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
9866 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
9867 array('href' => $url));
9868 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
9869 //retrieve activated protocol
9870 $active_protocols = empty($CFG->webserviceprotocols) ?
9871 array() : explode(',', $CFG->webserviceprotocols);
9872 if (!empty($active_protocols)) {
9873 $status = "";
9874 foreach ($active_protocols as $protocol) {
9875 $status .= $protocol . $brtag;
9878 $row[1] = $status;
9879 $row[2] = get_string('enableprotocolsdescription', 'webservice');
9880 $table->data[] = $row;
9883 /// 3. Select a web service
9884 $row = array();
9885 $url = new moodle_url("/admin/settings.php?section=externalservices");
9886 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
9887 array('href' => $url));
9888 $row[1] = "";
9889 $row[2] = get_string('createserviceforusersdescription', 'webservice');
9890 $table->data[] = $row;
9892 /// 4. Add functions
9893 $row = array();
9894 $url = new moodle_url("/admin/settings.php?section=externalservices");
9895 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
9896 array('href' => $url));
9897 $row[1] = "";
9898 $row[2] = get_string('addfunctionsdescription', 'webservice');
9899 $table->data[] = $row;
9901 /// 5. Add capability to users
9902 $row = array();
9903 $url = new moodle_url("/admin/roles/check.php?contextid=1");
9904 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
9905 array('href' => $url));
9906 $row[1] = "";
9907 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
9908 $table->data[] = $row;
9910 /// 6. Test the service
9911 $row = array();
9912 $url = new moodle_url("/admin/webservice/testclient.php");
9913 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
9914 array('href' => $url));
9915 $row[1] = "";
9916 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
9917 $table->data[] = $row;
9919 $return .= html_writer::table($table);
9921 return highlight($query, $return);
9928 * Special class for web service protocol administration.
9930 * @author Petr Skoda (skodak)
9932 class admin_setting_managewebserviceprotocols extends admin_setting {
9935 * Calls parent::__construct with specific arguments
9937 public function __construct() {
9938 $this->nosave = true;
9939 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
9943 * Always returns true, does nothing
9945 * @return true
9947 public function get_setting() {
9948 return true;
9952 * Always returns true, does nothing
9954 * @return true
9956 public function get_defaultsetting() {
9957 return true;
9961 * Always returns '', does not write anything
9963 * @return string Always returns ''
9965 public function write_setting($data) {
9966 // do not write any setting
9967 return '';
9971 * Checks if $query is one of the available webservices
9973 * @param string $query The string to search for
9974 * @return bool Returns true if found, false if not
9976 public function is_related($query) {
9977 if (parent::is_related($query)) {
9978 return true;
9981 $protocols = core_component::get_plugin_list('webservice');
9982 foreach ($protocols as $protocol=>$location) {
9983 if (strpos($protocol, $query) !== false) {
9984 return true;
9986 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
9987 if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
9988 return true;
9991 return false;
9995 * Builds the XHTML to display the control
9997 * @param string $data Unused
9998 * @param string $query
9999 * @return string
10001 public function output_html($data, $query='') {
10002 global $CFG, $OUTPUT;
10004 // display strings
10005 $stradministration = get_string('administration');
10006 $strsettings = get_string('settings');
10007 $stredit = get_string('edit');
10008 $strprotocol = get_string('protocol', 'webservice');
10009 $strenable = get_string('enable');
10010 $strdisable = get_string('disable');
10011 $strversion = get_string('version');
10013 $protocols_available = core_component::get_plugin_list('webservice');
10014 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10015 ksort($protocols_available);
10017 foreach ($active_protocols as $key=>$protocol) {
10018 if (empty($protocols_available[$protocol])) {
10019 unset($active_protocols[$key]);
10023 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10024 $return .= $OUTPUT->box_start('generalbox webservicesui');
10026 $table = new html_table();
10027 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
10028 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10029 $table->id = 'webserviceprotocols';
10030 $table->attributes['class'] = 'admintable generaltable';
10031 $table->data = array();
10033 // iterate through auth plugins and add to the display table
10034 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10035 foreach ($protocols_available as $protocol => $location) {
10036 $name = get_string('pluginname', 'webservice_'.$protocol);
10038 $plugin = new stdClass();
10039 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10040 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10042 $version = isset($plugin->version) ? $plugin->version : '';
10044 // hide/show link
10045 if (in_array($protocol, $active_protocols)) {
10046 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10047 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10048 $displayname = "<span>$name</span>";
10049 } else {
10050 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10051 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10052 $displayname = "<span class=\"dimmed_text\">$name</span>";
10055 // settings link
10056 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10057 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10058 } else {
10059 $settings = '';
10062 // add a row to the table
10063 $table->data[] = array($displayname, $version, $hideshow, $settings);
10065 $return .= html_writer::table($table);
10066 $return .= get_string('configwebserviceplugins', 'webservice');
10067 $return .= $OUTPUT->box_end();
10069 return highlight($query, $return);
10075 * Special class for web service token administration.
10077 * @author Jerome Mouneyrac
10079 class admin_setting_managewebservicetokens extends admin_setting {
10082 * Calls parent::__construct with specific arguments
10084 public function __construct() {
10085 $this->nosave = true;
10086 parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
10090 * Always returns true, does nothing
10092 * @return true
10094 public function get_setting() {
10095 return true;
10099 * Always returns true, does nothing
10101 * @return true
10103 public function get_defaultsetting() {
10104 return true;
10108 * Always returns '', does not write anything
10110 * @return string Always returns ''
10112 public function write_setting($data) {
10113 // do not write any setting
10114 return '';
10118 * Builds the XHTML to display the control
10120 * @param string $data Unused
10121 * @param string $query
10122 * @return string
10124 public function output_html($data, $query='') {
10125 global $CFG, $OUTPUT;
10127 require_once($CFG->dirroot . '/webservice/classes/token_table.php');
10128 $baseurl = new moodle_url('/' . $CFG->admin . '/settings.php?section=webservicetokens');
10130 $return = $OUTPUT->box_start('generalbox webservicestokenui');
10132 if (has_capability('moodle/webservice:managealltokens', context_system::instance())) {
10133 $return .= \html_writer::div(get_string('onlyseecreatedtokens', 'webservice'));
10136 $table = new \webservice\token_table('webservicetokens');
10137 $table->define_baseurl($baseurl);
10138 $table->attributes['class'] = 'admintable generaltable'; // Any need changing?
10139 $table->data = array();
10140 ob_start();
10141 $table->out(10, false);
10142 $tablehtml = ob_get_contents();
10143 ob_end_clean();
10144 $return .= $tablehtml;
10146 $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
10148 $return .= $OUTPUT->box_end();
10149 // add a token to the table
10150 $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
10151 $return .= get_string('add')."</a>";
10153 return highlight($query, $return);
10159 * Colour picker
10161 * @copyright 2010 Sam Hemelryk
10162 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10164 class admin_setting_configcolourpicker extends admin_setting {
10167 * Information for previewing the colour
10169 * @var array|null
10171 protected $previewconfig = null;
10174 * Use default when empty.
10176 protected $usedefaultwhenempty = true;
10180 * @param string $name
10181 * @param string $visiblename
10182 * @param string $description
10183 * @param string $defaultsetting
10184 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10186 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10187 $usedefaultwhenempty = true) {
10188 $this->previewconfig = $previewconfig;
10189 $this->usedefaultwhenempty = $usedefaultwhenempty;
10190 parent::__construct($name, $visiblename, $description, $defaultsetting);
10191 $this->set_force_ltr(true);
10195 * Return the setting
10197 * @return mixed returns config if successful else null
10199 public function get_setting() {
10200 return $this->config_read($this->name);
10204 * Saves the setting
10206 * @param string $data
10207 * @return bool
10209 public function write_setting($data) {
10210 $data = $this->validate($data);
10211 if ($data === false) {
10212 return get_string('validateerror', 'admin');
10214 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10218 * Validates the colour that was entered by the user
10220 * @param string $data
10221 * @return string|false
10223 protected function validate($data) {
10225 * List of valid HTML colour names
10227 * @var array
10229 $colornames = array(
10230 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10231 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10232 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10233 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10234 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10235 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10236 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10237 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10238 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10239 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10240 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10241 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10242 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10243 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10244 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10245 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10246 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10247 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10248 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10249 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10250 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10251 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10252 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10253 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10254 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10255 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10256 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10257 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10258 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10259 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10260 'whitesmoke', 'yellow', 'yellowgreen'
10263 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10264 if (strpos($data, '#')!==0) {
10265 $data = '#'.$data;
10267 return $data;
10268 } else if (in_array(strtolower($data), $colornames)) {
10269 return $data;
10270 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10271 return $data;
10272 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10273 return $data;
10274 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10275 return $data;
10276 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10277 return $data;
10278 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10279 return $data;
10280 } else if (empty($data)) {
10281 if ($this->usedefaultwhenempty){
10282 return $this->defaultsetting;
10283 } else {
10284 return '';
10286 } else {
10287 return false;
10292 * Generates the HTML for the setting
10294 * @global moodle_page $PAGE
10295 * @global core_renderer $OUTPUT
10296 * @param string $data
10297 * @param string $query
10299 public function output_html($data, $query = '') {
10300 global $PAGE, $OUTPUT;
10302 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10303 $context = (object) [
10304 'id' => $this->get_id(),
10305 'name' => $this->get_full_name(),
10306 'value' => $data,
10307 'icon' => $icon->export_for_template($OUTPUT),
10308 'haspreviewconfig' => !empty($this->previewconfig),
10309 'forceltr' => $this->get_force_ltr()
10312 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10313 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10315 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10316 $this->get_defaultsetting(), $query);
10323 * Class used for uploading of one file into file storage,
10324 * the file name is stored in config table.
10326 * Please note you need to implement your own '_pluginfile' callback function,
10327 * this setting only stores the file, it does not deal with file serving.
10329 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10330 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10332 class admin_setting_configstoredfile extends admin_setting {
10333 /** @var array file area options - should be one file only */
10334 protected $options;
10335 /** @var string name of the file area */
10336 protected $filearea;
10337 /** @var int intemid */
10338 protected $itemid;
10339 /** @var string used for detection of changes */
10340 protected $oldhashes;
10343 * Create new stored file setting.
10345 * @param string $name low level setting name
10346 * @param string $visiblename human readable setting name
10347 * @param string $description description of setting
10348 * @param mixed $filearea file area for file storage
10349 * @param int $itemid itemid for file storage
10350 * @param array $options file area options
10352 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10353 parent::__construct($name, $visiblename, $description, '');
10354 $this->filearea = $filearea;
10355 $this->itemid = $itemid;
10356 $this->options = (array)$options;
10360 * Applies defaults and returns all options.
10361 * @return array
10363 protected function get_options() {
10364 global $CFG;
10366 require_once("$CFG->libdir/filelib.php");
10367 require_once("$CFG->dirroot/repository/lib.php");
10368 $defaults = array(
10369 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10370 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10371 'context' => context_system::instance());
10372 foreach($this->options as $k => $v) {
10373 $defaults[$k] = $v;
10376 return $defaults;
10379 public function get_setting() {
10380 return $this->config_read($this->name);
10383 public function write_setting($data) {
10384 global $USER;
10386 // Let's not deal with validation here, this is for admins only.
10387 $current = $this->get_setting();
10388 if (empty($data) && $current === null) {
10389 // This will be the case when applying default settings (installation).
10390 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10391 } else if (!is_number($data)) {
10392 // Draft item id is expected here!
10393 return get_string('errorsetting', 'admin');
10396 $options = $this->get_options();
10397 $fs = get_file_storage();
10398 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10400 $this->oldhashes = null;
10401 if ($current) {
10402 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10403 if ($file = $fs->get_file_by_hash($hash)) {
10404 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10406 unset($file);
10409 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10410 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10411 // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10412 // with an error because the draft area does not exist, as he did not use it.
10413 $usercontext = context_user::instance($USER->id);
10414 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10415 return get_string('errorsetting', 'admin');
10419 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10420 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10422 $filepath = '';
10423 if ($files) {
10424 /** @var stored_file $file */
10425 $file = reset($files);
10426 $filepath = $file->get_filepath().$file->get_filename();
10429 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10432 public function post_write_settings($original) {
10433 $options = $this->get_options();
10434 $fs = get_file_storage();
10435 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10437 $current = $this->get_setting();
10438 $newhashes = null;
10439 if ($current) {
10440 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10441 if ($file = $fs->get_file_by_hash($hash)) {
10442 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10444 unset($file);
10447 if ($this->oldhashes === $newhashes) {
10448 $this->oldhashes = null;
10449 return false;
10451 $this->oldhashes = null;
10453 $callbackfunction = $this->updatedcallback;
10454 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10455 $callbackfunction($this->get_full_name());
10457 return true;
10460 public function output_html($data, $query = '') {
10461 global $PAGE, $CFG;
10463 $options = $this->get_options();
10464 $id = $this->get_id();
10465 $elname = $this->get_full_name();
10466 $draftitemid = file_get_submitted_draft_itemid($elname);
10467 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10468 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10470 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10471 require_once("$CFG->dirroot/lib/form/filemanager.php");
10473 $fmoptions = new stdClass();
10474 $fmoptions->mainfile = $options['mainfile'];
10475 $fmoptions->maxbytes = $options['maxbytes'];
10476 $fmoptions->maxfiles = $options['maxfiles'];
10477 $fmoptions->client_id = uniqid();
10478 $fmoptions->itemid = $draftitemid;
10479 $fmoptions->subdirs = $options['subdirs'];
10480 $fmoptions->target = $id;
10481 $fmoptions->accepted_types = $options['accepted_types'];
10482 $fmoptions->return_types = $options['return_types'];
10483 $fmoptions->context = $options['context'];
10484 $fmoptions->areamaxbytes = $options['areamaxbytes'];
10486 $fm = new form_filemanager($fmoptions);
10487 $output = $PAGE->get_renderer('core', 'files');
10488 $html = $output->render($fm);
10490 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
10491 $html .= '<input value="" id="'.$id.'" type="hidden" />';
10493 return format_admin_setting($this, $this->visiblename,
10494 '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
10495 $this->description, true, '', '', $query);
10501 * Administration interface for user specified regular expressions for device detection.
10503 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10505 class admin_setting_devicedetectregex extends admin_setting {
10508 * Calls parent::__construct with specific args
10510 * @param string $name
10511 * @param string $visiblename
10512 * @param string $description
10513 * @param mixed $defaultsetting
10515 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10516 global $CFG;
10517 parent::__construct($name, $visiblename, $description, $defaultsetting);
10521 * Return the current setting(s)
10523 * @return array Current settings array
10525 public function get_setting() {
10526 global $CFG;
10528 $config = $this->config_read($this->name);
10529 if (is_null($config)) {
10530 return null;
10533 return $this->prepare_form_data($config);
10537 * Save selected settings
10539 * @param array $data Array of settings to save
10540 * @return bool
10542 public function write_setting($data) {
10543 if (empty($data)) {
10544 $data = array();
10547 if ($this->config_write($this->name, $this->process_form_data($data))) {
10548 return ''; // success
10549 } else {
10550 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10555 * Return XHTML field(s) for regexes
10557 * @param array $data Array of options to set in HTML
10558 * @return string XHTML string for the fields and wrapping div(s)
10560 public function output_html($data, $query='') {
10561 global $OUTPUT;
10563 $context = (object) [
10564 'expressions' => [],
10565 'name' => $this->get_full_name()
10568 if (empty($data)) {
10569 $looplimit = 1;
10570 } else {
10571 $looplimit = (count($data)/2)+1;
10574 for ($i=0; $i<$looplimit; $i++) {
10576 $expressionname = 'expression'.$i;
10578 if (!empty($data[$expressionname])){
10579 $expression = $data[$expressionname];
10580 } else {
10581 $expression = '';
10584 $valuename = 'value'.$i;
10586 if (!empty($data[$valuename])){
10587 $value = $data[$valuename];
10588 } else {
10589 $value= '';
10592 $context->expressions[] = [
10593 'index' => $i,
10594 'expression' => $expression,
10595 'value' => $value
10599 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10601 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10605 * Converts the string of regexes
10607 * @see self::process_form_data()
10608 * @param $regexes string of regexes
10609 * @return array of form fields and their values
10611 protected function prepare_form_data($regexes) {
10613 $regexes = json_decode($regexes);
10615 $form = array();
10617 $i = 0;
10619 foreach ($regexes as $value => $regex) {
10620 $expressionname = 'expression'.$i;
10621 $valuename = 'value'.$i;
10623 $form[$expressionname] = $regex;
10624 $form[$valuename] = $value;
10625 $i++;
10628 return $form;
10632 * Converts the data from admin settings form into a string of regexes
10634 * @see self::prepare_form_data()
10635 * @param array $data array of admin form fields and values
10636 * @return false|string of regexes
10638 protected function process_form_data(array $form) {
10640 $count = count($form); // number of form field values
10642 if ($count % 2) {
10643 // we must get five fields per expression
10644 return false;
10647 $regexes = array();
10648 for ($i = 0; $i < $count / 2; $i++) {
10649 $expressionname = "expression".$i;
10650 $valuename = "value".$i;
10652 $expression = trim($form['expression'.$i]);
10653 $value = trim($form['value'.$i]);
10655 if (empty($expression)){
10656 continue;
10659 $regexes[$value] = $expression;
10662 $regexes = json_encode($regexes);
10664 return $regexes;
10670 * Multiselect for current modules
10672 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10674 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10675 private $excludesystem;
10678 * Calls parent::__construct - note array $choices is not required
10680 * @param string $name setting name
10681 * @param string $visiblename localised setting name
10682 * @param string $description setting description
10683 * @param array $defaultsetting a plain array of default module ids
10684 * @param bool $excludesystem If true, excludes modules with 'system' archetype
10686 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10687 $excludesystem = true) {
10688 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10689 $this->excludesystem = $excludesystem;
10693 * Loads an array of current module choices
10695 * @return bool always return true
10697 public function load_choices() {
10698 if (is_array($this->choices)) {
10699 return true;
10701 $this->choices = array();
10703 global $CFG, $DB;
10704 $records = $DB->get_records('modules', array('visible'=>1), 'name');
10705 foreach ($records as $record) {
10706 // Exclude modules if the code doesn't exist
10707 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10708 // Also exclude system modules (if specified)
10709 if (!($this->excludesystem &&
10710 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10711 MOD_ARCHETYPE_SYSTEM)) {
10712 $this->choices[$record->id] = $record->name;
10716 return true;
10721 * Admin setting to show if a php extension is enabled or not.
10723 * @copyright 2013 Damyon Wiese
10724 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10726 class admin_setting_php_extension_enabled extends admin_setting {
10728 /** @var string The name of the extension to check for */
10729 private $extension;
10732 * Calls parent::__construct with specific arguments
10734 public function __construct($name, $visiblename, $description, $extension) {
10735 $this->extension = $extension;
10736 $this->nosave = true;
10737 parent::__construct($name, $visiblename, $description, '');
10741 * Always returns true, does nothing
10743 * @return true
10745 public function get_setting() {
10746 return true;
10750 * Always returns true, does nothing
10752 * @return true
10754 public function get_defaultsetting() {
10755 return true;
10759 * Always returns '', does not write anything
10761 * @return string Always returns ''
10763 public function write_setting($data) {
10764 // Do not write any setting.
10765 return '';
10769 * Outputs the html for this setting.
10770 * @return string Returns an XHTML string
10772 public function output_html($data, $query='') {
10773 global $OUTPUT;
10775 $o = '';
10776 if (!extension_loaded($this->extension)) {
10777 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
10779 $o .= format_admin_setting($this, $this->visiblename, $warning);
10781 return $o;
10786 * Server timezone setting.
10788 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10789 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10790 * @author Petr Skoda <petr.skoda@totaralms.com>
10792 class admin_setting_servertimezone extends admin_setting_configselect {
10794 * Constructor.
10796 public function __construct() {
10797 $default = core_date::get_default_php_timezone();
10798 if ($default === 'UTC') {
10799 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
10800 $default = 'Europe/London';
10803 parent::__construct('timezone',
10804 new lang_string('timezone', 'core_admin'),
10805 new lang_string('configtimezone', 'core_admin'), $default, null);
10809 * Lazy load timezone options.
10810 * @return bool true if loaded, false if error
10812 public function load_choices() {
10813 global $CFG;
10814 if (is_array($this->choices)) {
10815 return true;
10818 $current = isset($CFG->timezone) ? $CFG->timezone : null;
10819 $this->choices = core_date::get_list_of_timezones($current, false);
10820 if ($current == 99) {
10821 // Do not show 99 unless it is current value, we want to get rid of it over time.
10822 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
10823 core_date::get_default_php_timezone());
10826 return true;
10831 * Forced user timezone setting.
10833 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10834 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10835 * @author Petr Skoda <petr.skoda@totaralms.com>
10837 class admin_setting_forcetimezone extends admin_setting_configselect {
10839 * Constructor.
10841 public function __construct() {
10842 parent::__construct('forcetimezone',
10843 new lang_string('forcetimezone', 'core_admin'),
10844 new lang_string('helpforcetimezone', 'core_admin'), '99', null);
10848 * Lazy load timezone options.
10849 * @return bool true if loaded, false if error
10851 public function load_choices() {
10852 global $CFG;
10853 if (is_array($this->choices)) {
10854 return true;
10857 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
10858 $this->choices = core_date::get_list_of_timezones($current, true);
10859 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
10861 return true;
10867 * Search setup steps info.
10869 * @package core
10870 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
10871 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10873 class admin_setting_searchsetupinfo extends admin_setting {
10876 * Calls parent::__construct with specific arguments
10878 public function __construct() {
10879 $this->nosave = true;
10880 parent::__construct('searchsetupinfo', '', '', '');
10884 * Always returns true, does nothing
10886 * @return true
10888 public function get_setting() {
10889 return true;
10893 * Always returns true, does nothing
10895 * @return true
10897 public function get_defaultsetting() {
10898 return true;
10902 * Always returns '', does not write anything
10904 * @param array $data
10905 * @return string Always returns ''
10907 public function write_setting($data) {
10908 // Do not write any setting.
10909 return '';
10913 * Builds the HTML to display the control
10915 * @param string $data Unused
10916 * @param string $query
10917 * @return string
10919 public function output_html($data, $query='') {
10920 global $CFG, $OUTPUT;
10922 $return = '';
10923 $brtag = html_writer::empty_tag('br');
10925 $searchareas = \core_search\manager::get_search_areas_list();
10926 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
10927 $anyindexed = false;
10928 foreach ($searchareas as $areaid => $searcharea) {
10929 list($componentname, $varname) = $searcharea->get_config_var_name();
10930 if (get_config($componentname, $varname . '_indexingstart')) {
10931 $anyindexed = true;
10932 break;
10936 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
10938 $table = new html_table();
10939 $table->head = array(get_string('step', 'search'), get_string('status'));
10940 $table->colclasses = array('leftalign step', 'leftalign status');
10941 $table->id = 'searchsetup';
10942 $table->attributes['class'] = 'admintable generaltable';
10943 $table->data = array();
10945 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
10947 // Select a search engine.
10948 $row = array();
10949 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
10950 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
10951 array('href' => $url));
10953 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10954 if (!empty($CFG->searchengine)) {
10955 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
10956 array('class' => 'badge badge-success'));
10959 $row[1] = $status;
10960 $table->data[] = $row;
10962 // Available areas.
10963 $row = array();
10964 $url = new moodle_url('/admin/searchareas.php');
10965 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
10966 array('href' => $url));
10968 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10969 if ($anyenabled) {
10970 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
10973 $row[1] = $status;
10974 $table->data[] = $row;
10976 // Setup search engine.
10977 $row = array();
10978 if (empty($CFG->searchengine)) {
10979 $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
10980 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10981 } else {
10982 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
10983 $row[0] = '3. ' . html_writer::tag('a', get_string('setupsearchengine', 'admin'),
10984 array('href' => $url));
10985 // Check the engine status.
10986 $searchengine = \core_search\manager::search_engine_instance();
10987 try {
10988 $serverstatus = $searchengine->is_server_ready();
10989 } catch (\moodle_exception $e) {
10990 $serverstatus = $e->getMessage();
10992 if ($serverstatus === true) {
10993 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
10994 } else {
10995 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
10997 $row[1] = $status;
10999 $table->data[] = $row;
11001 // Indexed data.
11002 $row = array();
11003 $url = new moodle_url('/admin/searchareas.php');
11004 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11005 if ($anyindexed) {
11006 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11007 } else {
11008 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11010 $row[1] = $status;
11011 $table->data[] = $row;
11013 // Enable global search.
11014 $row = array();
11015 $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11016 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11017 array('href' => $url));
11018 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11019 if (\core_search\manager::is_global_search_enabled()) {
11020 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11022 $row[1] = $status;
11023 $table->data[] = $row;
11025 $return .= html_writer::table($table);
11027 return highlight($query, $return);
11033 * Used to validate the contents of SCSS code and ensuring they are parsable.
11035 * It does not attempt to detect undefined SCSS variables because it is designed
11036 * to be used without knowledge of other config/scss included.
11038 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11039 * @copyright 2016 Dan Poltawski <dan@moodle.com>
11041 class admin_setting_scsscode extends admin_setting_configtextarea {
11044 * Validate the contents of the SCSS to ensure its parsable. Does not
11045 * attempt to detect undefined scss variables.
11047 * @param string $data The scss code from text field.
11048 * @return mixed bool true for success or string:error on failure.
11050 public function validate($data) {
11051 if (empty($data)) {
11052 return true;
11055 $scss = new core_scss();
11056 try {
11057 $scss->compile($data);
11058 } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11059 return get_string('scssinvalid', 'admin', $e->getMessage());
11060 } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11061 // Silently ignore this - it could be a scss variable defined from somewhere
11062 // else which we are not examining here.
11063 return true;
11066 return true;
11072 * Administration setting to define a list of file types.
11074 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11075 * @copyright 2017 David Mudrák <david@moodle.com>
11076 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11078 class admin_setting_filetypes extends admin_setting_configtext {
11080 /** @var array Allow selection from these file types only. */
11081 protected $onlytypes = [];
11083 /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11084 protected $allowall = true;
11086 /** @var core_form\filetypes_util instance to use as a helper. */
11087 protected $util = null;
11090 * Constructor.
11092 * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11093 * @param string $visiblename Localised label of the setting
11094 * @param string $description Localised description of the setting
11095 * @param string $defaultsetting Default setting value.
11096 * @param array $options Setting widget options, an array with optional keys:
11097 * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11098 * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11100 public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11102 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11104 if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11105 $this->onlytypes = $options['onlytypes'];
11108 if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11109 $this->allowall = (bool)$options['allowall'];
11112 $this->util = new \core_form\filetypes_util();
11116 * Normalize the user's input and write it to the database as comma separated list.
11118 * Comma separated list as a text representation of the array was chosen to
11119 * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11121 * @param string $data Value submitted by the admin.
11122 * @return string Epty string if all good, error message otherwise.
11124 public function write_setting($data) {
11125 return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11129 * Validate data before storage
11131 * @param string $data The setting values provided by the admin
11132 * @return bool|string True if ok, the string if error found
11134 public function validate($data) {
11136 // No need to call parent's validation here as we are PARAM_RAW.
11138 if ($this->util->is_whitelisted($data, $this->onlytypes)) {
11139 return true;
11141 } else {
11142 $troublemakers = $this->util->get_not_whitelisted($data, $this->onlytypes);
11143 return get_string('filetypesnotwhitelisted', 'core_form', implode(' ', $troublemakers));
11148 * Return an HTML string for the setting element.
11150 * @param string $data The current setting value
11151 * @param string $query Admin search query to be highlighted
11152 * @return string HTML to be displayed
11154 public function output_html($data, $query='') {
11155 global $OUTPUT, $PAGE;
11157 $default = $this->get_defaultsetting();
11158 $context = (object) [
11159 'id' => $this->get_id(),
11160 'name' => $this->get_full_name(),
11161 'value' => $data,
11162 'descriptions' => $this->util->describe_file_types($data),
11164 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11166 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11167 $this->get_id(),
11168 $this->visiblename->out(),
11169 $this->onlytypes,
11170 $this->allowall,
11173 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11177 * Should the values be always displayed in LTR mode?
11179 * We always return true here because these values are not RTL compatible.
11181 * @return bool True because these values are not RTL compatible.
11183 public function get_force_ltr() {
11184 return true;
11189 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11191 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11192 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11194 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11197 * Constructor.
11199 * @param string $name
11200 * @param string $visiblename
11201 * @param string $description
11202 * @param mixed $defaultsetting string or array
11203 * @param mixed $paramtype
11204 * @param string $cols
11205 * @param string $rows
11207 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11208 $cols = '60', $rows = '8') {
11209 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11210 // Pre-set force LTR to false.
11211 $this->set_force_ltr(false);
11215 * Validate the content and format of the age of digital consent map to ensure it is parsable.
11217 * @param string $data The age of digital consent map from text field.
11218 * @return mixed bool true for success or string:error on failure.
11220 public function validate($data) {
11221 if (empty($data)) {
11222 return true;
11225 try {
11226 \core_auth\digital_consent::parse_age_digital_consent_map($data);
11227 } catch (\moodle_exception $e) {
11228 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11231 return true;
11236 * Selection of plugins that can work as site policy handlers
11238 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11239 * @copyright 2018 Marina Glancy
11241 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11244 * Constructor
11245 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11246 * for ones in config_plugins.
11247 * @param string $visiblename localised
11248 * @param string $description long localised info
11249 * @param string $defaultsetting
11251 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11252 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11256 * Lazy-load the available choices for the select box
11258 public function load_choices() {
11259 if (during_initial_install()) {
11260 return false;
11262 if (is_array($this->choices)) {
11263 return true;
11266 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11267 $manager = new \core_privacy\local\sitepolicy\manager();
11268 $plugins = $manager->get_all_handlers();
11269 foreach ($plugins as $pname => $unused) {
11270 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11271 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11274 return true;
11279 * Used to validate theme presets code and ensuring they compile well.
11281 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11282 * @copyright 2019 Bas Brands <bas@moodle.com>
11284 class admin_setting_configthemepreset extends admin_setting_configselect {
11286 /** @var string The name of the theme to check for */
11287 private $themename;
11290 * Constructor
11291 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11292 * or 'myplugin/mysetting' for ones in config_plugins.
11293 * @param string $visiblename localised
11294 * @param string $description long localised info
11295 * @param string|int $defaultsetting
11296 * @param array $choices array of $value=>$label for each selection
11297 * @param string $themename name of theme to check presets for.
11299 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11300 $this->themename = $themename;
11301 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11305 * Write settings if validated
11307 * @param string $data
11308 * @return string
11310 public function write_setting($data) {
11311 $validated = $this->validate($data);
11312 if ($validated !== true) {
11313 return $validated;
11315 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11319 * Validate the preset file to ensure its parsable.
11321 * @param string $data The preset file chosen.
11322 * @return mixed bool true for success or string:error on failure.
11324 public function validate($data) {
11326 if (in_array($data, ['default.scss', 'plain.scss'])) {
11327 return true;
11330 $fs = get_file_storage();
11331 $theme = theme_config::load($this->themename);
11332 $context = context_system::instance();
11334 // If the preset has not changed there is no need to validate it.
11335 if ($theme->settings->preset == $data) {
11336 return true;
11339 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11340 // This operation uses a lot of resources.
11341 raise_memory_limit(MEMORY_EXTRA);
11342 core_php_time_limit::raise(300);
11344 // TODO: MDL-62757 When changing anything in this method please do not forget to check
11345 // if the get_css_content_from_scss() method in class theme_config needs updating too.
11347 $compiler = new core_scss();
11348 $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11349 $compiler->append_raw_scss($presetfile->get_content());
11350 if ($scssproperties = $theme->get_scss_property()) {
11351 $compiler->setImportPaths($scssproperties[0]);
11353 $compiler->append_raw_scss($theme->get_extra_scss_code());
11355 try {
11356 $compiler->to_css();
11357 } catch (Exception $e) {
11358 return get_string('invalidthemepreset', 'admin', $e->getMessage());
11361 // Try to save memory.
11362 $compiler = null;
11363 unset($compiler);
11366 return true;
11371 * Selection of plugins that can work as H5P libraries handlers
11373 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11374 * @copyright 2020 Sara Arjona <sara@moodle.com>
11376 class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11379 * Constructor
11380 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11381 * for ones in config_plugins.
11382 * @param string $visiblename localised
11383 * @param string $description long localised info
11384 * @param string $defaultsetting
11386 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11387 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11391 * Lazy-load the available choices for the select box
11393 public function load_choices() {
11394 if (during_initial_install()) {
11395 return false;
11397 if (is_array($this->choices)) {
11398 return true;
11401 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11402 foreach ($this->choices as $name => $class) {
11403 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11404 ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11407 return true;