Merge branch 'MDL-42592_master' of https://github.com/markn86/moodle
[moodle.git] / lib / adminlib.php
blobb3006870ceda5dd09c9945f2df74483c53eb45bd
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(dirname(dirname(dirname(__FILE__))).'/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 @set_time_limit(0);
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);
135 if (file_exists("$base/db/subplugins.php")) {
136 $subplugins = array();
137 include("$base/db/subplugins.php");
138 foreach ($subplugins as $subplugintype=>$dir) {
139 $instances = core_component::get_plugin_list($subplugintype);
140 foreach ($instances as $subpluginname => $notusedpluginpath) {
141 uninstall_plugin($subplugintype, $subpluginname);
148 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
150 if ($type === 'mod') {
151 $pluginname = $name; // eg. 'forum'
152 if (get_string_manager()->string_exists('modulename', $component)) {
153 $strpluginname = get_string('modulename', $component);
154 } else {
155 $strpluginname = $component;
158 } else {
159 $pluginname = $component;
160 if (get_string_manager()->string_exists('pluginname', $component)) {
161 $strpluginname = get_string('pluginname', $component);
162 } else {
163 $strpluginname = $component;
167 echo $OUTPUT->heading($pluginname);
169 // Custom plugin uninstall.
170 $plugindirectory = core_component::get_plugin_directory($type, $name);
171 $uninstalllib = $plugindirectory . '/db/uninstall.php';
172 if (file_exists($uninstalllib)) {
173 require_once($uninstalllib);
174 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
175 if (function_exists($uninstallfunction)) {
176 // Do not verify result, let plugin complain if necessary.
177 $uninstallfunction();
181 // Specific plugin type cleanup.
182 $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
183 if ($plugininfo) {
184 $plugininfo->uninstall_cleanup();
185 core_plugin_manager::reset_caches();
187 $plugininfo = null;
189 // perform clean-up task common for all the plugin/subplugin types
191 //delete the web service functions and pre-built services
192 require_once($CFG->dirroot.'/lib/externallib.php');
193 external_delete_descriptions($component);
195 // delete calendar events
196 $DB->delete_records('event', array('modulename' => $pluginname));
198 // delete all the logs
199 $DB->delete_records('log', array('module' => $pluginname));
201 // delete log_display information
202 $DB->delete_records('log_display', array('component' => $component));
204 // delete the module configuration records
205 unset_all_config_for_plugin($component);
206 if ($type === 'mod') {
207 unset_all_config_for_plugin($pluginname);
210 // delete message provider
211 message_provider_uninstall($component);
213 // delete the plugin tables
214 $xmldbfilepath = $plugindirectory . '/db/install.xml';
215 drop_plugin_tables($component, $xmldbfilepath, false);
216 if ($type === 'mod' or $type === 'block') {
217 // non-frankenstyle table prefixes
218 drop_plugin_tables($name, $xmldbfilepath, false);
221 // delete the capabilities that were defined by this module
222 capabilities_cleanup($component);
224 // remove event handlers and dequeue pending events
225 events_uninstall($component);
227 // Delete all remaining files in the filepool owned by the component.
228 $fs = get_file_storage();
229 $fs->delete_component_files($component);
231 // Finally purge all caches.
232 purge_all_caches();
234 // Invalidate the hash used for upgrade detections.
235 set_config('allversionshash', '');
237 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
241 * Returns the version of installed component
243 * @param string $component component name
244 * @param string $source either 'disk' or 'installed' - where to get the version information from
245 * @return string|bool version number or false if the component is not found
247 function get_component_version($component, $source='installed') {
248 global $CFG, $DB;
250 list($type, $name) = core_component::normalize_component($component);
252 // moodle core or a core subsystem
253 if ($type === 'core') {
254 if ($source === 'installed') {
255 if (empty($CFG->version)) {
256 return false;
257 } else {
258 return $CFG->version;
260 } else {
261 if (!is_readable($CFG->dirroot.'/version.php')) {
262 return false;
263 } else {
264 $version = null; //initialize variable for IDEs
265 include($CFG->dirroot.'/version.php');
266 return $version;
271 // activity module
272 if ($type === 'mod') {
273 if ($source === 'installed') {
274 if ($CFG->version < 2013092001.02) {
275 return $DB->get_field('modules', 'version', array('name'=>$name));
276 } else {
277 return get_config('mod_'.$name, 'version');
280 } else {
281 $mods = core_component::get_plugin_list('mod');
282 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
283 return false;
284 } else {
285 $plugin = new stdClass();
286 $plugin->version = null;
287 $module = $plugin;
288 include($mods[$name].'/version.php');
289 return $plugin->version;
294 // block
295 if ($type === 'block') {
296 if ($source === 'installed') {
297 if ($CFG->version < 2013092001.02) {
298 return $DB->get_field('block', 'version', array('name'=>$name));
299 } else {
300 return get_config('block_'.$name, 'version');
302 } else {
303 $blocks = core_component::get_plugin_list('block');
304 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
305 return false;
306 } else {
307 $plugin = new stdclass();
308 include($blocks[$name].'/version.php');
309 return $plugin->version;
314 // all other plugin types
315 if ($source === 'installed') {
316 return get_config($type.'_'.$name, 'version');
317 } else {
318 $plugins = core_component::get_plugin_list($type);
319 if (empty($plugins[$name])) {
320 return false;
321 } else {
322 $plugin = new stdclass();
323 include($plugins[$name].'/version.php');
324 return $plugin->version;
330 * Delete all plugin tables
332 * @param string $name Name of plugin, used as table prefix
333 * @param string $file Path to install.xml file
334 * @param bool $feedback defaults to true
335 * @return bool Always returns true
337 function drop_plugin_tables($name, $file, $feedback=true) {
338 global $CFG, $DB;
340 // first try normal delete
341 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
342 return true;
345 // then try to find all tables that start with name and are not in any xml file
346 $used_tables = get_used_table_names();
348 $tables = $DB->get_tables();
350 /// Iterate over, fixing id fields as necessary
351 foreach ($tables as $table) {
352 if (in_array($table, $used_tables)) {
353 continue;
356 if (strpos($table, $name) !== 0) {
357 continue;
360 // found orphan table --> delete it
361 if ($DB->get_manager()->table_exists($table)) {
362 $xmldb_table = new xmldb_table($table);
363 $DB->get_manager()->drop_table($xmldb_table);
367 return true;
371 * Returns names of all known tables == tables that moodle knows about.
373 * @return array Array of lowercase table names
375 function get_used_table_names() {
376 $table_names = array();
377 $dbdirs = get_db_directories();
379 foreach ($dbdirs as $dbdir) {
380 $file = $dbdir.'/install.xml';
382 $xmldb_file = new xmldb_file($file);
384 if (!$xmldb_file->fileExists()) {
385 continue;
388 $loaded = $xmldb_file->loadXMLStructure();
389 $structure = $xmldb_file->getStructure();
391 if ($loaded and $tables = $structure->getTables()) {
392 foreach($tables as $table) {
393 $table_names[] = strtolower($table->getName());
398 return $table_names;
402 * Returns list of all directories where we expect install.xml files
403 * @return array Array of paths
405 function get_db_directories() {
406 global $CFG;
408 $dbdirs = array();
410 /// First, the main one (lib/db)
411 $dbdirs[] = $CFG->libdir.'/db';
413 /// Then, all the ones defined by core_component::get_plugin_types()
414 $plugintypes = core_component::get_plugin_types();
415 foreach ($plugintypes as $plugintype => $pluginbasedir) {
416 if ($plugins = core_component::get_plugin_list($plugintype)) {
417 foreach ($plugins as $plugin => $plugindir) {
418 $dbdirs[] = $plugindir.'/db';
423 return $dbdirs;
427 * Try to obtain or release the cron lock.
428 * @param string $name name of lock
429 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
430 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
431 * @return bool true if lock obtained
433 function set_cron_lock($name, $until, $ignorecurrent=false) {
434 global $DB;
435 if (empty($name)) {
436 debugging("Tried to get a cron lock for a null fieldname");
437 return false;
440 // remove lock by force == remove from config table
441 if (is_null($until)) {
442 set_config($name, null);
443 return true;
446 if (!$ignorecurrent) {
447 // read value from db - other processes might have changed it
448 $value = $DB->get_field('config', 'value', array('name'=>$name));
450 if ($value and $value > time()) {
451 //lock active
452 return false;
456 set_config($name, $until);
457 return true;
461 * Test if and critical warnings are present
462 * @return bool
464 function admin_critical_warnings_present() {
465 global $SESSION;
467 if (!has_capability('moodle/site:config', context_system::instance())) {
468 return 0;
471 if (!isset($SESSION->admin_critical_warning)) {
472 $SESSION->admin_critical_warning = 0;
473 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
474 $SESSION->admin_critical_warning = 1;
478 return $SESSION->admin_critical_warning;
482 * Detects if float supports at least 10 decimal digits
484 * Detects if float supports at least 10 decimal digits
485 * and also if float-->string conversion works as expected.
487 * @return bool true if problem found
489 function is_float_problem() {
490 $num1 = 2009010200.01;
491 $num2 = 2009010200.02;
493 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
497 * Try to verify that dataroot is not accessible from web.
499 * Try to verify that dataroot is not accessible from web.
500 * It is not 100% correct but might help to reduce number of vulnerable sites.
501 * Protection from httpd.conf and .htaccess is not detected properly.
503 * @uses INSECURE_DATAROOT_WARNING
504 * @uses INSECURE_DATAROOT_ERROR
505 * @param bool $fetchtest try to test public access by fetching file, default false
506 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
508 function is_dataroot_insecure($fetchtest=false) {
509 global $CFG;
511 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
513 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
514 $rp = strrev(trim($rp, '/'));
515 $rp = explode('/', $rp);
516 foreach($rp as $r) {
517 if (strpos($siteroot, '/'.$r.'/') === 0) {
518 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
519 } else {
520 break; // probably alias root
524 $siteroot = strrev($siteroot);
525 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
527 if (strpos($dataroot, $siteroot) !== 0) {
528 return false;
531 if (!$fetchtest) {
532 return INSECURE_DATAROOT_WARNING;
535 // now try all methods to fetch a test file using http protocol
537 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
538 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
539 $httpdocroot = $matches[1];
540 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
541 make_upload_directory('diag');
542 $testfile = $CFG->dataroot.'/diag/public.txt';
543 if (!file_exists($testfile)) {
544 file_put_contents($testfile, 'test file, do not delete');
545 @chmod($testfile, $CFG->filepermissions);
547 $teststr = trim(file_get_contents($testfile));
548 if (empty($teststr)) {
549 // hmm, strange
550 return INSECURE_DATAROOT_WARNING;
553 $testurl = $datarooturl.'/diag/public.txt';
554 if (extension_loaded('curl') and
555 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
556 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
557 ($ch = @curl_init($testurl)) !== false) {
558 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
559 curl_setopt($ch, CURLOPT_HEADER, false);
560 $data = curl_exec($ch);
561 if (!curl_errno($ch)) {
562 $data = trim($data);
563 if ($data === $teststr) {
564 curl_close($ch);
565 return INSECURE_DATAROOT_ERROR;
568 curl_close($ch);
571 if ($data = @file_get_contents($testurl)) {
572 $data = trim($data);
573 if ($data === $teststr) {
574 return INSECURE_DATAROOT_ERROR;
578 preg_match('|https?://([^/]+)|i', $testurl, $matches);
579 $sitename = $matches[1];
580 $error = 0;
581 if ($fp = @fsockopen($sitename, 80, $error)) {
582 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
583 $localurl = $matches[1];
584 $out = "GET $localurl HTTP/1.1\r\n";
585 $out .= "Host: $sitename\r\n";
586 $out .= "Connection: Close\r\n\r\n";
587 fwrite($fp, $out);
588 $data = '';
589 $incoming = false;
590 while (!feof($fp)) {
591 if ($incoming) {
592 $data .= fgets($fp, 1024);
593 } else if (@fgets($fp, 1024) === "\r\n") {
594 $incoming = true;
597 fclose($fp);
598 $data = trim($data);
599 if ($data === $teststr) {
600 return INSECURE_DATAROOT_ERROR;
604 return INSECURE_DATAROOT_WARNING;
608 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
610 function enable_cli_maintenance_mode() {
611 global $CFG;
613 if (file_exists("$CFG->dataroot/climaintenance.html")) {
614 unlink("$CFG->dataroot/climaintenance.html");
617 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
618 $data = $CFG->maintenance_message;
619 $data = bootstrap_renderer::early_error_content($data, null, null, null);
620 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
622 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
623 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
625 } else {
626 $data = get_string('sitemaintenance', 'admin');
627 $data = bootstrap_renderer::early_error_content($data, null, null, null);
628 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
631 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
632 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
635 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
639 * Interface for anything appearing in the admin tree
641 * The interface that is implemented by anything that appears in the admin tree
642 * block. It forces inheriting classes to define a method for checking user permissions
643 * and methods for finding something in the admin tree.
645 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
647 interface part_of_admin_tree {
650 * Finds a named part_of_admin_tree.
652 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
653 * and not parentable_part_of_admin_tree, then this function should only check if
654 * $this->name matches $name. If it does, it should return a reference to $this,
655 * otherwise, it should return a reference to NULL.
657 * If a class inherits parentable_part_of_admin_tree, this method should be called
658 * recursively on all child objects (assuming, of course, the parent object's name
659 * doesn't match the search criterion).
661 * @param string $name The internal name of the part_of_admin_tree we're searching for.
662 * @return mixed An object reference or a NULL reference.
664 public function locate($name);
667 * Removes named part_of_admin_tree.
669 * @param string $name The internal name of the part_of_admin_tree we want to remove.
670 * @return bool success.
672 public function prune($name);
675 * Search using query
676 * @param string $query
677 * @return mixed array-object structure of found settings and pages
679 public function search($query);
682 * Verifies current user's access to this part_of_admin_tree.
684 * Used to check if the current user has access to this part of the admin tree or
685 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
686 * then this method is usually just a call to has_capability() in the site context.
688 * If a class inherits parentable_part_of_admin_tree, this method should return the
689 * logical OR of the return of check_access() on all child objects.
691 * @return bool True if the user has access, false if she doesn't.
693 public function check_access();
696 * Mostly useful for removing of some parts of the tree in admin tree block.
698 * @return True is hidden from normal list view
700 public function is_hidden();
703 * Show we display Save button at the page bottom?
704 * @return bool
706 public function show_save();
711 * Interface implemented by any part_of_admin_tree that has children.
713 * The interface implemented by any part_of_admin_tree that can be a parent
714 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
715 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
716 * include an add method for adding other part_of_admin_tree objects as children.
718 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
720 interface parentable_part_of_admin_tree extends part_of_admin_tree {
723 * Adds a part_of_admin_tree object to the admin tree.
725 * Used to add a part_of_admin_tree object to this object or a child of this
726 * object. $something should only be added if $destinationname matches
727 * $this->name. If it doesn't, add should be called on child objects that are
728 * also parentable_part_of_admin_tree's.
730 * $something should be appended as the last child in the $destinationname. If the
731 * $beforesibling is specified, $something should be prepended to it. If the given
732 * sibling is not found, $something should be appended to the end of $destinationname
733 * and a developer debugging message should be displayed.
735 * @param string $destinationname The internal name of the new parent for $something.
736 * @param part_of_admin_tree $something The object to be added.
737 * @return bool True on success, false on failure.
739 public function add($destinationname, $something, $beforesibling = null);
745 * The object used to represent folders (a.k.a. categories) in the admin tree block.
747 * Each admin_category object contains a number of part_of_admin_tree objects.
749 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
751 class admin_category implements parentable_part_of_admin_tree {
753 /** @var mixed An array of part_of_admin_tree objects that are this object's children */
754 public $children;
755 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
756 public $name;
757 /** @var string The displayed name for this category. Usually obtained through get_string() */
758 public $visiblename;
759 /** @var bool Should this category be hidden in admin tree block? */
760 public $hidden;
761 /** @var mixed Either a string or an array or strings */
762 public $path;
763 /** @var mixed Either a string or an array or strings */
764 public $visiblepath;
766 /** @var array fast lookup category cache, all categories of one tree point to one cache */
767 protected $category_cache;
770 * Constructor for an empty admin category
772 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
773 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
774 * @param bool $hidden hide category in admin tree block, defaults to false
776 public function __construct($name, $visiblename, $hidden=false) {
777 $this->children = array();
778 $this->name = $name;
779 $this->visiblename = $visiblename;
780 $this->hidden = $hidden;
784 * Returns a reference to the part_of_admin_tree object with internal name $name.
786 * @param string $name The internal name of the object we want.
787 * @param bool $findpath initialize path and visiblepath arrays
788 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
789 * defaults to false
791 public function locate($name, $findpath=false) {
792 if (!isset($this->category_cache[$this->name])) {
793 // somebody much have purged the cache
794 $this->category_cache[$this->name] = $this;
797 if ($this->name == $name) {
798 if ($findpath) {
799 $this->visiblepath[] = $this->visiblename;
800 $this->path[] = $this->name;
802 return $this;
805 // quick category lookup
806 if (!$findpath and isset($this->category_cache[$name])) {
807 return $this->category_cache[$name];
810 $return = NULL;
811 foreach($this->children as $childid=>$unused) {
812 if ($return = $this->children[$childid]->locate($name, $findpath)) {
813 break;
817 if (!is_null($return) and $findpath) {
818 $return->visiblepath[] = $this->visiblename;
819 $return->path[] = $this->name;
822 return $return;
826 * Search using query
828 * @param string query
829 * @return mixed array-object structure of found settings and pages
831 public function search($query) {
832 $result = array();
833 foreach ($this->children as $child) {
834 $subsearch = $child->search($query);
835 if (!is_array($subsearch)) {
836 debugging('Incorrect search result from '.$child->name);
837 continue;
839 $result = array_merge($result, $subsearch);
841 return $result;
845 * Removes part_of_admin_tree object with internal name $name.
847 * @param string $name The internal name of the object we want to remove.
848 * @return bool success
850 public function prune($name) {
852 if ($this->name == $name) {
853 return false; //can not remove itself
856 foreach($this->children as $precedence => $child) {
857 if ($child->name == $name) {
858 // clear cache and delete self
859 while($this->category_cache) {
860 // delete the cache, but keep the original array address
861 array_pop($this->category_cache);
863 unset($this->children[$precedence]);
864 return true;
865 } else if ($this->children[$precedence]->prune($name)) {
866 return true;
869 return false;
873 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
875 * By default the new part of the tree is appended as the last child of the parent. You
876 * can specify a sibling node that the new part should be prepended to. If the given
877 * sibling is not found, the part is appended to the end (as it would be by default) and
878 * a developer debugging message is displayed.
880 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
881 * @param string $destinationame The internal name of the immediate parent that we want for $something.
882 * @param mixed $something A part_of_admin_tree or setting instance to be added.
883 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
884 * @return bool True if successfully added, false if $something can not be added.
886 public function add($parentname, $something, $beforesibling = null) {
887 global $CFG;
889 $parent = $this->locate($parentname);
890 if (is_null($parent)) {
891 debugging('parent does not exist!');
892 return false;
895 if ($something instanceof part_of_admin_tree) {
896 if (!($parent instanceof parentable_part_of_admin_tree)) {
897 debugging('error - parts of tree can be inserted only into parentable parts');
898 return false;
900 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
901 // The name of the node is already used, simply warn the developer that this should not happen.
902 // It is intentional to check for the debug level before performing the check.
903 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
905 if (is_null($beforesibling)) {
906 // Append $something as the parent's last child.
907 $parent->children[] = $something;
908 } else {
909 if (!is_string($beforesibling) or trim($beforesibling) === '') {
910 throw new coding_exception('Unexpected value of the beforesibling parameter');
912 // Try to find the position of the sibling.
913 $siblingposition = null;
914 foreach ($parent->children as $childposition => $child) {
915 if ($child->name === $beforesibling) {
916 $siblingposition = $childposition;
917 break;
920 if (is_null($siblingposition)) {
921 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
922 $parent->children[] = $something;
923 } else {
924 $parent->children = array_merge(
925 array_slice($parent->children, 0, $siblingposition),
926 array($something),
927 array_slice($parent->children, $siblingposition)
931 if ($something instanceof admin_category) {
932 if (isset($this->category_cache[$something->name])) {
933 debugging('Duplicate admin category name: '.$something->name);
934 } else {
935 $this->category_cache[$something->name] = $something;
936 $something->category_cache =& $this->category_cache;
937 foreach ($something->children as $child) {
938 // just in case somebody already added subcategories
939 if ($child instanceof admin_category) {
940 if (isset($this->category_cache[$child->name])) {
941 debugging('Duplicate admin category name: '.$child->name);
942 } else {
943 $this->category_cache[$child->name] = $child;
944 $child->category_cache =& $this->category_cache;
950 return true;
952 } else {
953 debugging('error - can not add this element');
954 return false;
960 * Checks if the user has access to anything in this category.
962 * @return bool True if the user has access to at least one child in this category, false otherwise.
964 public function check_access() {
965 foreach ($this->children as $child) {
966 if ($child->check_access()) {
967 return true;
970 return false;
974 * Is this category hidden in admin tree block?
976 * @return bool True if hidden
978 public function is_hidden() {
979 return $this->hidden;
983 * Show we display Save button at the page bottom?
984 * @return bool
986 public function show_save() {
987 foreach ($this->children as $child) {
988 if ($child->show_save()) {
989 return true;
992 return false;
998 * Root of admin settings tree, does not have any parent.
1000 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1002 class admin_root extends admin_category {
1003 /** @var array List of errors */
1004 public $errors;
1005 /** @var string search query */
1006 public $search;
1007 /** @var bool full tree flag - true means all settings required, false only pages required */
1008 public $fulltree;
1009 /** @var bool flag indicating loaded tree */
1010 public $loaded;
1011 /** @var mixed site custom defaults overriding defaults in settings files*/
1012 public $custom_defaults;
1015 * @param bool $fulltree true means all settings required,
1016 * false only pages required
1018 public function __construct($fulltree) {
1019 global $CFG;
1021 parent::__construct('root', get_string('administration'), false);
1022 $this->errors = array();
1023 $this->search = '';
1024 $this->fulltree = $fulltree;
1025 $this->loaded = false;
1027 $this->category_cache = array();
1029 // load custom defaults if found
1030 $this->custom_defaults = null;
1031 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1032 if (is_readable($defaultsfile)) {
1033 $defaults = array();
1034 include($defaultsfile);
1035 if (is_array($defaults) and count($defaults)) {
1036 $this->custom_defaults = $defaults;
1042 * Empties children array, and sets loaded to false
1044 * @param bool $requirefulltree
1046 public function purge_children($requirefulltree) {
1047 $this->children = array();
1048 $this->fulltree = ($requirefulltree || $this->fulltree);
1049 $this->loaded = false;
1050 //break circular dependencies - this helps PHP 5.2
1051 while($this->category_cache) {
1052 array_pop($this->category_cache);
1054 $this->category_cache = array();
1060 * Links external PHP pages into the admin tree.
1062 * See detailed usage example at the top of this document (adminlib.php)
1064 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1066 class admin_externalpage implements part_of_admin_tree {
1068 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1069 public $name;
1071 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1072 public $visiblename;
1074 /** @var string The external URL that we should link to when someone requests this external page. */
1075 public $url;
1077 /** @var string The role capability/permission a user must have to access this external page. */
1078 public $req_capability;
1080 /** @var object The context in which capability/permission should be checked, default is site context. */
1081 public $context;
1083 /** @var bool hidden in admin tree block. */
1084 public $hidden;
1086 /** @var mixed either string or array of string */
1087 public $path;
1089 /** @var array list of visible names of page parents */
1090 public $visiblepath;
1093 * Constructor for adding an external page into the admin tree.
1095 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1096 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1097 * @param string $url The external URL that we should link to when someone requests this external page.
1098 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1099 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1100 * @param stdClass $context The context the page relates to. Not sure what happens
1101 * if you specify something other than system or front page. Defaults to system.
1103 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1104 $this->name = $name;
1105 $this->visiblename = $visiblename;
1106 $this->url = $url;
1107 if (is_array($req_capability)) {
1108 $this->req_capability = $req_capability;
1109 } else {
1110 $this->req_capability = array($req_capability);
1112 $this->hidden = $hidden;
1113 $this->context = $context;
1117 * Returns a reference to the part_of_admin_tree object with internal name $name.
1119 * @param string $name The internal name of the object we want.
1120 * @param bool $findpath defaults to false
1121 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1123 public function locate($name, $findpath=false) {
1124 if ($this->name == $name) {
1125 if ($findpath) {
1126 $this->visiblepath = array($this->visiblename);
1127 $this->path = array($this->name);
1129 return $this;
1130 } else {
1131 $return = NULL;
1132 return $return;
1137 * This function always returns false, required function by interface
1139 * @param string $name
1140 * @return false
1142 public function prune($name) {
1143 return false;
1147 * Search using query
1149 * @param string $query
1150 * @return mixed array-object structure of found settings and pages
1152 public function search($query) {
1153 $found = false;
1154 if (strpos(strtolower($this->name), $query) !== false) {
1155 $found = true;
1156 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1157 $found = true;
1159 if ($found) {
1160 $result = new stdClass();
1161 $result->page = $this;
1162 $result->settings = array();
1163 return array($this->name => $result);
1164 } else {
1165 return array();
1170 * Determines if the current user has access to this external page based on $this->req_capability.
1172 * @return bool True if user has access, false otherwise.
1174 public function check_access() {
1175 global $CFG;
1176 $context = empty($this->context) ? context_system::instance() : $this->context;
1177 foreach($this->req_capability as $cap) {
1178 if (has_capability($cap, $context)) {
1179 return true;
1182 return false;
1186 * Is this external page hidden in admin tree block?
1188 * @return bool True if hidden
1190 public function is_hidden() {
1191 return $this->hidden;
1195 * Show we display Save button at the page bottom?
1196 * @return bool
1198 public function show_save() {
1199 return false;
1205 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1207 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1209 class admin_settingpage implements part_of_admin_tree {
1211 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1212 public $name;
1214 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1215 public $visiblename;
1217 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1218 public $settings;
1220 /** @var string The role capability/permission a user must have to access this external page. */
1221 public $req_capability;
1223 /** @var object The context in which capability/permission should be checked, default is site context. */
1224 public $context;
1226 /** @var bool hidden in admin tree block. */
1227 public $hidden;
1229 /** @var mixed string of paths or array of strings of paths */
1230 public $path;
1232 /** @var array list of visible names of page parents */
1233 public $visiblepath;
1236 * see admin_settingpage for details of this function
1238 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1239 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1240 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1241 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1242 * @param stdClass $context The context the page relates to. Not sure what happens
1243 * if you specify something other than system or front page. Defaults to system.
1245 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1246 $this->settings = new stdClass();
1247 $this->name = $name;
1248 $this->visiblename = $visiblename;
1249 if (is_array($req_capability)) {
1250 $this->req_capability = $req_capability;
1251 } else {
1252 $this->req_capability = array($req_capability);
1254 $this->hidden = $hidden;
1255 $this->context = $context;
1259 * see admin_category
1261 * @param string $name
1262 * @param bool $findpath
1263 * @return mixed Object (this) if name == this->name, else returns null
1265 public function locate($name, $findpath=false) {
1266 if ($this->name == $name) {
1267 if ($findpath) {
1268 $this->visiblepath = array($this->visiblename);
1269 $this->path = array($this->name);
1271 return $this;
1272 } else {
1273 $return = NULL;
1274 return $return;
1279 * Search string in settings page.
1281 * @param string $query
1282 * @return array
1284 public function search($query) {
1285 $found = array();
1287 foreach ($this->settings as $setting) {
1288 if ($setting->is_related($query)) {
1289 $found[] = $setting;
1293 if ($found) {
1294 $result = new stdClass();
1295 $result->page = $this;
1296 $result->settings = $found;
1297 return array($this->name => $result);
1300 $found = false;
1301 if (strpos(strtolower($this->name), $query) !== false) {
1302 $found = true;
1303 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1304 $found = true;
1306 if ($found) {
1307 $result = new stdClass();
1308 $result->page = $this;
1309 $result->settings = array();
1310 return array($this->name => $result);
1311 } else {
1312 return array();
1317 * This function always returns false, required by interface
1319 * @param string $name
1320 * @return bool Always false
1322 public function prune($name) {
1323 return false;
1327 * adds an admin_setting to this admin_settingpage
1329 * 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
1330 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1332 * @param object $setting is the admin_setting object you want to add
1333 * @return bool true if successful, false if not
1335 public function add($setting) {
1336 if (!($setting instanceof admin_setting)) {
1337 debugging('error - not a setting instance');
1338 return false;
1341 $this->settings->{$setting->name} = $setting;
1342 return true;
1346 * see admin_externalpage
1348 * @return bool Returns true for yes false for no
1350 public function check_access() {
1351 global $CFG;
1352 $context = empty($this->context) ? context_system::instance() : $this->context;
1353 foreach($this->req_capability as $cap) {
1354 if (has_capability($cap, $context)) {
1355 return true;
1358 return false;
1362 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1363 * @return string Returns an XHTML string
1365 public function output_html() {
1366 $adminroot = admin_get_root();
1367 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1368 foreach($this->settings as $setting) {
1369 $fullname = $setting->get_full_name();
1370 if (array_key_exists($fullname, $adminroot->errors)) {
1371 $data = $adminroot->errors[$fullname]->data;
1372 } else {
1373 $data = $setting->get_setting();
1374 // do not use defaults if settings not available - upgrade settings handles the defaults!
1376 $return .= $setting->output_html($data);
1378 $return .= '</fieldset>';
1379 return $return;
1383 * Is this settings page hidden in admin tree block?
1385 * @return bool True if hidden
1387 public function is_hidden() {
1388 return $this->hidden;
1392 * Show we display Save button at the page bottom?
1393 * @return bool
1395 public function show_save() {
1396 foreach($this->settings as $setting) {
1397 if (empty($setting->nosave)) {
1398 return true;
1401 return false;
1407 * Admin settings class. Only exists on setting pages.
1408 * Read & write happens at this level; no authentication.
1410 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1412 abstract class admin_setting {
1413 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1414 public $name;
1415 /** @var string localised name */
1416 public $visiblename;
1417 /** @var string localised long description in Markdown format */
1418 public $description;
1419 /** @var mixed Can be string or array of string */
1420 public $defaultsetting;
1421 /** @var string */
1422 public $updatedcallback;
1423 /** @var mixed can be String or Null. Null means main config table */
1424 public $plugin; // null means main config table
1425 /** @var bool true indicates this setting does not actually save anything, just information */
1426 public $nosave = false;
1427 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1428 public $affectsmodinfo = false;
1429 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1430 private $flags = array();
1433 * Constructor
1434 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1435 * or 'myplugin/mysetting' for ones in config_plugins.
1436 * @param string $visiblename localised name
1437 * @param string $description localised long description
1438 * @param mixed $defaultsetting string or array depending on implementation
1440 public function __construct($name, $visiblename, $description, $defaultsetting) {
1441 $this->parse_setting_name($name);
1442 $this->visiblename = $visiblename;
1443 $this->description = $description;
1444 $this->defaultsetting = $defaultsetting;
1448 * Generic function to add a flag to this admin setting.
1450 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1451 * @param bool $default - The default for the flag
1452 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1453 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1455 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1456 if (empty($this->flags[$shortname])) {
1457 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1458 } else {
1459 $this->flags[$shortname]->set_options($enabled, $default);
1464 * Set the enabled options flag on this admin setting.
1466 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1467 * @param bool $default - The default for the flag
1469 public function set_enabled_flag_options($enabled, $default) {
1470 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1474 * Set the advanced options flag on this admin setting.
1476 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1477 * @param bool $default - The default for the flag
1479 public function set_advanced_flag_options($enabled, $default) {
1480 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1485 * Set the locked options flag on this admin setting.
1487 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1488 * @param bool $default - The default for the flag
1490 public function set_locked_flag_options($enabled, $default) {
1491 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1495 * Get the currently saved value for a setting flag
1497 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1498 * @return bool
1500 public function get_setting_flag_value(admin_setting_flag $flag) {
1501 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1502 if (!isset($value)) {
1503 $value = $flag->get_default();
1506 return !empty($value);
1510 * Get the list of defaults for the flags on this setting.
1512 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1514 public function get_setting_flag_defaults(& $defaults) {
1515 foreach ($this->flags as $flag) {
1516 if ($flag->is_enabled() && $flag->get_default()) {
1517 $defaults[] = $flag->get_displayname();
1523 * Output the input fields for the advanced and locked flags on this setting.
1525 * @param bool $adv - The current value of the advanced flag.
1526 * @param bool $locked - The current value of the locked flag.
1527 * @return string $output - The html for the flags.
1529 public function output_setting_flags() {
1530 $output = '';
1532 foreach ($this->flags as $flag) {
1533 if ($flag->is_enabled()) {
1534 $output .= $flag->output_setting_flag($this);
1538 if (!empty($output)) {
1539 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1541 return $output;
1545 * Write the values of the flags for this admin setting.
1547 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1548 * @return bool - true if successful.
1550 public function write_setting_flags($data) {
1551 $result = true;
1552 foreach ($this->flags as $flag) {
1553 $result = $result && $flag->write_setting_flag($this, $data);
1555 return $result;
1559 * Set up $this->name and potentially $this->plugin
1561 * Set up $this->name and possibly $this->plugin based on whether $name looks
1562 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1563 * on the names, that is, output a developer debug warning if the name
1564 * contains anything other than [a-zA-Z0-9_]+.
1566 * @param string $name the setting name passed in to the constructor.
1568 private function parse_setting_name($name) {
1569 $bits = explode('/', $name);
1570 if (count($bits) > 2) {
1571 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1573 $this->name = array_pop($bits);
1574 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1575 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1577 if (!empty($bits)) {
1578 $this->plugin = array_pop($bits);
1579 if ($this->plugin === 'moodle') {
1580 $this->plugin = null;
1581 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1582 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1588 * Returns the fullname prefixed by the plugin
1589 * @return string
1591 public function get_full_name() {
1592 return 's_'.$this->plugin.'_'.$this->name;
1596 * Returns the ID string based on plugin and name
1597 * @return string
1599 public function get_id() {
1600 return 'id_s_'.$this->plugin.'_'.$this->name;
1604 * @param bool $affectsmodinfo If true, changes to this setting will
1605 * cause the course cache to be rebuilt
1607 public function set_affects_modinfo($affectsmodinfo) {
1608 $this->affectsmodinfo = $affectsmodinfo;
1612 * Returns the config if possible
1614 * @return mixed returns config if successful else null
1616 public function config_read($name) {
1617 global $CFG;
1618 if (!empty($this->plugin)) {
1619 $value = get_config($this->plugin, $name);
1620 return $value === false ? NULL : $value;
1622 } else {
1623 if (isset($CFG->$name)) {
1624 return $CFG->$name;
1625 } else {
1626 return NULL;
1632 * Used to set a config pair and log change
1634 * @param string $name
1635 * @param mixed $value Gets converted to string if not null
1636 * @return bool Write setting to config table
1638 public function config_write($name, $value) {
1639 global $DB, $USER, $CFG;
1641 if ($this->nosave) {
1642 return true;
1645 // make sure it is a real change
1646 $oldvalue = get_config($this->plugin, $name);
1647 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1648 $value = is_null($value) ? null : (string)$value;
1650 if ($oldvalue === $value) {
1651 return true;
1654 // store change
1655 set_config($name, $value, $this->plugin);
1657 // Some admin settings affect course modinfo
1658 if ($this->affectsmodinfo) {
1659 // Clear course cache for all courses
1660 rebuild_course_cache(0, true);
1663 add_to_config_log($name, $oldvalue, $value, $this->plugin);
1665 return true; // BC only
1669 * Returns current value of this setting
1670 * @return mixed array or string depending on instance, NULL means not set yet
1672 public abstract function get_setting();
1675 * Returns default setting if exists
1676 * @return mixed array or string depending on instance; NULL means no default, user must supply
1678 public function get_defaultsetting() {
1679 $adminroot = admin_get_root(false, false);
1680 if (!empty($adminroot->custom_defaults)) {
1681 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1682 if (isset($adminroot->custom_defaults[$plugin])) {
1683 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1684 return $adminroot->custom_defaults[$plugin][$this->name];
1688 return $this->defaultsetting;
1692 * Store new setting
1694 * @param mixed $data string or array, must not be NULL
1695 * @return string empty string if ok, string error message otherwise
1697 public abstract function write_setting($data);
1700 * Return part of form with setting
1701 * This function should always be overwritten
1703 * @param mixed $data array or string depending on setting
1704 * @param string $query
1705 * @return string
1707 public function output_html($data, $query='') {
1708 // should be overridden
1709 return;
1713 * Function called if setting updated - cleanup, cache reset, etc.
1714 * @param string $functionname Sets the function name
1715 * @return void
1717 public function set_updatedcallback($functionname) {
1718 $this->updatedcallback = $functionname;
1722 * Execute postupdatecallback if necessary.
1723 * @param mixed $original original value before write_setting()
1724 * @return bool true if changed, false if not.
1726 public function post_write_settings($original) {
1727 // Comparison must work for arrays too.
1728 if (serialize($original) === serialize($this->get_setting())) {
1729 return false;
1732 $callbackfunction = $this->updatedcallback;
1733 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
1734 $callbackfunction($this->get_full_name());
1736 return true;
1740 * Is setting related to query text - used when searching
1741 * @param string $query
1742 * @return bool
1744 public function is_related($query) {
1745 if (strpos(strtolower($this->name), $query) !== false) {
1746 return true;
1748 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1749 return true;
1751 if (strpos(core_text::strtolower($this->description), $query) !== false) {
1752 return true;
1754 $current = $this->get_setting();
1755 if (!is_null($current)) {
1756 if (is_string($current)) {
1757 if (strpos(core_text::strtolower($current), $query) !== false) {
1758 return true;
1762 $default = $this->get_defaultsetting();
1763 if (!is_null($default)) {
1764 if (is_string($default)) {
1765 if (strpos(core_text::strtolower($default), $query) !== false) {
1766 return true;
1770 return false;
1775 * An additional option that can be applied to an admin setting.
1776 * The currently supported options are 'ADVANCED' and 'LOCKED'.
1778 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1780 class admin_setting_flag {
1781 /** @var bool Flag to indicate if this option can be toggled for this setting */
1782 private $enabled = false;
1783 /** @var bool Flag to indicate if this option defaults to true or false */
1784 private $default = false;
1785 /** @var string Short string used to create setting name - e.g. 'adv' */
1786 private $shortname = '';
1787 /** @var string String used as the label for this flag */
1788 private $displayname = '';
1789 /** @const Checkbox for this flag is displayed in admin page */
1790 const ENABLED = true;
1791 /** @const Checkbox for this flag is not displayed in admin page */
1792 const DISABLED = false;
1795 * Constructor
1797 * @param bool $enabled Can this option can be toggled.
1798 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
1799 * @param bool $default The default checked state for this setting option.
1800 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
1801 * @param string $displayname The displayname of this flag. Used as a label for the flag.
1803 public function __construct($enabled, $default, $shortname, $displayname) {
1804 $this->shortname = $shortname;
1805 $this->displayname = $displayname;
1806 $this->set_options($enabled, $default);
1810 * Update the values of this setting options class
1812 * @param bool $enabled Can this option can be toggled.
1813 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
1814 * @param bool $default The default checked state for this setting option.
1816 public function set_options($enabled, $default) {
1817 $this->enabled = $enabled;
1818 $this->default = $default;
1822 * Should this option appear in the interface and be toggleable?
1824 * @return bool Is it enabled?
1826 public function is_enabled() {
1827 return $this->enabled;
1831 * Should this option be checked by default?
1833 * @return bool Is it on by default?
1835 public function get_default() {
1836 return $this->default;
1840 * Return the short name for this flag. e.g. 'adv' or 'locked'
1842 * @return string
1844 public function get_shortname() {
1845 return $this->shortname;
1849 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
1851 * @return string
1853 public function get_displayname() {
1854 return $this->displayname;
1858 * Save the submitted data for this flag - or set it to the default if $data is null.
1860 * @param admin_setting $setting - The admin setting for this flag
1861 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1862 * @return bool
1864 public function write_setting_flag(admin_setting $setting, $data) {
1865 $result = true;
1866 if ($this->is_enabled()) {
1867 if (!isset($data)) {
1868 $value = $this->get_default();
1869 } else {
1870 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
1872 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
1875 return $result;
1880 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
1882 * @param admin_setting $setting - The admin setting for this flag
1883 * @return string - The html for the checkbox.
1885 public function output_setting_flag(admin_setting $setting) {
1886 $value = $setting->get_setting_flag_value($this);
1887 $output = ' <input type="checkbox" class="form-checkbox" ' .
1888 ' id="' . $setting->get_id() . '_' . $this->get_shortname() . '" ' .
1889 ' name="' . $setting->get_full_name() . '_' . $this->get_shortname() . '" ' .
1890 ' value="1" ' . ($value ? 'checked="checked"' : '') . ' />' .
1891 ' <label for="' . $setting->get_id() . '_' . $this->get_shortname() . '">' .
1892 $this->get_displayname() .
1893 ' </label> ';
1894 return $output;
1900 * No setting - just heading and text.
1902 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1904 class admin_setting_heading extends admin_setting {
1907 * not a setting, just text
1908 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1909 * @param string $heading heading
1910 * @param string $information text in box
1912 public function __construct($name, $heading, $information) {
1913 $this->nosave = true;
1914 parent::__construct($name, $heading, $information, '');
1918 * Always returns true
1919 * @return bool Always returns true
1921 public function get_setting() {
1922 return true;
1926 * Always returns true
1927 * @return bool Always returns true
1929 public function get_defaultsetting() {
1930 return true;
1934 * Never write settings
1935 * @return string Always returns an empty string
1937 public function write_setting($data) {
1938 // do not write any setting
1939 return '';
1943 * Returns an HTML string
1944 * @return string Returns an HTML string
1946 public function output_html($data, $query='') {
1947 global $OUTPUT;
1948 $return = '';
1949 if ($this->visiblename != '') {
1950 $return .= $OUTPUT->heading($this->visiblename, 3, 'main');
1952 if ($this->description != '') {
1953 $return .= $OUTPUT->box(highlight($query, markdown_to_html($this->description)), 'generalbox formsettingheading');
1955 return $return;
1961 * The most flexibly setting, user is typing text
1963 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1965 class admin_setting_configtext extends admin_setting {
1967 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
1968 public $paramtype;
1969 /** @var int default field size */
1970 public $size;
1973 * Config text constructor
1975 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1976 * @param string $visiblename localised
1977 * @param string $description long localised info
1978 * @param string $defaultsetting
1979 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
1980 * @param int $size default field size
1982 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
1983 $this->paramtype = $paramtype;
1984 if (!is_null($size)) {
1985 $this->size = $size;
1986 } else {
1987 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
1989 parent::__construct($name, $visiblename, $description, $defaultsetting);
1993 * Return the setting
1995 * @return mixed returns config if successful else null
1997 public function get_setting() {
1998 return $this->config_read($this->name);
2001 public function write_setting($data) {
2002 if ($this->paramtype === PARAM_INT and $data === '') {
2003 // do not complain if '' used instead of 0
2004 $data = 0;
2006 // $data is a string
2007 $validated = $this->validate($data);
2008 if ($validated !== true) {
2009 return $validated;
2011 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2015 * Validate data before storage
2016 * @param string data
2017 * @return mixed true if ok string if error found
2019 public function validate($data) {
2020 // allow paramtype to be a custom regex if it is the form of /pattern/
2021 if (preg_match('#^/.*/$#', $this->paramtype)) {
2022 if (preg_match($this->paramtype, $data)) {
2023 return true;
2024 } else {
2025 return get_string('validateerror', 'admin');
2028 } else if ($this->paramtype === PARAM_RAW) {
2029 return true;
2031 } else {
2032 $cleaned = clean_param($data, $this->paramtype);
2033 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2034 return true;
2035 } else {
2036 return get_string('validateerror', 'admin');
2042 * Return an XHTML string for the setting
2043 * @return string Returns an XHTML string
2045 public function output_html($data, $query='') {
2046 $default = $this->get_defaultsetting();
2048 return format_admin_setting($this, $this->visiblename,
2049 '<div class="form-text defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" /></div>',
2050 $this->description, true, '', $default, $query);
2056 * General text area without html editor.
2058 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2060 class admin_setting_configtextarea extends admin_setting_configtext {
2061 private $rows;
2062 private $cols;
2065 * @param string $name
2066 * @param string $visiblename
2067 * @param string $description
2068 * @param mixed $defaultsetting string or array
2069 * @param mixed $paramtype
2070 * @param string $cols The number of columns to make the editor
2071 * @param string $rows The number of rows to make the editor
2073 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2074 $this->rows = $rows;
2075 $this->cols = $cols;
2076 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2080 * Returns an XHTML string for the editor
2082 * @param string $data
2083 * @param string $query
2084 * @return string XHTML string for the editor
2086 public function output_html($data, $query='') {
2087 $default = $this->get_defaultsetting();
2089 $defaultinfo = $default;
2090 if (!is_null($default) and $default !== '') {
2091 $defaultinfo = "\n".$default;
2094 return format_admin_setting($this, $this->visiblename,
2095 '<div class="form-textarea" ><textarea rows="'. $this->rows .'" cols="'. $this->cols .'" id="'. $this->get_id() .'" name="'. $this->get_full_name() .'" spellcheck="true">'. s($data) .'</textarea></div>',
2096 $this->description, true, '', $defaultinfo, $query);
2102 * General text area with html editor.
2104 class admin_setting_confightmleditor extends admin_setting_configtext {
2105 private $rows;
2106 private $cols;
2109 * @param string $name
2110 * @param string $visiblename
2111 * @param string $description
2112 * @param mixed $defaultsetting string or array
2113 * @param mixed $paramtype
2115 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2116 $this->rows = $rows;
2117 $this->cols = $cols;
2118 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2119 editors_head_setup();
2123 * Returns an XHTML string for the editor
2125 * @param string $data
2126 * @param string $query
2127 * @return string XHTML string for the editor
2129 public function output_html($data, $query='') {
2130 $default = $this->get_defaultsetting();
2132 $defaultinfo = $default;
2133 if (!is_null($default) and $default !== '') {
2134 $defaultinfo = "\n".$default;
2137 $editor = editors_get_preferred_editor(FORMAT_HTML);
2138 $editor->use_editor($this->get_id(), array('noclean'=>true));
2140 return format_admin_setting($this, $this->visiblename,
2141 '<div class="form-textarea"><textarea rows="'. $this->rows .'" cols="'. $this->cols .'" id="'. $this->get_id() .'" name="'. $this->get_full_name() .'" spellcheck="true">'. s($data) .'</textarea></div>',
2142 $this->description, true, '', $defaultinfo, $query);
2148 * Password field, allows unmasking of password
2150 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2152 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2154 * Constructor
2155 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2156 * @param string $visiblename localised
2157 * @param string $description long localised info
2158 * @param string $defaultsetting default password
2160 public function __construct($name, $visiblename, $description, $defaultsetting) {
2161 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2165 * Returns XHTML for the field
2166 * Writes Javascript into the HTML below right before the last div
2168 * @todo Make javascript available through newer methods if possible
2169 * @param string $data Value for the field
2170 * @param string $query Passed as final argument for format_admin_setting
2171 * @return string XHTML field
2173 public function output_html($data, $query='') {
2174 $id = $this->get_id();
2175 $unmask = get_string('unmaskpassword', 'form');
2176 $unmaskjs = '<script type="text/javascript">
2177 //<![CDATA[
2178 var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
2180 document.getElementById("'.$id.'").setAttribute("autocomplete", "off");
2182 var unmaskdiv = document.getElementById("'.$id.'unmaskdiv");
2184 var unmaskchb = document.createElement("input");
2185 unmaskchb.setAttribute("type", "checkbox");
2186 unmaskchb.setAttribute("id", "'.$id.'unmask");
2187 unmaskchb.onchange = function() {unmaskPassword("'.$id.'");};
2188 unmaskdiv.appendChild(unmaskchb);
2190 var unmasklbl = document.createElement("label");
2191 unmasklbl.innerHTML = "'.addslashes_js($unmask).'";
2192 if (is_ie) {
2193 unmasklbl.setAttribute("htmlFor", "'.$id.'unmask");
2194 } else {
2195 unmasklbl.setAttribute("for", "'.$id.'unmask");
2197 unmaskdiv.appendChild(unmasklbl);
2199 if (is_ie) {
2200 // ugly hack to work around the famous onchange IE bug
2201 unmaskchb.onclick = function() {this.blur();};
2202 unmaskdiv.onclick = function() {this.blur();};
2204 //]]>
2205 </script>';
2206 return format_admin_setting($this, $this->visiblename,
2207 '<div class="form-password"><input type="password" size="'.$this->size.'" id="'.$id.'" name="'.$this->get_full_name().'" value="'.s($data).'" /><div class="unmask" id="'.$id.'unmaskdiv"></div>'.$unmaskjs.'</div>',
2208 $this->description, true, '', NULL, $query);
2213 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2214 * Note: Only advanced makes sense right now - locked does not.
2216 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2218 class admin_setting_configempty extends admin_setting_configtext {
2221 * @param string $name
2222 * @param string $visiblename
2223 * @param string $description
2225 public function __construct($name, $visiblename, $description) {
2226 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2230 * Returns an XHTML string for the hidden field
2232 * @param string $data
2233 * @param string $query
2234 * @return string XHTML string for the editor
2236 public function output_html($data, $query='') {
2237 return format_admin_setting($this,
2238 $this->visiblename,
2239 '<div class="form-empty" >' .
2240 '<input type="hidden"' .
2241 ' id="'. $this->get_id() .'"' .
2242 ' name="'. $this->get_full_name() .'"' .
2243 ' value=""/></div>',
2244 $this->description,
2245 true,
2247 get_string('none'),
2248 $query);
2254 * Path to directory
2256 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2258 class admin_setting_configfile extends admin_setting_configtext {
2260 * Constructor
2261 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2262 * @param string $visiblename localised
2263 * @param string $description long localised info
2264 * @param string $defaultdirectory default directory location
2266 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2267 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2271 * Returns XHTML for the field
2273 * Returns XHTML for the field and also checks whether the file
2274 * specified in $data exists using file_exists()
2276 * @param string $data File name and path to use in value attr
2277 * @param string $query
2278 * @return string XHTML field
2280 public function output_html($data, $query='') {
2281 $default = $this->get_defaultsetting();
2283 if ($data) {
2284 if (file_exists($data)) {
2285 $executable = '<span class="pathok">&#x2714;</span>';
2286 } else {
2287 $executable = '<span class="patherror">&#x2718;</span>';
2289 } else {
2290 $executable = '';
2293 return format_admin_setting($this, $this->visiblename,
2294 '<div class="form-file defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" />'.$executable.'</div>',
2295 $this->description, true, '', $default, $query);
2298 * checks if execpatch has been disabled in config.php
2300 public function write_setting($data) {
2301 global $CFG;
2302 if (!empty($CFG->preventexecpath)) {
2303 return '';
2305 return parent::write_setting($data);
2311 * Path to executable file
2313 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2315 class admin_setting_configexecutable extends admin_setting_configfile {
2318 * Returns an XHTML field
2320 * @param string $data This is the value for the field
2321 * @param string $query
2322 * @return string XHTML field
2324 public function output_html($data, $query='') {
2325 global $CFG;
2326 $default = $this->get_defaultsetting();
2328 if ($data) {
2329 if (file_exists($data) and is_executable($data)) {
2330 $executable = '<span class="pathok">&#x2714;</span>';
2331 } else {
2332 $executable = '<span class="patherror">&#x2718;</span>';
2334 } else {
2335 $executable = '';
2337 if (!empty($CFG->preventexecpath)) {
2338 $this->visiblename .= '<div class="form-overridden">'.get_string('execpathnotallowed', 'admin').'</div>';
2341 return format_admin_setting($this, $this->visiblename,
2342 '<div class="form-file defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" />'.$executable.'</div>',
2343 $this->description, true, '', $default, $query);
2349 * Path to directory
2351 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2353 class admin_setting_configdirectory extends admin_setting_configfile {
2356 * Returns an XHTML field
2358 * @param string $data This is the value for the field
2359 * @param string $query
2360 * @return string XHTML
2362 public function output_html($data, $query='') {
2363 $default = $this->get_defaultsetting();
2365 if ($data) {
2366 if (file_exists($data) and is_dir($data)) {
2367 $executable = '<span class="pathok">&#x2714;</span>';
2368 } else {
2369 $executable = '<span class="patherror">&#x2718;</span>';
2371 } else {
2372 $executable = '';
2375 return format_admin_setting($this, $this->visiblename,
2376 '<div class="form-file defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" />'.$executable.'</div>',
2377 $this->description, true, '', $default, $query);
2383 * Checkbox
2385 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2387 class admin_setting_configcheckbox extends admin_setting {
2388 /** @var string Value used when checked */
2389 public $yes;
2390 /** @var string Value used when not checked */
2391 public $no;
2394 * Constructor
2395 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2396 * @param string $visiblename localised
2397 * @param string $description long localised info
2398 * @param string $defaultsetting
2399 * @param string $yes value used when checked
2400 * @param string $no value used when not checked
2402 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2403 parent::__construct($name, $visiblename, $description, $defaultsetting);
2404 $this->yes = (string)$yes;
2405 $this->no = (string)$no;
2409 * Retrieves the current setting using the objects name
2411 * @return string
2413 public function get_setting() {
2414 return $this->config_read($this->name);
2418 * Sets the value for the setting
2420 * Sets the value for the setting to either the yes or no values
2421 * of the object by comparing $data to yes
2423 * @param mixed $data Gets converted to str for comparison against yes value
2424 * @return string empty string or error
2426 public function write_setting($data) {
2427 if ((string)$data === $this->yes) { // convert to strings before comparison
2428 $data = $this->yes;
2429 } else {
2430 $data = $this->no;
2432 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2436 * Returns an XHTML checkbox field
2438 * @param string $data If $data matches yes then checkbox is checked
2439 * @param string $query
2440 * @return string XHTML field
2442 public function output_html($data, $query='') {
2443 $default = $this->get_defaultsetting();
2445 if (!is_null($default)) {
2446 if ((string)$default === $this->yes) {
2447 $defaultinfo = get_string('checkboxyes', 'admin');
2448 } else {
2449 $defaultinfo = get_string('checkboxno', 'admin');
2451 } else {
2452 $defaultinfo = NULL;
2455 if ((string)$data === $this->yes) { // convert to strings before comparison
2456 $checked = 'checked="checked"';
2457 } else {
2458 $checked = '';
2461 return format_admin_setting($this, $this->visiblename,
2462 '<div class="form-checkbox defaultsnext" ><input type="hidden" name="'.$this->get_full_name().'" value="'.s($this->no).'" /> '
2463 .'<input type="checkbox" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($this->yes).'" '.$checked.' /></div>',
2464 $this->description, true, '', $defaultinfo, $query);
2470 * Multiple checkboxes, each represents different value, stored in csv format
2472 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2474 class admin_setting_configmulticheckbox extends admin_setting {
2475 /** @var array Array of choices value=>label */
2476 public $choices;
2479 * Constructor: uses parent::__construct
2481 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2482 * @param string $visiblename localised
2483 * @param string $description long localised info
2484 * @param array $defaultsetting array of selected
2485 * @param array $choices array of $value=>$label for each checkbox
2487 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2488 $this->choices = $choices;
2489 parent::__construct($name, $visiblename, $description, $defaultsetting);
2493 * This public function may be used in ancestors for lazy loading of choices
2495 * @todo Check if this function is still required content commented out only returns true
2496 * @return bool true if loaded, false if error
2498 public function load_choices() {
2500 if (is_array($this->choices)) {
2501 return true;
2503 .... load choices here
2505 return true;
2509 * Is setting related to query text - used when searching
2511 * @param string $query
2512 * @return bool true on related, false on not or failure
2514 public function is_related($query) {
2515 if (!$this->load_choices() or empty($this->choices)) {
2516 return false;
2518 if (parent::is_related($query)) {
2519 return true;
2522 foreach ($this->choices as $desc) {
2523 if (strpos(core_text::strtolower($desc), $query) !== false) {
2524 return true;
2527 return false;
2531 * Returns the current setting if it is set
2533 * @return mixed null if null, else an array
2535 public function get_setting() {
2536 $result = $this->config_read($this->name);
2538 if (is_null($result)) {
2539 return NULL;
2541 if ($result === '') {
2542 return array();
2544 $enabled = explode(',', $result);
2545 $setting = array();
2546 foreach ($enabled as $option) {
2547 $setting[$option] = 1;
2549 return $setting;
2553 * Saves the setting(s) provided in $data
2555 * @param array $data An array of data, if not array returns empty str
2556 * @return mixed empty string on useless data or bool true=success, false=failed
2558 public function write_setting($data) {
2559 if (!is_array($data)) {
2560 return ''; // ignore it
2562 if (!$this->load_choices() or empty($this->choices)) {
2563 return '';
2565 unset($data['xxxxx']);
2566 $result = array();
2567 foreach ($data as $key => $value) {
2568 if ($value and array_key_exists($key, $this->choices)) {
2569 $result[] = $key;
2572 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
2576 * Returns XHTML field(s) as required by choices
2578 * Relies on data being an array should data ever be another valid vartype with
2579 * acceptable value this may cause a warning/error
2580 * if (!is_array($data)) would fix the problem
2582 * @todo Add vartype handling to ensure $data is an array
2584 * @param array $data An array of checked values
2585 * @param string $query
2586 * @return string XHTML field
2588 public function output_html($data, $query='') {
2589 if (!$this->load_choices() or empty($this->choices)) {
2590 return '';
2592 $default = $this->get_defaultsetting();
2593 if (is_null($default)) {
2594 $default = array();
2596 if (is_null($data)) {
2597 $data = array();
2599 $options = array();
2600 $defaults = array();
2601 foreach ($this->choices as $key=>$description) {
2602 if (!empty($data[$key])) {
2603 $checked = 'checked="checked"';
2604 } else {
2605 $checked = '';
2607 if (!empty($default[$key])) {
2608 $defaults[] = $description;
2611 $options[] = '<input type="checkbox" id="'.$this->get_id().'_'.$key.'" name="'.$this->get_full_name().'['.$key.']" value="1" '.$checked.' />'
2612 .'<label for="'.$this->get_id().'_'.$key.'">'.highlightfast($query, $description).'</label>';
2615 if (is_null($default)) {
2616 $defaultinfo = NULL;
2617 } else if (!empty($defaults)) {
2618 $defaultinfo = implode(', ', $defaults);
2619 } else {
2620 $defaultinfo = get_string('none');
2623 $return = '<div class="form-multicheckbox">';
2624 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2625 if ($options) {
2626 $return .= '<ul>';
2627 foreach ($options as $option) {
2628 $return .= '<li>'.$option.'</li>';
2630 $return .= '</ul>';
2632 $return .= '</div>';
2634 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
2641 * Multiple checkboxes 2, value stored as string 00101011
2643 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2645 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
2648 * Returns the setting if set
2650 * @return mixed null if not set, else an array of set settings
2652 public function get_setting() {
2653 $result = $this->config_read($this->name);
2654 if (is_null($result)) {
2655 return NULL;
2657 if (!$this->load_choices()) {
2658 return NULL;
2660 $result = str_pad($result, count($this->choices), '0');
2661 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
2662 $setting = array();
2663 foreach ($this->choices as $key=>$unused) {
2664 $value = array_shift($result);
2665 if ($value) {
2666 $setting[$key] = 1;
2669 return $setting;
2673 * Save setting(s) provided in $data param
2675 * @param array $data An array of settings to save
2676 * @return mixed empty string for bad data or bool true=>success, false=>error
2678 public function write_setting($data) {
2679 if (!is_array($data)) {
2680 return ''; // ignore it
2682 if (!$this->load_choices() or empty($this->choices)) {
2683 return '';
2685 $result = '';
2686 foreach ($this->choices as $key=>$unused) {
2687 if (!empty($data[$key])) {
2688 $result .= '1';
2689 } else {
2690 $result .= '0';
2693 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
2699 * Select one value from list
2701 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2703 class admin_setting_configselect extends admin_setting {
2704 /** @var array Array of choices value=>label */
2705 public $choices;
2708 * Constructor
2709 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2710 * @param string $visiblename localised
2711 * @param string $description long localised info
2712 * @param string|int $defaultsetting
2713 * @param array $choices array of $value=>$label for each selection
2715 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2716 $this->choices = $choices;
2717 parent::__construct($name, $visiblename, $description, $defaultsetting);
2721 * This function may be used in ancestors for lazy loading of choices
2723 * Override this method if loading of choices is expensive, such
2724 * as when it requires multiple db requests.
2726 * @return bool true if loaded, false if error
2728 public function load_choices() {
2730 if (is_array($this->choices)) {
2731 return true;
2733 .... load choices here
2735 return true;
2739 * Check if this is $query is related to a choice
2741 * @param string $query
2742 * @return bool true if related, false if not
2744 public function is_related($query) {
2745 if (parent::is_related($query)) {
2746 return true;
2748 if (!$this->load_choices()) {
2749 return false;
2751 foreach ($this->choices as $key=>$value) {
2752 if (strpos(core_text::strtolower($key), $query) !== false) {
2753 return true;
2755 if (strpos(core_text::strtolower($value), $query) !== false) {
2756 return true;
2759 return false;
2763 * Return the setting
2765 * @return mixed returns config if successful else null
2767 public function get_setting() {
2768 return $this->config_read($this->name);
2772 * Save a setting
2774 * @param string $data
2775 * @return string empty of error string
2777 public function write_setting($data) {
2778 if (!$this->load_choices() or empty($this->choices)) {
2779 return '';
2781 if (!array_key_exists($data, $this->choices)) {
2782 return ''; // ignore it
2785 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2789 * Returns XHTML select field
2791 * Ensure the options are loaded, and generate the XHTML for the select
2792 * element and any warning message. Separating this out from output_html
2793 * makes it easier to subclass this class.
2795 * @param string $data the option to show as selected.
2796 * @param string $current the currently selected option in the database, null if none.
2797 * @param string $default the default selected option.
2798 * @return array the HTML for the select element, and a warning message.
2800 public function output_select_html($data, $current, $default, $extraname = '') {
2801 if (!$this->load_choices() or empty($this->choices)) {
2802 return array('', '');
2805 $warning = '';
2806 if (is_null($current)) {
2807 // first run
2808 } else if (empty($current) and (array_key_exists('', $this->choices) or array_key_exists(0, $this->choices))) {
2809 // no warning
2810 } else if (!array_key_exists($current, $this->choices)) {
2811 $warning = get_string('warningcurrentsetting', 'admin', s($current));
2812 if (!is_null($default) and $data == $current) {
2813 $data = $default; // use default instead of first value when showing the form
2817 $selecthtml = '<select id="'.$this->get_id().'" name="'.$this->get_full_name().$extraname.'">';
2818 foreach ($this->choices as $key => $value) {
2819 // the string cast is needed because key may be integer - 0 is equal to most strings!
2820 $selecthtml .= '<option value="'.$key.'"'.((string)$key==$data ? ' selected="selected"' : '').'>'.$value.'</option>';
2822 $selecthtml .= '</select>';
2823 return array($selecthtml, $warning);
2827 * Returns XHTML select field and wrapping div(s)
2829 * @see output_select_html()
2831 * @param string $data the option to show as selected
2832 * @param string $query
2833 * @return string XHTML field and wrapping div
2835 public function output_html($data, $query='') {
2836 $default = $this->get_defaultsetting();
2837 $current = $this->get_setting();
2839 list($selecthtml, $warning) = $this->output_select_html($data, $current, $default);
2840 if (!$selecthtml) {
2841 return '';
2844 if (!is_null($default) and array_key_exists($default, $this->choices)) {
2845 $defaultinfo = $this->choices[$default];
2846 } else {
2847 $defaultinfo = NULL;
2850 $return = '<div class="form-select defaultsnext">' . $selecthtml . '</div>';
2852 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
2858 * Select multiple items from list
2860 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2862 class admin_setting_configmultiselect extends admin_setting_configselect {
2864 * Constructor
2865 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2866 * @param string $visiblename localised
2867 * @param string $description long localised info
2868 * @param array $defaultsetting array of selected items
2869 * @param array $choices array of $value=>$label for each list item
2871 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2872 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
2876 * Returns the select setting(s)
2878 * @return mixed null or array. Null if no settings else array of setting(s)
2880 public function get_setting() {
2881 $result = $this->config_read($this->name);
2882 if (is_null($result)) {
2883 return NULL;
2885 if ($result === '') {
2886 return array();
2888 return explode(',', $result);
2892 * Saves setting(s) provided through $data
2894 * Potential bug in the works should anyone call with this function
2895 * using a vartype that is not an array
2897 * @param array $data
2899 public function write_setting($data) {
2900 if (!is_array($data)) {
2901 return ''; //ignore it
2903 if (!$this->load_choices() or empty($this->choices)) {
2904 return '';
2907 unset($data['xxxxx']);
2909 $save = array();
2910 foreach ($data as $value) {
2911 if (!array_key_exists($value, $this->choices)) {
2912 continue; // ignore it
2914 $save[] = $value;
2917 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
2921 * Is setting related to query text - used when searching
2923 * @param string $query
2924 * @return bool true if related, false if not
2926 public function is_related($query) {
2927 if (!$this->load_choices() or empty($this->choices)) {
2928 return false;
2930 if (parent::is_related($query)) {
2931 return true;
2934 foreach ($this->choices as $desc) {
2935 if (strpos(core_text::strtolower($desc), $query) !== false) {
2936 return true;
2939 return false;
2943 * Returns XHTML multi-select field
2945 * @todo Add vartype handling to ensure $data is an array
2946 * @param array $data Array of values to select by default
2947 * @param string $query
2948 * @return string XHTML multi-select field
2950 public function output_html($data, $query='') {
2951 if (!$this->load_choices() or empty($this->choices)) {
2952 return '';
2954 $choices = $this->choices;
2955 $default = $this->get_defaultsetting();
2956 if (is_null($default)) {
2957 $default = array();
2959 if (is_null($data)) {
2960 $data = array();
2963 $defaults = array();
2964 $size = min(10, count($this->choices));
2965 $return = '<div class="form-select"><input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2966 $return .= '<select id="'.$this->get_id().'" name="'.$this->get_full_name().'[]" size="'.$size.'" multiple="multiple">';
2967 foreach ($this->choices as $key => $description) {
2968 if (in_array($key, $data)) {
2969 $selected = 'selected="selected"';
2970 } else {
2971 $selected = '';
2973 if (in_array($key, $default)) {
2974 $defaults[] = $description;
2977 $return .= '<option value="'.s($key).'" '.$selected.'>'.$description.'</option>';
2980 if (is_null($default)) {
2981 $defaultinfo = NULL;
2982 } if (!empty($defaults)) {
2983 $defaultinfo = implode(', ', $defaults);
2984 } else {
2985 $defaultinfo = get_string('none');
2988 $return .= '</select></div>';
2989 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
2994 * Time selector
2996 * This is a liiitle bit messy. we're using two selects, but we're returning
2997 * them as an array named after $name (so we only use $name2 internally for the setting)
2999 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3001 class admin_setting_configtime extends admin_setting {
3002 /** @var string Used for setting second select (minutes) */
3003 public $name2;
3006 * Constructor
3007 * @param string $hoursname setting for hours
3008 * @param string $minutesname setting for hours
3009 * @param string $visiblename localised
3010 * @param string $description long localised info
3011 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3013 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3014 $this->name2 = $minutesname;
3015 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3019 * Get the selected time
3021 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3023 public function get_setting() {
3024 $result1 = $this->config_read($this->name);
3025 $result2 = $this->config_read($this->name2);
3026 if (is_null($result1) or is_null($result2)) {
3027 return NULL;
3030 return array('h' => $result1, 'm' => $result2);
3034 * Store the time (hours and minutes)
3036 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3037 * @return bool true if success, false if not
3039 public function write_setting($data) {
3040 if (!is_array($data)) {
3041 return '';
3044 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3045 return ($result ? '' : get_string('errorsetting', 'admin'));
3049 * Returns XHTML time select fields
3051 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3052 * @param string $query
3053 * @return string XHTML time select fields and wrapping div(s)
3055 public function output_html($data, $query='') {
3056 $default = $this->get_defaultsetting();
3058 if (is_array($default)) {
3059 $defaultinfo = $default['h'].':'.$default['m'];
3060 } else {
3061 $defaultinfo = NULL;
3064 $return = '<div class="form-time defaultsnext">'.
3065 '<select id="'.$this->get_id().'h" name="'.$this->get_full_name().'[h]">';
3066 for ($i = 0; $i < 24; $i++) {
3067 $return .= '<option value="'.$i.'"'.($i == $data['h'] ? ' selected="selected"' : '').'>'.$i.'</option>';
3069 $return .= '</select>:<select id="'.$this->get_id().'m" name="'.$this->get_full_name().'[m]">';
3070 for ($i = 0; $i < 60; $i += 5) {
3071 $return .= '<option value="'.$i.'"'.($i == $data['m'] ? ' selected="selected"' : '').'>'.$i.'</option>';
3073 $return .= '</select></div>';
3074 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
3081 * Seconds duration setting.
3083 * @copyright 2012 Petr Skoda (http://skodak.org)
3084 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3086 class admin_setting_configduration extends admin_setting {
3088 /** @var int default duration unit */
3089 protected $defaultunit;
3092 * Constructor
3093 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3094 * or 'myplugin/mysetting' for ones in config_plugins.
3095 * @param string $visiblename localised name
3096 * @param string $description localised long description
3097 * @param mixed $defaultsetting string or array depending on implementation
3098 * @param int $defaultunit - day, week, etc. (in seconds)
3100 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3101 if (is_number($defaultsetting)) {
3102 $defaultsetting = self::parse_seconds($defaultsetting);
3104 $units = self::get_units();
3105 if (isset($units[$defaultunit])) {
3106 $this->defaultunit = $defaultunit;
3107 } else {
3108 $this->defaultunit = 86400;
3110 parent::__construct($name, $visiblename, $description, $defaultsetting);
3114 * Returns selectable units.
3115 * @static
3116 * @return array
3118 protected static function get_units() {
3119 return array(
3120 604800 => get_string('weeks'),
3121 86400 => get_string('days'),
3122 3600 => get_string('hours'),
3123 60 => get_string('minutes'),
3124 1 => get_string('seconds'),
3129 * Converts seconds to some more user friendly string.
3130 * @static
3131 * @param int $seconds
3132 * @return string
3134 protected static function get_duration_text($seconds) {
3135 if (empty($seconds)) {
3136 return get_string('none');
3138 $data = self::parse_seconds($seconds);
3139 switch ($data['u']) {
3140 case (60*60*24*7):
3141 return get_string('numweeks', '', $data['v']);
3142 case (60*60*24):
3143 return get_string('numdays', '', $data['v']);
3144 case (60*60):
3145 return get_string('numhours', '', $data['v']);
3146 case (60):
3147 return get_string('numminutes', '', $data['v']);
3148 default:
3149 return get_string('numseconds', '', $data['v']*$data['u']);
3154 * Finds suitable units for given duration.
3155 * @static
3156 * @param int $seconds
3157 * @return array
3159 protected static function parse_seconds($seconds) {
3160 foreach (self::get_units() as $unit => $unused) {
3161 if ($seconds % $unit === 0) {
3162 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3165 return array('v'=>(int)$seconds, 'u'=>1);
3169 * Get the selected duration as array.
3171 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3173 public function get_setting() {
3174 $seconds = $this->config_read($this->name);
3175 if (is_null($seconds)) {
3176 return null;
3179 return self::parse_seconds($seconds);
3183 * Store the duration as seconds.
3185 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3186 * @return bool true if success, false if not
3188 public function write_setting($data) {
3189 if (!is_array($data)) {
3190 return '';
3193 $seconds = (int)($data['v']*$data['u']);
3194 if ($seconds < 0) {
3195 return get_string('errorsetting', 'admin');
3198 $result = $this->config_write($this->name, $seconds);
3199 return ($result ? '' : get_string('errorsetting', 'admin'));
3203 * Returns duration text+select fields.
3205 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3206 * @param string $query
3207 * @return string duration text+select fields and wrapping div(s)
3209 public function output_html($data, $query='') {
3210 $default = $this->get_defaultsetting();
3212 if (is_number($default)) {
3213 $defaultinfo = self::get_duration_text($default);
3214 } else if (is_array($default)) {
3215 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3216 } else {
3217 $defaultinfo = null;
3220 $units = self::get_units();
3222 $return = '<div class="form-duration defaultsnext">';
3223 $return .= '<input type="text" size="5" id="'.$this->get_id().'v" name="'.$this->get_full_name().'[v]" value="'.s($data['v']).'" />';
3224 $return .= '<select id="'.$this->get_id().'u" name="'.$this->get_full_name().'[u]">';
3225 foreach ($units as $val => $text) {
3226 $selected = '';
3227 if ($data['v'] == 0) {
3228 if ($val == $this->defaultunit) {
3229 $selected = ' selected="selected"';
3231 } else if ($val == $data['u']) {
3232 $selected = ' selected="selected"';
3234 $return .= '<option value="'.$val.'"'.$selected.'>'.$text.'</option>';
3236 $return .= '</select></div>';
3237 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
3243 * Used to validate a textarea used for ip addresses
3245 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3247 class admin_setting_configiplist extends admin_setting_configtextarea {
3250 * Validate the contents of the textarea as IP addresses
3252 * Used to validate a new line separated list of IP addresses collected from
3253 * a textarea control
3255 * @param string $data A list of IP Addresses separated by new lines
3256 * @return mixed bool true for success or string:error on failure
3258 public function validate($data) {
3259 if(!empty($data)) {
3260 $ips = explode("\n", $data);
3261 } else {
3262 return true;
3264 $result = true;
3265 foreach($ips as $ip) {
3266 $ip = trim($ip);
3267 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3268 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3269 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3270 $result = true;
3271 } else {
3272 $result = false;
3273 break;
3276 if($result) {
3277 return true;
3278 } else {
3279 return get_string('validateerror', 'admin');
3286 * An admin setting for selecting one or more users who have a capability
3287 * in the system context
3289 * An admin setting for selecting one or more users, who have a particular capability
3290 * in the system context. Warning, make sure the list will never be too long. There is
3291 * no paging or searching of this list.
3293 * To correctly get a list of users from this config setting, you need to call the
3294 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
3296 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3298 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
3299 /** @var string The capabilities name */
3300 protected $capability;
3301 /** @var int include admin users too */
3302 protected $includeadmins;
3305 * Constructor.
3307 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3308 * @param string $visiblename localised name
3309 * @param string $description localised long description
3310 * @param array $defaultsetting array of usernames
3311 * @param string $capability string capability name.
3312 * @param bool $includeadmins include administrators
3314 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
3315 $this->capability = $capability;
3316 $this->includeadmins = $includeadmins;
3317 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
3321 * Load all of the uses who have the capability into choice array
3323 * @return bool Always returns true
3325 function load_choices() {
3326 if (is_array($this->choices)) {
3327 return true;
3329 list($sort, $sortparams) = users_order_by_sql('u');
3330 if (!empty($sortparams)) {
3331 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
3332 'This is unexpected, and a problem because there is no way to pass these ' .
3333 'parameters to get_users_by_capability. See MDL-34657.');
3335 $userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
3336 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
3337 $this->choices = array(
3338 '$@NONE@$' => get_string('nobody'),
3339 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
3341 if ($this->includeadmins) {
3342 $admins = get_admins();
3343 foreach ($admins as $user) {
3344 $this->choices[$user->id] = fullname($user);
3347 if (is_array($users)) {
3348 foreach ($users as $user) {
3349 $this->choices[$user->id] = fullname($user);
3352 return true;
3356 * Returns the default setting for class
3358 * @return mixed Array, or string. Empty string if no default
3360 public function get_defaultsetting() {
3361 $this->load_choices();
3362 $defaultsetting = parent::get_defaultsetting();
3363 if (empty($defaultsetting)) {
3364 return array('$@NONE@$');
3365 } else if (array_key_exists($defaultsetting, $this->choices)) {
3366 return $defaultsetting;
3367 } else {
3368 return '';
3373 * Returns the current setting
3375 * @return mixed array or string
3377 public function get_setting() {
3378 $result = parent::get_setting();
3379 if ($result === null) {
3380 // this is necessary for settings upgrade
3381 return null;
3383 if (empty($result)) {
3384 $result = array('$@NONE@$');
3386 return $result;
3390 * Save the chosen setting provided as $data
3392 * @param array $data
3393 * @return mixed string or array
3395 public function write_setting($data) {
3396 // If all is selected, remove any explicit options.
3397 if (in_array('$@ALL@$', $data)) {
3398 $data = array('$@ALL@$');
3400 // None never needs to be written to the DB.
3401 if (in_array('$@NONE@$', $data)) {
3402 unset($data[array_search('$@NONE@$', $data)]);
3404 return parent::write_setting($data);
3410 * Special checkbox for calendar - resets SESSION vars.
3412 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3414 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
3416 * Calls the parent::__construct with default values
3418 * name => calendar_adminseesall
3419 * visiblename => get_string('adminseesall', 'admin')
3420 * description => get_string('helpadminseesall', 'admin')
3421 * defaultsetting => 0
3423 public function __construct() {
3424 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
3425 get_string('helpadminseesall', 'admin'), '0');
3429 * Stores the setting passed in $data
3431 * @param mixed gets converted to string for comparison
3432 * @return string empty string or error message
3434 public function write_setting($data) {
3435 global $SESSION;
3436 return parent::write_setting($data);
3441 * Special select for settings that are altered in setup.php and can not be altered on the fly
3443 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3445 class admin_setting_special_selectsetup extends admin_setting_configselect {
3447 * Reads the setting directly from the database
3449 * @return mixed
3451 public function get_setting() {
3452 // read directly from db!
3453 return get_config(NULL, $this->name);
3457 * Save the setting passed in $data
3459 * @param string $data The setting to save
3460 * @return string empty or error message
3462 public function write_setting($data) {
3463 global $CFG;
3464 // do not change active CFG setting!
3465 $current = $CFG->{$this->name};
3466 $result = parent::write_setting($data);
3467 $CFG->{$this->name} = $current;
3468 return $result;
3474 * Special select for frontpage - stores data in course table
3476 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3478 class admin_setting_sitesetselect extends admin_setting_configselect {
3480 * Returns the site name for the selected site
3482 * @see get_site()
3483 * @return string The site name of the selected site
3485 public function get_setting() {
3486 $site = course_get_format(get_site())->get_course();
3487 return $site->{$this->name};
3491 * Updates the database and save the setting
3493 * @param string data
3494 * @return string empty or error message
3496 public function write_setting($data) {
3497 global $DB, $SITE, $COURSE;
3498 if (!in_array($data, array_keys($this->choices))) {
3499 return get_string('errorsetting', 'admin');
3501 $record = new stdClass();
3502 $record->id = SITEID;
3503 $temp = $this->name;
3504 $record->$temp = $data;
3505 $record->timemodified = time();
3507 course_get_format($SITE)->update_course_format_options($record);
3508 $DB->update_record('course', $record);
3510 // Reset caches.
3511 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3512 if ($SITE->id == $COURSE->id) {
3513 $COURSE = $SITE;
3515 format_base::reset_course_cache($SITE->id);
3517 return '';
3524 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
3525 * block to hidden.
3527 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3529 class admin_setting_bloglevel extends admin_setting_configselect {
3531 * Updates the database and save the setting
3533 * @param string data
3534 * @return string empty or error message
3536 public function write_setting($data) {
3537 global $DB, $CFG;
3538 if ($data == 0) {
3539 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
3540 foreach ($blogblocks as $block) {
3541 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
3543 } else {
3544 // reenable all blocks only when switching from disabled blogs
3545 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
3546 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
3547 foreach ($blogblocks as $block) {
3548 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
3552 return parent::write_setting($data);
3558 * Special select - lists on the frontpage - hacky
3560 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3562 class admin_setting_courselist_frontpage extends admin_setting {
3563 /** @var array Array of choices value=>label */
3564 public $choices;
3567 * Construct override, requires one param
3569 * @param bool $loggedin Is the user logged in
3571 public function __construct($loggedin) {
3572 global $CFG;
3573 require_once($CFG->dirroot.'/course/lib.php');
3574 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
3575 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
3576 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
3577 $defaults = array(FRONTPAGEALLCOURSELIST);
3578 parent::__construct($name, $visiblename, $description, $defaults);
3582 * Loads the choices available
3584 * @return bool always returns true
3586 public function load_choices() {
3587 if (is_array($this->choices)) {
3588 return true;
3590 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
3591 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
3592 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
3593 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
3594 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
3595 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
3596 'none' => get_string('none'));
3597 if ($this->name === 'frontpage') {
3598 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
3600 return true;
3604 * Returns the selected settings
3606 * @param mixed array or setting or null
3608 public function get_setting() {
3609 $result = $this->config_read($this->name);
3610 if (is_null($result)) {
3611 return NULL;
3613 if ($result === '') {
3614 return array();
3616 return explode(',', $result);
3620 * Save the selected options
3622 * @param array $data
3623 * @return mixed empty string (data is not an array) or bool true=success false=failure
3625 public function write_setting($data) {
3626 if (!is_array($data)) {
3627 return '';
3629 $this->load_choices();
3630 $save = array();
3631 foreach($data as $datum) {
3632 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
3633 continue;
3635 $save[$datum] = $datum; // no duplicates
3637 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3641 * Return XHTML select field and wrapping div
3643 * @todo Add vartype handling to make sure $data is an array
3644 * @param array $data Array of elements to select by default
3645 * @return string XHTML select field and wrapping div
3647 public function output_html($data, $query='') {
3648 $this->load_choices();
3649 $currentsetting = array();
3650 foreach ($data as $key) {
3651 if ($key != 'none' and array_key_exists($key, $this->choices)) {
3652 $currentsetting[] = $key; // already selected first
3656 $return = '<div class="form-group">';
3657 for ($i = 0; $i < count($this->choices) - 1; $i++) {
3658 if (!array_key_exists($i, $currentsetting)) {
3659 $currentsetting[$i] = 'none'; //none
3661 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
3662 foreach ($this->choices as $key => $value) {
3663 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
3665 $return .= '</select>';
3666 if ($i !== count($this->choices) - 2) {
3667 $return .= '<br />';
3670 $return .= '</div>';
3672 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3678 * Special checkbox for frontpage - stores data in course table
3680 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3682 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
3684 * Returns the current sites name
3686 * @return string
3688 public function get_setting() {
3689 $site = course_get_format(get_site())->get_course();
3690 return $site->{$this->name};
3694 * Save the selected setting
3696 * @param string $data The selected site
3697 * @return string empty string or error message
3699 public function write_setting($data) {
3700 global $DB, $SITE, $COURSE;
3701 $record = new stdClass();
3702 $record->id = $SITE->id;
3703 $record->{$this->name} = ($data == '1' ? 1 : 0);
3704 $record->timemodified = time();
3706 course_get_format($SITE)->update_course_format_options($record);
3707 $DB->update_record('course', $record);
3709 // Reset caches.
3710 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3711 if ($SITE->id == $COURSE->id) {
3712 $COURSE = $SITE;
3714 format_base::reset_course_cache($SITE->id);
3716 return '';
3721 * Special text for frontpage - stores data in course table.
3722 * Empty string means not set here. Manual setting is required.
3724 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3726 class admin_setting_sitesettext extends admin_setting_configtext {
3728 * Return the current setting
3730 * @return mixed string or null
3732 public function get_setting() {
3733 $site = course_get_format(get_site())->get_course();
3734 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
3738 * Validate the selected data
3740 * @param string $data The selected value to validate
3741 * @return mixed true or message string
3743 public function validate($data) {
3744 $cleaned = clean_param($data, PARAM_TEXT);
3745 if ($cleaned === '') {
3746 return get_string('required');
3748 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
3749 return true;
3750 } else {
3751 return get_string('validateerror', 'admin');
3756 * Save the selected setting
3758 * @param string $data The selected value
3759 * @return string empty or error message
3761 public function write_setting($data) {
3762 global $DB, $SITE, $COURSE;
3763 $data = trim($data);
3764 $validated = $this->validate($data);
3765 if ($validated !== true) {
3766 return $validated;
3769 $record = new stdClass();
3770 $record->id = $SITE->id;
3771 $record->{$this->name} = $data;
3772 $record->timemodified = time();
3774 course_get_format($SITE)->update_course_format_options($record);
3775 $DB->update_record('course', $record);
3777 // Reset caches.
3778 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3779 if ($SITE->id == $COURSE->id) {
3780 $COURSE = $SITE;
3782 format_base::reset_course_cache($SITE->id);
3784 return '';
3790 * Special text editor for site description.
3792 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3794 class admin_setting_special_frontpagedesc extends admin_setting {
3796 * Calls parent::__construct with specific arguments
3798 public function __construct() {
3799 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
3800 editors_head_setup();
3804 * Return the current setting
3805 * @return string The current setting
3807 public function get_setting() {
3808 $site = course_get_format(get_site())->get_course();
3809 return $site->{$this->name};
3813 * Save the new setting
3815 * @param string $data The new value to save
3816 * @return string empty or error message
3818 public function write_setting($data) {
3819 global $DB, $SITE, $COURSE;
3820 $record = new stdClass();
3821 $record->id = $SITE->id;
3822 $record->{$this->name} = $data;
3823 $record->timemodified = time();
3825 course_get_format($SITE)->update_course_format_options($record);
3826 $DB->update_record('course', $record);
3828 // Reset caches.
3829 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3830 if ($SITE->id == $COURSE->id) {
3831 $COURSE = $SITE;
3833 format_base::reset_course_cache($SITE->id);
3835 return '';
3839 * Returns XHTML for the field plus wrapping div
3841 * @param string $data The current value
3842 * @param string $query
3843 * @return string The XHTML output
3845 public function output_html($data, $query='') {
3846 global $CFG;
3848 $return = '<div class="form-htmlarea">'.print_textarea(true, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
3850 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3856 * Administration interface for emoticon_manager settings.
3858 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3860 class admin_setting_emoticons extends admin_setting {
3863 * Calls parent::__construct with specific args
3865 public function __construct() {
3866 global $CFG;
3868 $manager = get_emoticon_manager();
3869 $defaults = $this->prepare_form_data($manager->default_emoticons());
3870 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
3874 * Return the current setting(s)
3876 * @return array Current settings array
3878 public function get_setting() {
3879 global $CFG;
3881 $manager = get_emoticon_manager();
3883 $config = $this->config_read($this->name);
3884 if (is_null($config)) {
3885 return null;
3888 $config = $manager->decode_stored_config($config);
3889 if (is_null($config)) {
3890 return null;
3893 return $this->prepare_form_data($config);
3897 * Save selected settings
3899 * @param array $data Array of settings to save
3900 * @return bool
3902 public function write_setting($data) {
3904 $manager = get_emoticon_manager();
3905 $emoticons = $this->process_form_data($data);
3907 if ($emoticons === false) {
3908 return false;
3911 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
3912 return ''; // success
3913 } else {
3914 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
3919 * Return XHTML field(s) for options
3921 * @param array $data Array of options to set in HTML
3922 * @return string XHTML string for the fields and wrapping div(s)
3924 public function output_html($data, $query='') {
3925 global $OUTPUT;
3927 $out = html_writer::start_tag('table', array('id' => 'emoticonsetting', 'class' => 'admintable generaltable'));
3928 $out .= html_writer::start_tag('thead');
3929 $out .= html_writer::start_tag('tr');
3930 $out .= html_writer::tag('th', get_string('emoticontext', 'admin'));
3931 $out .= html_writer::tag('th', get_string('emoticonimagename', 'admin'));
3932 $out .= html_writer::tag('th', get_string('emoticoncomponent', 'admin'));
3933 $out .= html_writer::tag('th', get_string('emoticonalt', 'admin'), array('colspan' => 2));
3934 $out .= html_writer::tag('th', '');
3935 $out .= html_writer::end_tag('tr');
3936 $out .= html_writer::end_tag('thead');
3937 $out .= html_writer::start_tag('tbody');
3938 $i = 0;
3939 foreach($data as $field => $value) {
3940 switch ($i) {
3941 case 0:
3942 $out .= html_writer::start_tag('tr');
3943 $current_text = $value;
3944 $current_filename = '';
3945 $current_imagecomponent = '';
3946 $current_altidentifier = '';
3947 $current_altcomponent = '';
3948 case 1:
3949 $current_filename = $value;
3950 case 2:
3951 $current_imagecomponent = $value;
3952 case 3:
3953 $current_altidentifier = $value;
3954 case 4:
3955 $current_altcomponent = $value;
3958 $out .= html_writer::tag('td',
3959 html_writer::empty_tag('input',
3960 array(
3961 'type' => 'text',
3962 'class' => 'form-text',
3963 'name' => $this->get_full_name().'['.$field.']',
3964 'value' => $value,
3966 ), array('class' => 'c'.$i)
3969 if ($i == 4) {
3970 if (get_string_manager()->string_exists($current_altidentifier, $current_altcomponent)) {
3971 $alt = get_string($current_altidentifier, $current_altcomponent);
3972 } else {
3973 $alt = $current_text;
3975 if ($current_filename) {
3976 $out .= html_writer::tag('td', $OUTPUT->render(new pix_emoticon($current_filename, $alt, $current_imagecomponent)));
3977 } else {
3978 $out .= html_writer::tag('td', '');
3980 $out .= html_writer::end_tag('tr');
3981 $i = 0;
3982 } else {
3983 $i++;
3987 $out .= html_writer::end_tag('tbody');
3988 $out .= html_writer::end_tag('table');
3989 $out = html_writer::tag('div', $out, array('class' => 'form-group'));
3990 $out .= html_writer::tag('div', html_writer::link(new moodle_url('/admin/resetemoticons.php'), get_string('emoticonsreset', 'admin')));
3992 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', NULL, $query);
3996 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
3998 * @see self::process_form_data()
3999 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4000 * @return array of form fields and their values
4002 protected function prepare_form_data(array $emoticons) {
4004 $form = array();
4005 $i = 0;
4006 foreach ($emoticons as $emoticon) {
4007 $form['text'.$i] = $emoticon->text;
4008 $form['imagename'.$i] = $emoticon->imagename;
4009 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
4010 $form['altidentifier'.$i] = $emoticon->altidentifier;
4011 $form['altcomponent'.$i] = $emoticon->altcomponent;
4012 $i++;
4014 // add one more blank field set for new object
4015 $form['text'.$i] = '';
4016 $form['imagename'.$i] = '';
4017 $form['imagecomponent'.$i] = '';
4018 $form['altidentifier'.$i] = '';
4019 $form['altcomponent'.$i] = '';
4021 return $form;
4025 * Converts the data from admin settings form into an array of emoticon objects
4027 * @see self::prepare_form_data()
4028 * @param array $data array of admin form fields and values
4029 * @return false|array of emoticon objects
4031 protected function process_form_data(array $form) {
4033 $count = count($form); // number of form field values
4035 if ($count % 5) {
4036 // we must get five fields per emoticon object
4037 return false;
4040 $emoticons = array();
4041 for ($i = 0; $i < $count / 5; $i++) {
4042 $emoticon = new stdClass();
4043 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
4044 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
4045 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
4046 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
4047 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
4049 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
4050 // prevent from breaking http://url.addresses by accident
4051 $emoticon->text = '';
4054 if (strlen($emoticon->text) < 2) {
4055 // do not allow single character emoticons
4056 $emoticon->text = '';
4059 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
4060 // emoticon text must contain some non-alphanumeric character to prevent
4061 // breaking HTML tags
4062 $emoticon->text = '';
4065 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
4066 $emoticons[] = $emoticon;
4069 return $emoticons;
4075 * Special setting for limiting of the list of available languages.
4077 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4079 class admin_setting_langlist extends admin_setting_configtext {
4081 * Calls parent::__construct with specific arguments
4083 public function __construct() {
4084 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4088 * Save the new setting
4090 * @param string $data The new setting
4091 * @return bool
4093 public function write_setting($data) {
4094 $return = parent::write_setting($data);
4095 get_string_manager()->reset_caches();
4096 return $return;
4102 * Selection of one of the recognised countries using the list
4103 * returned by {@link get_list_of_countries()}.
4105 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4107 class admin_settings_country_select extends admin_setting_configselect {
4108 protected $includeall;
4109 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
4110 $this->includeall = $includeall;
4111 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4115 * Lazy-load the available choices for the select box
4117 public function load_choices() {
4118 global $CFG;
4119 if (is_array($this->choices)) {
4120 return true;
4122 $this->choices = array_merge(
4123 array('0' => get_string('choosedots')),
4124 get_string_manager()->get_list_of_countries($this->includeall));
4125 return true;
4131 * admin_setting_configselect for the default number of sections in a course,
4132 * simply so we can lazy-load the choices.
4134 * @copyright 2011 The Open University
4135 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4137 class admin_settings_num_course_sections extends admin_setting_configselect {
4138 public function __construct($name, $visiblename, $description, $defaultsetting) {
4139 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
4142 /** Lazy-load the available choices for the select box */
4143 public function load_choices() {
4144 $max = get_config('moodlecourse', 'maxsections');
4145 if (!isset($max) || !is_numeric($max)) {
4146 $max = 52;
4148 for ($i = 0; $i <= $max; $i++) {
4149 $this->choices[$i] = "$i";
4151 return true;
4157 * Course category selection
4159 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4161 class admin_settings_coursecat_select extends admin_setting_configselect {
4163 * Calls parent::__construct with specific arguments
4165 public function __construct($name, $visiblename, $description, $defaultsetting) {
4166 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4170 * Load the available choices for the select box
4172 * @return bool
4174 public function load_choices() {
4175 global $CFG;
4176 require_once($CFG->dirroot.'/course/lib.php');
4177 if (is_array($this->choices)) {
4178 return true;
4180 $this->choices = make_categories_options();
4181 return true;
4187 * Special control for selecting days to backup
4189 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4191 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
4193 * Calls parent::__construct with specific arguments
4195 public function __construct() {
4196 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
4197 $this->plugin = 'backup';
4201 * Load the available choices for the select box
4203 * @return bool Always returns true
4205 public function load_choices() {
4206 if (is_array($this->choices)) {
4207 return true;
4209 $this->choices = array();
4210 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
4211 foreach ($days as $day) {
4212 $this->choices[$day] = get_string($day, 'calendar');
4214 return true;
4220 * Special debug setting
4222 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4224 class admin_setting_special_debug extends admin_setting_configselect {
4226 * Calls parent::__construct with specific arguments
4228 public function __construct() {
4229 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
4233 * Load the available choices for the select box
4235 * @return bool
4237 public function load_choices() {
4238 if (is_array($this->choices)) {
4239 return true;
4241 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
4242 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
4243 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
4244 DEBUG_ALL => get_string('debugall', 'admin'),
4245 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
4246 return true;
4252 * Special admin control
4254 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4256 class admin_setting_special_calendar_weekend extends admin_setting {
4258 * Calls parent::__construct with specific arguments
4260 public function __construct() {
4261 $name = 'calendar_weekend';
4262 $visiblename = get_string('calendar_weekend', 'admin');
4263 $description = get_string('helpweekenddays', 'admin');
4264 $default = array ('0', '6'); // Saturdays and Sundays
4265 parent::__construct($name, $visiblename, $description, $default);
4269 * Gets the current settings as an array
4271 * @return mixed Null if none, else array of settings
4273 public function get_setting() {
4274 $result = $this->config_read($this->name);
4275 if (is_null($result)) {
4276 return NULL;
4278 if ($result === '') {
4279 return array();
4281 $settings = array();
4282 for ($i=0; $i<7; $i++) {
4283 if ($result & (1 << $i)) {
4284 $settings[] = $i;
4287 return $settings;
4291 * Save the new settings
4293 * @param array $data Array of new settings
4294 * @return bool
4296 public function write_setting($data) {
4297 if (!is_array($data)) {
4298 return '';
4300 unset($data['xxxxx']);
4301 $result = 0;
4302 foreach($data as $index) {
4303 $result |= 1 << $index;
4305 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
4309 * Return XHTML to display the control
4311 * @param array $data array of selected days
4312 * @param string $query
4313 * @return string XHTML for display (field + wrapping div(s)
4315 public function output_html($data, $query='') {
4316 // The order matters very much because of the implied numeric keys
4317 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
4318 $return = '<table><thead><tr>';
4319 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
4320 foreach($days as $index => $day) {
4321 $return .= '<td><label for="'.$this->get_id().$index.'">'.get_string($day, 'calendar').'</label></td>';
4323 $return .= '</tr></thead><tbody><tr>';
4324 foreach($days as $index => $day) {
4325 $return .= '<td><input type="checkbox" class="form-checkbox" id="'.$this->get_id().$index.'" name="'.$this->get_full_name().'[]" value="'.$index.'" '.(in_array("$index", $data) ? 'checked="checked"' : '').' /></td>';
4327 $return .= '</tr></tbody></table>';
4329 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
4336 * Admin setting that allows a user to pick a behaviour.
4338 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4340 class admin_setting_question_behaviour extends admin_setting_configselect {
4342 * @param string $name name of config variable
4343 * @param string $visiblename display name
4344 * @param string $description description
4345 * @param string $default default.
4347 public function __construct($name, $visiblename, $description, $default) {
4348 parent::__construct($name, $visiblename, $description, $default, NULL);
4352 * Load list of behaviours as choices
4353 * @return bool true => success, false => error.
4355 public function load_choices() {
4356 global $CFG;
4357 require_once($CFG->dirroot . '/question/engine/lib.php');
4358 $this->choices = question_engine::get_behaviour_options('');
4359 return true;
4365 * Admin setting that allows a user to pick appropriate roles for something.
4367 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4369 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
4370 /** @var array Array of capabilities which identify roles */
4371 private $types;
4374 * @param string $name Name of config variable
4375 * @param string $visiblename Display name
4376 * @param string $description Description
4377 * @param array $types Array of archetypes which identify
4378 * roles that will be enabled by default.
4380 public function __construct($name, $visiblename, $description, $types) {
4381 parent::__construct($name, $visiblename, $description, NULL, NULL);
4382 $this->types = $types;
4386 * Load roles as choices
4388 * @return bool true=>success, false=>error
4390 public function load_choices() {
4391 global $CFG, $DB;
4392 if (during_initial_install()) {
4393 return false;
4395 if (is_array($this->choices)) {
4396 return true;
4398 if ($roles = get_all_roles()) {
4399 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
4400 return true;
4401 } else {
4402 return false;
4407 * Return the default setting for this control
4409 * @return array Array of default settings
4411 public function get_defaultsetting() {
4412 global $CFG;
4414 if (during_initial_install()) {
4415 return null;
4417 $result = array();
4418 foreach($this->types as $archetype) {
4419 if ($caproles = get_archetype_roles($archetype)) {
4420 foreach ($caproles as $caprole) {
4421 $result[$caprole->id] = 1;
4425 return $result;
4431 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
4433 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4435 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
4437 * Constructor
4438 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4439 * @param string $visiblename localised
4440 * @param string $description long localised info
4441 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
4442 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
4443 * @param int $size default field size
4445 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
4446 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
4447 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4453 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
4455 * @copyright 2009 Petr Skoda (http://skodak.org)
4456 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4458 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
4461 * Constructor
4462 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4463 * @param string $visiblename localised
4464 * @param string $description long localised info
4465 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
4466 * @param string $yes value used when checked
4467 * @param string $no value used when not checked
4469 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4470 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
4471 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4478 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
4480 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
4482 * @copyright 2010 Sam Hemelryk
4483 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4485 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
4487 * Constructor
4488 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4489 * @param string $visiblename localised
4490 * @param string $description long localised info
4491 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
4492 * @param string $yes value used when checked
4493 * @param string $no value used when not checked
4495 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4496 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
4497 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
4504 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
4506 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4508 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
4510 * Calls parent::__construct with specific arguments
4512 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4513 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
4514 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4521 * Graded roles in gradebook
4523 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4525 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
4527 * Calls parent::__construct with specific arguments
4529 public function __construct() {
4530 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
4531 get_string('configgradebookroles', 'admin'),
4532 array('student'));
4539 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4541 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
4543 * Saves the new settings passed in $data
4545 * @param string $data
4546 * @return mixed string or Array
4548 public function write_setting($data) {
4549 global $CFG, $DB;
4551 $oldvalue = $this->config_read($this->name);
4552 $return = parent::write_setting($data);
4553 $newvalue = $this->config_read($this->name);
4555 if ($oldvalue !== $newvalue) {
4556 // force full regrading
4557 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
4560 return $return;
4566 * Which roles to show on course description page
4568 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4570 class admin_setting_special_coursecontact extends admin_setting_pickroles {
4572 * Calls parent::__construct with specific arguments
4574 public function __construct() {
4575 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
4576 get_string('coursecontact_desc', 'admin'),
4577 array('editingteacher'));
4584 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4586 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
4588 * Calls parent::__construct with specific arguments
4590 function admin_setting_special_gradelimiting() {
4591 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
4592 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
4596 * Force site regrading
4598 function regrade_all() {
4599 global $CFG;
4600 require_once("$CFG->libdir/gradelib.php");
4601 grade_force_site_regrading();
4605 * Saves the new settings
4607 * @param mixed $data
4608 * @return string empty string or error message
4610 function write_setting($data) {
4611 $previous = $this->get_setting();
4613 if ($previous === null) {
4614 if ($data) {
4615 $this->regrade_all();
4617 } else {
4618 if ($data != $previous) {
4619 $this->regrade_all();
4622 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4629 * Primary grade export plugin - has state tracking.
4631 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4633 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
4635 * Calls parent::__construct with specific arguments
4637 public function __construct() {
4638 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
4639 get_string('configgradeexport', 'admin'), array(), NULL);
4643 * Load the available choices for the multicheckbox
4645 * @return bool always returns true
4647 public function load_choices() {
4648 if (is_array($this->choices)) {
4649 return true;
4651 $this->choices = array();
4653 if ($plugins = core_component::get_plugin_list('gradeexport')) {
4654 foreach($plugins as $plugin => $unused) {
4655 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
4658 return true;
4664 * Grade category settings
4666 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4668 class admin_setting_gradecat_combo extends admin_setting {
4669 /** @var array Array of choices */
4670 public $choices;
4673 * Sets choices and calls parent::__construct with passed arguments
4674 * @param string $name
4675 * @param string $visiblename
4676 * @param string $description
4677 * @param mixed $defaultsetting string or array depending on implementation
4678 * @param array $choices An array of choices for the control
4680 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4681 $this->choices = $choices;
4682 parent::__construct($name, $visiblename, $description, $defaultsetting);
4686 * Return the current setting(s) array
4688 * @return array Array of value=>xx, forced=>xx, adv=>xx
4690 public function get_setting() {
4691 global $CFG;
4693 $value = $this->config_read($this->name);
4694 $flag = $this->config_read($this->name.'_flag');
4696 if (is_null($value) or is_null($flag)) {
4697 return NULL;
4700 $flag = (int)$flag;
4701 $forced = (boolean)(1 & $flag); // first bit
4702 $adv = (boolean)(2 & $flag); // second bit
4704 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
4708 * Save the new settings passed in $data
4710 * @todo Add vartype handling to ensure $data is array
4711 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4712 * @return string empty or error message
4714 public function write_setting($data) {
4715 global $CFG;
4717 $value = $data['value'];
4718 $forced = empty($data['forced']) ? 0 : 1;
4719 $adv = empty($data['adv']) ? 0 : 2;
4720 $flag = ($forced | $adv); //bitwise or
4722 if (!in_array($value, array_keys($this->choices))) {
4723 return 'Error setting ';
4726 $oldvalue = $this->config_read($this->name);
4727 $oldflag = (int)$this->config_read($this->name.'_flag');
4728 $oldforced = (1 & $oldflag); // first bit
4730 $result1 = $this->config_write($this->name, $value);
4731 $result2 = $this->config_write($this->name.'_flag', $flag);
4733 // force regrade if needed
4734 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
4735 require_once($CFG->libdir.'/gradelib.php');
4736 grade_category::updated_forced_settings();
4739 if ($result1 and $result2) {
4740 return '';
4741 } else {
4742 return get_string('errorsetting', 'admin');
4747 * Return XHTML to display the field and wrapping div
4749 * @todo Add vartype handling to ensure $data is array
4750 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4751 * @param string $query
4752 * @return string XHTML to display control
4754 public function output_html($data, $query='') {
4755 $value = $data['value'];
4756 $forced = !empty($data['forced']);
4757 $adv = !empty($data['adv']);
4759 $default = $this->get_defaultsetting();
4760 if (!is_null($default)) {
4761 $defaultinfo = array();
4762 if (isset($this->choices[$default['value']])) {
4763 $defaultinfo[] = $this->choices[$default['value']];
4765 if (!empty($default['forced'])) {
4766 $defaultinfo[] = get_string('force');
4768 if (!empty($default['adv'])) {
4769 $defaultinfo[] = get_string('advanced');
4771 $defaultinfo = implode(', ', $defaultinfo);
4773 } else {
4774 $defaultinfo = NULL;
4778 $return = '<div class="form-group">';
4779 $return .= '<select class="form-select" id="'.$this->get_id().'" name="'.$this->get_full_name().'[value]">';
4780 foreach ($this->choices as $key => $val) {
4781 // the string cast is needed because key may be integer - 0 is equal to most strings!
4782 $return .= '<option value="'.$key.'"'.((string)$key==$value ? ' selected="selected"' : '').'>'.$val.'</option>';
4784 $return .= '</select>';
4785 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'force" name="'.$this->get_full_name().'[forced]" value="1" '.($forced ? 'checked="checked"' : '').' />'
4786 .'<label for="'.$this->get_id().'force">'.get_string('force').'</label>';
4787 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'adv" name="'.$this->get_full_name().'[adv]" value="1" '.($adv ? 'checked="checked"' : '').' />'
4788 .'<label for="'.$this->get_id().'adv">'.get_string('advanced').'</label>';
4789 $return .= '</div>';
4791 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
4797 * Selection of grade report in user profiles
4799 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4801 class admin_setting_grade_profilereport extends admin_setting_configselect {
4803 * Calls parent::__construct with specific arguments
4805 public function __construct() {
4806 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
4810 * Loads an array of choices for the configselect control
4812 * @return bool always return true
4814 public function load_choices() {
4815 if (is_array($this->choices)) {
4816 return true;
4818 $this->choices = array();
4820 global $CFG;
4821 require_once($CFG->libdir.'/gradelib.php');
4823 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
4824 if (file_exists($plugindir.'/lib.php')) {
4825 require_once($plugindir.'/lib.php');
4826 $functionname = 'grade_report_'.$plugin.'_profilereport';
4827 if (function_exists($functionname)) {
4828 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
4832 return true;
4838 * Special class for register auth selection
4840 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4842 class admin_setting_special_registerauth extends admin_setting_configselect {
4844 * Calls parent::__construct with specific arguments
4846 public function __construct() {
4847 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
4851 * Returns the default option
4853 * @return string empty or default option
4855 public function get_defaultsetting() {
4856 $this->load_choices();
4857 $defaultsetting = parent::get_defaultsetting();
4858 if (array_key_exists($defaultsetting, $this->choices)) {
4859 return $defaultsetting;
4860 } else {
4861 return '';
4866 * Loads the possible choices for the array
4868 * @return bool always returns true
4870 public function load_choices() {
4871 global $CFG;
4873 if (is_array($this->choices)) {
4874 return true;
4876 $this->choices = array();
4877 $this->choices[''] = get_string('disable');
4879 $authsenabled = get_enabled_auth_plugins(true);
4881 foreach ($authsenabled as $auth) {
4882 $authplugin = get_auth_plugin($auth);
4883 if (!$authplugin->can_signup()) {
4884 continue;
4886 // Get the auth title (from core or own auth lang files)
4887 $authtitle = $authplugin->get_title();
4888 $this->choices[$auth] = $authtitle;
4890 return true;
4896 * General plugins manager
4898 class admin_page_pluginsoverview extends admin_externalpage {
4901 * Sets basic information about the external page
4903 public function __construct() {
4904 global $CFG;
4905 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
4906 "$CFG->wwwroot/$CFG->admin/plugins.php");
4911 * Module manage page
4913 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4915 class admin_page_managemods extends admin_externalpage {
4917 * Calls parent::__construct with specific arguments
4919 public function __construct() {
4920 global $CFG;
4921 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
4925 * Try to find the specified module
4927 * @param string $query The module to search for
4928 * @return array
4930 public function search($query) {
4931 global $CFG, $DB;
4932 if ($result = parent::search($query)) {
4933 return $result;
4936 $found = false;
4937 if ($modules = $DB->get_records('modules')) {
4938 foreach ($modules as $module) {
4939 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
4940 continue;
4942 if (strpos($module->name, $query) !== false) {
4943 $found = true;
4944 break;
4946 $strmodulename = get_string('modulename', $module->name);
4947 if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
4948 $found = true;
4949 break;
4953 if ($found) {
4954 $result = new stdClass();
4955 $result->page = $this;
4956 $result->settings = array();
4957 return array($this->name => $result);
4958 } else {
4959 return array();
4966 * Special class for enrol plugins management.
4968 * @copyright 2010 Petr Skoda {@link http://skodak.org}
4969 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4971 class admin_setting_manageenrols extends admin_setting {
4973 * Calls parent::__construct with specific arguments
4975 public function __construct() {
4976 $this->nosave = true;
4977 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
4981 * Always returns true, does nothing
4983 * @return true
4985 public function get_setting() {
4986 return true;
4990 * Always returns true, does nothing
4992 * @return true
4994 public function get_defaultsetting() {
4995 return true;
4999 * Always returns '', does not write anything
5001 * @return string Always returns ''
5003 public function write_setting($data) {
5004 // do not write any setting
5005 return '';
5009 * Checks if $query is one of the available enrol plugins
5011 * @param string $query The string to search for
5012 * @return bool Returns true if found, false if not
5014 public function is_related($query) {
5015 if (parent::is_related($query)) {
5016 return true;
5019 $query = core_text::strtolower($query);
5020 $enrols = enrol_get_plugins(false);
5021 foreach ($enrols as $name=>$enrol) {
5022 $localised = get_string('pluginname', 'enrol_'.$name);
5023 if (strpos(core_text::strtolower($name), $query) !== false) {
5024 return true;
5026 if (strpos(core_text::strtolower($localised), $query) !== false) {
5027 return true;
5030 return false;
5034 * Builds the XHTML to display the control
5036 * @param string $data Unused
5037 * @param string $query
5038 * @return string
5040 public function output_html($data, $query='') {
5041 global $CFG, $OUTPUT, $DB, $PAGE;
5043 // Display strings.
5044 $strup = get_string('up');
5045 $strdown = get_string('down');
5046 $strsettings = get_string('settings');
5047 $strenable = get_string('enable');
5048 $strdisable = get_string('disable');
5049 $struninstall = get_string('uninstallplugin', 'core_admin');
5050 $strusage = get_string('enrolusage', 'enrol');
5051 $strversion = get_string('version');
5052 $strtest = get_string('testsettings', 'core_enrol');
5054 $pluginmanager = core_plugin_manager::instance();
5056 $enrols_available = enrol_get_plugins(false);
5057 $active_enrols = enrol_get_plugins(true);
5059 $allenrols = array();
5060 foreach ($active_enrols as $key=>$enrol) {
5061 $allenrols[$key] = true;
5063 foreach ($enrols_available as $key=>$enrol) {
5064 $allenrols[$key] = true;
5066 // Now find all borked plugins and at least allow then to uninstall.
5067 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
5068 foreach ($condidates as $candidate) {
5069 if (empty($allenrols[$candidate])) {
5070 $allenrols[$candidate] = true;
5074 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
5075 $return .= $OUTPUT->box_start('generalbox enrolsui');
5077 $table = new html_table();
5078 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
5079 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
5080 $table->id = 'courseenrolmentplugins';
5081 $table->attributes['class'] = 'admintable generaltable';
5082 $table->data = array();
5084 // Iterate through enrol plugins and add to the display table.
5085 $updowncount = 1;
5086 $enrolcount = count($active_enrols);
5087 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
5088 $printed = array();
5089 foreach($allenrols as $enrol => $unused) {
5090 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
5091 $version = get_config('enrol_'.$enrol, 'version');
5092 if ($version === false) {
5093 $version = '';
5096 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
5097 $name = get_string('pluginname', 'enrol_'.$enrol);
5098 } else {
5099 $name = $enrol;
5101 // Usage.
5102 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
5103 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
5104 $usage = "$ci / $cp";
5106 // Hide/show links.
5107 $class = '';
5108 if (isset($active_enrols[$enrol])) {
5109 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
5110 $hideshow = "<a href=\"$aurl\">";
5111 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
5112 $enabled = true;
5113 $displayname = $name;
5114 } else if (isset($enrols_available[$enrol])) {
5115 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
5116 $hideshow = "<a href=\"$aurl\">";
5117 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
5118 $enabled = false;
5119 $displayname = $name;
5120 $class = 'dimmed_text';
5121 } else {
5122 $hideshow = '';
5123 $enabled = false;
5124 $displayname = '<span class="notifyproblem">'.$name.'</span>';
5126 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
5127 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
5128 } else {
5129 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
5132 // Up/down link (only if enrol is enabled).
5133 $updown = '';
5134 if ($enabled) {
5135 if ($updowncount > 1) {
5136 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
5137 $updown .= "<a href=\"$aurl\">";
5138 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" class=\"iconsmall\" /></a>&nbsp;";
5139 } else {
5140 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5142 if ($updowncount < $enrolcount) {
5143 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
5144 $updown .= "<a href=\"$aurl\">";
5145 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" class=\"iconsmall\" /></a>";
5146 } else {
5147 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5149 ++$updowncount;
5152 // Add settings link.
5153 if (!$version) {
5154 $settings = '';
5155 } else if ($surl = $plugininfo->get_settings_url()) {
5156 $settings = html_writer::link($surl, $strsettings);
5157 } else {
5158 $settings = '';
5161 // Add uninstall info.
5162 $uninstall = '';
5163 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
5164 $uninstall = html_writer::link($uninstallurl, $struninstall);
5167 $test = '';
5168 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
5169 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
5170 $test = html_writer::link($testsettingsurl, $strtest);
5173 // Add a row to the table.
5174 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
5175 if ($class) {
5176 $row->attributes['class'] = $class;
5178 $table->data[] = $row;
5180 $printed[$enrol] = true;
5183 $return .= html_writer::table($table);
5184 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
5185 $return .= $OUTPUT->box_end();
5186 return highlight($query, $return);
5192 * Blocks manage page
5194 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5196 class admin_page_manageblocks extends admin_externalpage {
5198 * Calls parent::__construct with specific arguments
5200 public function __construct() {
5201 global $CFG;
5202 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
5206 * Search for a specific block
5208 * @param string $query The string to search for
5209 * @return array
5211 public function search($query) {
5212 global $CFG, $DB;
5213 if ($result = parent::search($query)) {
5214 return $result;
5217 $found = false;
5218 if ($blocks = $DB->get_records('block')) {
5219 foreach ($blocks as $block) {
5220 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
5221 continue;
5223 if (strpos($block->name, $query) !== false) {
5224 $found = true;
5225 break;
5227 $strblockname = get_string('pluginname', 'block_'.$block->name);
5228 if (strpos(core_text::strtolower($strblockname), $query) !== false) {
5229 $found = true;
5230 break;
5234 if ($found) {
5235 $result = new stdClass();
5236 $result->page = $this;
5237 $result->settings = array();
5238 return array($this->name => $result);
5239 } else {
5240 return array();
5246 * Message outputs configuration
5248 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5250 class admin_page_managemessageoutputs extends admin_externalpage {
5252 * Calls parent::__construct with specific arguments
5254 public function __construct() {
5255 global $CFG;
5256 parent::__construct('managemessageoutputs', get_string('managemessageoutputs', 'message'), new moodle_url('/admin/message.php'));
5260 * Search for a specific message processor
5262 * @param string $query The string to search for
5263 * @return array
5265 public function search($query) {
5266 global $CFG, $DB;
5267 if ($result = parent::search($query)) {
5268 return $result;
5271 $found = false;
5272 if ($processors = get_message_processors()) {
5273 foreach ($processors as $processor) {
5274 if (!$processor->available) {
5275 continue;
5277 if (strpos($processor->name, $query) !== false) {
5278 $found = true;
5279 break;
5281 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
5282 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
5283 $found = true;
5284 break;
5288 if ($found) {
5289 $result = new stdClass();
5290 $result->page = $this;
5291 $result->settings = array();
5292 return array($this->name => $result);
5293 } else {
5294 return array();
5300 * Default message outputs configuration
5302 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5304 class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
5306 * Calls parent::__construct with specific arguments
5308 public function __construct() {
5309 global $CFG;
5310 admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
5316 * Manage question behaviours page
5318 * @copyright 2011 The Open University
5319 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5321 class admin_page_manageqbehaviours extends admin_externalpage {
5323 * Constructor
5325 public function __construct() {
5326 global $CFG;
5327 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
5328 new moodle_url('/admin/qbehaviours.php'));
5332 * Search question behaviours for the specified string
5334 * @param string $query The string to search for in question behaviours
5335 * @return array
5337 public function search($query) {
5338 global $CFG;
5339 if ($result = parent::search($query)) {
5340 return $result;
5343 $found = false;
5344 require_once($CFG->dirroot . '/question/engine/lib.php');
5345 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
5346 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
5347 $query) !== false) {
5348 $found = true;
5349 break;
5352 if ($found) {
5353 $result = new stdClass();
5354 $result->page = $this;
5355 $result->settings = array();
5356 return array($this->name => $result);
5357 } else {
5358 return array();
5365 * Question type manage page
5367 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5369 class admin_page_manageqtypes extends admin_externalpage {
5371 * Calls parent::__construct with specific arguments
5373 public function __construct() {
5374 global $CFG;
5375 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
5376 new moodle_url('/admin/qtypes.php'));
5380 * Search question types for the specified string
5382 * @param string $query The string to search for in question types
5383 * @return array
5385 public function search($query) {
5386 global $CFG;
5387 if ($result = parent::search($query)) {
5388 return $result;
5391 $found = false;
5392 require_once($CFG->dirroot . '/question/engine/bank.php');
5393 foreach (question_bank::get_all_qtypes() as $qtype) {
5394 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
5395 $found = true;
5396 break;
5399 if ($found) {
5400 $result = new stdClass();
5401 $result->page = $this;
5402 $result->settings = array();
5403 return array($this->name => $result);
5404 } else {
5405 return array();
5411 class admin_page_manageportfolios extends admin_externalpage {
5413 * Calls parent::__construct with specific arguments
5415 public function __construct() {
5416 global $CFG;
5417 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
5418 "$CFG->wwwroot/$CFG->admin/portfolio.php");
5422 * Searches page for the specified string.
5423 * @param string $query The string to search for
5424 * @return bool True if it is found on this page
5426 public function search($query) {
5427 global $CFG;
5428 if ($result = parent::search($query)) {
5429 return $result;
5432 $found = false;
5433 $portfolios = core_component::get_plugin_list('portfolio');
5434 foreach ($portfolios as $p => $dir) {
5435 if (strpos($p, $query) !== false) {
5436 $found = true;
5437 break;
5440 if (!$found) {
5441 foreach (portfolio_instances(false, false) as $instance) {
5442 $title = $instance->get('name');
5443 if (strpos(core_text::strtolower($title), $query) !== false) {
5444 $found = true;
5445 break;
5450 if ($found) {
5451 $result = new stdClass();
5452 $result->page = $this;
5453 $result->settings = array();
5454 return array($this->name => $result);
5455 } else {
5456 return array();
5462 class admin_page_managerepositories extends admin_externalpage {
5464 * Calls parent::__construct with specific arguments
5466 public function __construct() {
5467 global $CFG;
5468 parent::__construct('managerepositories', get_string('manage',
5469 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
5473 * Searches page for the specified string.
5474 * @param string $query The string to search for
5475 * @return bool True if it is found on this page
5477 public function search($query) {
5478 global $CFG;
5479 if ($result = parent::search($query)) {
5480 return $result;
5483 $found = false;
5484 $repositories= core_component::get_plugin_list('repository');
5485 foreach ($repositories as $p => $dir) {
5486 if (strpos($p, $query) !== false) {
5487 $found = true;
5488 break;
5491 if (!$found) {
5492 foreach (repository::get_types() as $instance) {
5493 $title = $instance->get_typename();
5494 if (strpos(core_text::strtolower($title), $query) !== false) {
5495 $found = true;
5496 break;
5501 if ($found) {
5502 $result = new stdClass();
5503 $result->page = $this;
5504 $result->settings = array();
5505 return array($this->name => $result);
5506 } else {
5507 return array();
5514 * Special class for authentication administration.
5516 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5518 class admin_setting_manageauths extends admin_setting {
5520 * Calls parent::__construct with specific arguments
5522 public function __construct() {
5523 $this->nosave = true;
5524 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
5528 * Always returns true
5530 * @return true
5532 public function get_setting() {
5533 return true;
5537 * Always returns true
5539 * @return true
5541 public function get_defaultsetting() {
5542 return true;
5546 * Always returns '' and doesn't write anything
5548 * @return string Always returns ''
5550 public function write_setting($data) {
5551 // do not write any setting
5552 return '';
5556 * Search to find if Query is related to auth plugin
5558 * @param string $query The string to search for
5559 * @return bool true for related false for not
5561 public function is_related($query) {
5562 if (parent::is_related($query)) {
5563 return true;
5566 $authsavailable = core_component::get_plugin_list('auth');
5567 foreach ($authsavailable as $auth => $dir) {
5568 if (strpos($auth, $query) !== false) {
5569 return true;
5571 $authplugin = get_auth_plugin($auth);
5572 $authtitle = $authplugin->get_title();
5573 if (strpos(core_text::strtolower($authtitle), $query) !== false) {
5574 return true;
5577 return false;
5581 * Return XHTML to display control
5583 * @param mixed $data Unused
5584 * @param string $query
5585 * @return string highlight
5587 public function output_html($data, $query='') {
5588 global $CFG, $OUTPUT, $DB;
5590 // display strings
5591 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
5592 'settings', 'edit', 'name', 'enable', 'disable',
5593 'up', 'down', 'none', 'users'));
5594 $txt->updown = "$txt->up/$txt->down";
5595 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
5596 $txt->testsettings = get_string('testsettings', 'core_auth');
5598 $authsavailable = core_component::get_plugin_list('auth');
5599 get_enabled_auth_plugins(true); // fix the list of enabled auths
5600 if (empty($CFG->auth)) {
5601 $authsenabled = array();
5602 } else {
5603 $authsenabled = explode(',', $CFG->auth);
5606 // construct the display array, with enabled auth plugins at the top, in order
5607 $displayauths = array();
5608 $registrationauths = array();
5609 $registrationauths[''] = $txt->disable;
5610 $authplugins = array();
5611 foreach ($authsenabled as $auth) {
5612 $authplugin = get_auth_plugin($auth);
5613 $authplugins[$auth] = $authplugin;
5614 /// Get the auth title (from core or own auth lang files)
5615 $authtitle = $authplugin->get_title();
5616 /// Apply titles
5617 $displayauths[$auth] = $authtitle;
5618 if ($authplugin->can_signup()) {
5619 $registrationauths[$auth] = $authtitle;
5623 foreach ($authsavailable as $auth => $dir) {
5624 if (array_key_exists($auth, $displayauths)) {
5625 continue; //already in the list
5627 $authplugin = get_auth_plugin($auth);
5628 $authplugins[$auth] = $authplugin;
5629 /// Get the auth title (from core or own auth lang files)
5630 $authtitle = $authplugin->get_title();
5631 /// Apply titles
5632 $displayauths[$auth] = $authtitle;
5633 if ($authplugin->can_signup()) {
5634 $registrationauths[$auth] = $authtitle;
5638 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
5639 $return .= $OUTPUT->box_start('generalbox authsui');
5641 $table = new html_table();
5642 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
5643 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
5644 $table->data = array();
5645 $table->attributes['class'] = 'admintable generaltable';
5646 $table->id = 'manageauthtable';
5648 //add always enabled plugins first
5649 $displayname = $displayauths['manual'];
5650 $settings = "<a href=\"auth_config.php?auth=manual\">{$txt->settings}</a>";
5651 //$settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
5652 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
5653 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
5654 $displayname = $displayauths['nologin'];
5655 $settings = "<a href=\"auth_config.php?auth=nologin\">{$txt->settings}</a>";
5656 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
5657 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
5660 // iterate through auth plugins and add to the display table
5661 $updowncount = 1;
5662 $authcount = count($authsenabled);
5663 $url = "auth.php?sesskey=" . sesskey();
5664 foreach ($displayauths as $auth => $name) {
5665 if ($auth == 'manual' or $auth == 'nologin') {
5666 continue;
5668 $class = '';
5669 // hide/show link
5670 if (in_array($auth, $authsenabled)) {
5671 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
5672 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5673 // $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\"><input type=\"checkbox\" checked /></a>";
5674 $enabled = true;
5675 $displayname = $name;
5677 else {
5678 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
5679 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5680 // $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\"><input type=\"checkbox\" /></a>";
5681 $enabled = false;
5682 $displayname = $name;
5683 $class = 'dimmed_text';
5686 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
5688 // up/down link (only if auth is enabled)
5689 $updown = '';
5690 if ($enabled) {
5691 if ($updowncount > 1) {
5692 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
5693 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5695 else {
5696 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5698 if ($updowncount < $authcount) {
5699 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
5700 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5702 else {
5703 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5705 ++ $updowncount;
5708 // settings link
5709 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
5710 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
5711 } else {
5712 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
5715 // Uninstall link.
5716 $uninstall = '';
5717 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
5718 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
5721 $test = '';
5722 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
5723 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
5724 $test = html_writer::link($testurl, $txt->testsettings);
5727 // Add a row to the table.
5728 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
5729 if ($class) {
5730 $row->attributes['class'] = $class;
5732 $table->data[] = $row;
5734 $return .= html_writer::table($table);
5735 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
5736 $return .= $OUTPUT->box_end();
5737 return highlight($query, $return);
5743 * Special class for authentication administration.
5745 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5747 class admin_setting_manageeditors extends admin_setting {
5749 * Calls parent::__construct with specific arguments
5751 public function __construct() {
5752 $this->nosave = true;
5753 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
5757 * Always returns true, does nothing
5759 * @return true
5761 public function get_setting() {
5762 return true;
5766 * Always returns true, does nothing
5768 * @return true
5770 public function get_defaultsetting() {
5771 return true;
5775 * Always returns '', does not write anything
5777 * @return string Always returns ''
5779 public function write_setting($data) {
5780 // do not write any setting
5781 return '';
5785 * Checks if $query is one of the available editors
5787 * @param string $query The string to search for
5788 * @return bool Returns true if found, false if not
5790 public function is_related($query) {
5791 if (parent::is_related($query)) {
5792 return true;
5795 $editors_available = editors_get_available();
5796 foreach ($editors_available as $editor=>$editorstr) {
5797 if (strpos($editor, $query) !== false) {
5798 return true;
5800 if (strpos(core_text::strtolower($editorstr), $query) !== false) {
5801 return true;
5804 return false;
5808 * Builds the XHTML to display the control
5810 * @param string $data Unused
5811 * @param string $query
5812 * @return string
5814 public function output_html($data, $query='') {
5815 global $CFG, $OUTPUT;
5817 // display strings
5818 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
5819 'up', 'down', 'none'));
5820 $struninstall = get_string('uninstallplugin', 'core_admin');
5822 $txt->updown = "$txt->up/$txt->down";
5824 $editors_available = editors_get_available();
5825 $active_editors = explode(',', $CFG->texteditors);
5827 $active_editors = array_reverse($active_editors);
5828 foreach ($active_editors as $key=>$editor) {
5829 if (empty($editors_available[$editor])) {
5830 unset($active_editors[$key]);
5831 } else {
5832 $name = $editors_available[$editor];
5833 unset($editors_available[$editor]);
5834 $editors_available[$editor] = $name;
5837 if (empty($active_editors)) {
5838 //$active_editors = array('textarea');
5840 $editors_available = array_reverse($editors_available, true);
5841 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
5842 $return .= $OUTPUT->box_start('generalbox editorsui');
5844 $table = new html_table();
5845 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
5846 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
5847 $table->id = 'editormanagement';
5848 $table->attributes['class'] = 'admintable generaltable';
5849 $table->data = array();
5851 // iterate through auth plugins and add to the display table
5852 $updowncount = 1;
5853 $editorcount = count($active_editors);
5854 $url = "editors.php?sesskey=" . sesskey();
5855 foreach ($editors_available as $editor => $name) {
5856 // hide/show link
5857 $class = '';
5858 if (in_array($editor, $active_editors)) {
5859 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
5860 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5861 // $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\"><input type=\"checkbox\" checked /></a>";
5862 $enabled = true;
5863 $displayname = $name;
5865 else {
5866 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
5867 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5868 // $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\"><input type=\"checkbox\" /></a>";
5869 $enabled = false;
5870 $displayname = $name;
5871 $class = 'dimmed_text';
5874 // up/down link (only if auth is enabled)
5875 $updown = '';
5876 if ($enabled) {
5877 if ($updowncount > 1) {
5878 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
5879 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5881 else {
5882 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5884 if ($updowncount < $editorcount) {
5885 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
5886 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5888 else {
5889 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5891 ++ $updowncount;
5894 // settings link
5895 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
5896 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
5897 $settings = "<a href='$eurl'>{$txt->settings}</a>";
5898 } else {
5899 $settings = '';
5902 $uninstall = '';
5903 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
5904 $uninstall = html_writer::link($uninstallurl, $struninstall);
5907 // Add a row to the table.
5908 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
5909 if ($class) {
5910 $row->attributes['class'] = $class;
5912 $table->data[] = $row;
5914 $return .= html_writer::table($table);
5915 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
5916 $return .= $OUTPUT->box_end();
5917 return highlight($query, $return);
5923 * Special class for license administration.
5925 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5927 class admin_setting_managelicenses extends admin_setting {
5929 * Calls parent::__construct with specific arguments
5931 public function __construct() {
5932 $this->nosave = true;
5933 parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', '');
5937 * Always returns true, does nothing
5939 * @return true
5941 public function get_setting() {
5942 return true;
5946 * Always returns true, does nothing
5948 * @return true
5950 public function get_defaultsetting() {
5951 return true;
5955 * Always returns '', does not write anything
5957 * @return string Always returns ''
5959 public function write_setting($data) {
5960 // do not write any setting
5961 return '';
5965 * Builds the XHTML to display the control
5967 * @param string $data Unused
5968 * @param string $query
5969 * @return string
5971 public function output_html($data, $query='') {
5972 global $CFG, $OUTPUT;
5973 require_once($CFG->libdir . '/licenselib.php');
5974 $url = "licenses.php?sesskey=" . sesskey();
5976 // display strings
5977 $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none'));
5978 $licenses = license_manager::get_licenses();
5980 $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true);
5982 $return .= $OUTPUT->box_start('generalbox editorsui');
5984 $table = new html_table();
5985 $table->head = array($txt->name, $txt->enable);
5986 $table->colclasses = array('leftalign', 'centeralign');
5987 $table->id = 'availablelicenses';
5988 $table->attributes['class'] = 'admintable generaltable';
5989 $table->data = array();
5991 foreach ($licenses as $value) {
5992 $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank'));
5994 if ($value->enabled == 1) {
5995 $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
5996 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/hide'), 'class'=>'iconsmall', 'alt'=>'disable')));
5997 } else {
5998 $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
5999 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/show'), 'class'=>'iconsmall', 'alt'=>'enable')));
6002 if ($value->shortname == $CFG->sitedefaultlicense) {
6003 $displayname .= ' '.html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/locked'), 'class'=>'iconsmall', 'alt'=>get_string('default'), 'title'=>get_string('default')));
6004 $hideshow = '';
6007 $enabled = true;
6009 $table->data[] =array($displayname, $hideshow);
6011 $return .= html_writer::table($table);
6012 $return .= $OUTPUT->box_end();
6013 return highlight($query, $return);
6018 * Course formats manager. Allows to enable/disable formats and jump to settings
6020 class admin_setting_manageformats extends admin_setting {
6023 * Calls parent::__construct with specific arguments
6025 public function __construct() {
6026 $this->nosave = true;
6027 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
6031 * Always returns true
6033 * @return true
6035 public function get_setting() {
6036 return true;
6040 * Always returns true
6042 * @return true
6044 public function get_defaultsetting() {
6045 return true;
6049 * Always returns '' and doesn't write anything
6051 * @param mixed $data string or array, must not be NULL
6052 * @return string Always returns ''
6054 public function write_setting($data) {
6055 // do not write any setting
6056 return '';
6060 * Search to find if Query is related to format plugin
6062 * @param string $query The string to search for
6063 * @return bool true for related false for not
6065 public function is_related($query) {
6066 if (parent::is_related($query)) {
6067 return true;
6069 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
6070 foreach ($formats as $format) {
6071 if (strpos($format->component, $query) !== false ||
6072 strpos(core_text::strtolower($format->displayname), $query) !== false) {
6073 return true;
6076 return false;
6080 * Return XHTML to display control
6082 * @param mixed $data Unused
6083 * @param string $query
6084 * @return string highlight
6086 public function output_html($data, $query='') {
6087 global $CFG, $OUTPUT;
6088 $return = '';
6089 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
6090 $return .= $OUTPUT->box_start('generalbox formatsui');
6092 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
6094 // display strings
6095 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
6096 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
6097 $txt->updown = "$txt->up/$txt->down";
6099 $table = new html_table();
6100 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
6101 $table->align = array('left', 'center', 'center', 'center', 'center');
6102 $table->attributes['class'] = 'manageformattable generaltable admintable';
6103 $table->data = array();
6105 $cnt = 0;
6106 $defaultformat = get_config('moodlecourse', 'format');
6107 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
6108 foreach ($formats as $format) {
6109 $url = new moodle_url('/admin/courseformats.php',
6110 array('sesskey' => sesskey(), 'format' => $format->name));
6111 $isdefault = '';
6112 $class = '';
6113 if ($format->is_enabled()) {
6114 $strformatname = $format->displayname;
6115 if ($defaultformat === $format->name) {
6116 $hideshow = $txt->default;
6117 } else {
6118 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
6119 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
6121 } else {
6122 $strformatname = $format->displayname;
6123 $class = 'dimmed_text';
6124 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
6125 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
6127 $updown = '';
6128 if ($cnt) {
6129 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
6130 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
6131 } else {
6132 $updown .= $spacer;
6134 if ($cnt < count($formats) - 1) {
6135 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
6136 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
6137 } else {
6138 $updown .= $spacer;
6140 $cnt++;
6141 $settings = '';
6142 if ($format->get_settings_url()) {
6143 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
6145 $uninstall = '';
6146 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
6147 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
6149 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
6150 if ($class) {
6151 $row->attributes['class'] = $class;
6153 $table->data[] = $row;
6155 $return .= html_writer::table($table);
6156 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
6157 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
6158 $return .= $OUTPUT->box_end();
6159 return highlight($query, $return);
6164 * Special class for filter administration.
6166 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6168 class admin_page_managefilters extends admin_externalpage {
6170 * Calls parent::__construct with specific arguments
6172 public function __construct() {
6173 global $CFG;
6174 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
6178 * Searches all installed filters for specified filter
6180 * @param string $query The filter(string) to search for
6181 * @param string $query
6183 public function search($query) {
6184 global $CFG;
6185 if ($result = parent::search($query)) {
6186 return $result;
6189 $found = false;
6190 $filternames = filter_get_all_installed();
6191 foreach ($filternames as $path => $strfiltername) {
6192 if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
6193 $found = true;
6194 break;
6196 if (strpos($path, $query) !== false) {
6197 $found = true;
6198 break;
6202 if ($found) {
6203 $result = new stdClass;
6204 $result->page = $this;
6205 $result->settings = array();
6206 return array($this->name => $result);
6207 } else {
6208 return array();
6215 * Initialise admin page - this function does require login and permission
6216 * checks specified in page definition.
6218 * This function must be called on each admin page before other code.
6220 * @global moodle_page $PAGE
6222 * @param string $section name of page
6223 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
6224 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
6225 * added to the turn blocks editing on/off form, so this page reloads correctly.
6226 * @param string $actualurl if the actual page being viewed is not the normal one for this
6227 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
6228 * @param array $options Additional options that can be specified for page setup.
6229 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
6231 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
6232 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
6234 $PAGE->set_context(null); // hack - set context to something, by default to system context
6236 $site = get_site();
6237 require_login();
6239 if (!empty($options['pagelayout'])) {
6240 // A specific page layout has been requested.
6241 $PAGE->set_pagelayout($options['pagelayout']);
6242 } else if ($section === 'upgradesettings') {
6243 $PAGE->set_pagelayout('maintenance');
6244 } else {
6245 $PAGE->set_pagelayout('admin');
6248 $adminroot = admin_get_root(false, false); // settings not required for external pages
6249 $extpage = $adminroot->locate($section, true);
6251 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
6252 // The requested section isn't in the admin tree
6253 // It could be because the user has inadequate capapbilities or because the section doesn't exist
6254 if (!has_capability('moodle/site:config', context_system::instance())) {
6255 // The requested section could depend on a different capability
6256 // but most likely the user has inadequate capabilities
6257 print_error('accessdenied', 'admin');
6258 } else {
6259 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
6263 // this eliminates our need to authenticate on the actual pages
6264 if (!$extpage->check_access()) {
6265 print_error('accessdenied', 'admin');
6266 die;
6269 navigation_node::require_admin_tree();
6271 // $PAGE->set_extra_button($extrabutton); TODO
6273 if (!$actualurl) {
6274 $actualurl = $extpage->url;
6277 $PAGE->set_url($actualurl, $extraurlparams);
6278 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
6279 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
6282 if (empty($SITE->fullname) || empty($SITE->shortname)) {
6283 // During initial install.
6284 $strinstallation = get_string('installation', 'install');
6285 $strsettings = get_string('settings');
6286 $PAGE->navbar->add($strsettings);
6287 $PAGE->set_title($strinstallation);
6288 $PAGE->set_heading($strinstallation);
6289 $PAGE->set_cacheable(false);
6290 return;
6293 // Locate the current item on the navigation and make it active when found.
6294 $path = $extpage->path;
6295 $node = $PAGE->settingsnav;
6296 while ($node && count($path) > 0) {
6297 $node = $node->get(array_pop($path));
6299 if ($node) {
6300 $node->make_active();
6303 // Normal case.
6304 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
6305 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
6306 $USER->editing = $adminediting;
6309 $visiblepathtosection = array_reverse($extpage->visiblepath);
6311 if ($PAGE->user_allowed_editing()) {
6312 if ($PAGE->user_is_editing()) {
6313 $caption = get_string('blockseditoff');
6314 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
6315 } else {
6316 $caption = get_string('blocksediton');
6317 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
6319 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
6322 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
6323 $PAGE->set_heading($SITE->fullname);
6325 // prevent caching in nav block
6326 $PAGE->navigation->clear_cache();
6330 * Returns the reference to admin tree root
6332 * @return object admin_root object
6334 function admin_get_root($reload=false, $requirefulltree=true) {
6335 global $CFG, $DB, $OUTPUT;
6337 static $ADMIN = NULL;
6339 if (is_null($ADMIN)) {
6340 // create the admin tree!
6341 $ADMIN = new admin_root($requirefulltree);
6344 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
6345 $ADMIN->purge_children($requirefulltree);
6348 if (!$ADMIN->loaded) {
6349 // we process this file first to create categories first and in correct order
6350 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
6352 // now we process all other files in admin/settings to build the admin tree
6353 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
6354 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
6355 continue;
6357 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
6358 // plugins are loaded last - they may insert pages anywhere
6359 continue;
6361 require($file);
6363 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
6365 $ADMIN->loaded = true;
6368 return $ADMIN;
6371 /// settings utility functions
6374 * This function applies default settings.
6376 * @param object $node, NULL means complete tree, null by default
6377 * @param bool $unconditional if true overrides all values with defaults, null buy default
6379 function admin_apply_default_settings($node=NULL, $unconditional=true) {
6380 global $CFG;
6382 if (is_null($node)) {
6383 core_plugin_manager::reset_caches();
6384 $node = admin_get_root(true, true);
6387 if ($node instanceof admin_category) {
6388 $entries = array_keys($node->children);
6389 foreach ($entries as $entry) {
6390 admin_apply_default_settings($node->children[$entry], $unconditional);
6393 } else if ($node instanceof admin_settingpage) {
6394 foreach ($node->settings as $setting) {
6395 if (!$unconditional and !is_null($setting->get_setting())) {
6396 //do not override existing defaults
6397 continue;
6399 $defaultsetting = $setting->get_defaultsetting();
6400 if (is_null($defaultsetting)) {
6401 // no value yet - default maybe applied after admin user creation or in upgradesettings
6402 continue;
6404 $setting->write_setting($defaultsetting);
6405 $setting->write_setting_flags(null);
6408 // Just in case somebody modifies the list of active plugins directly.
6409 core_plugin_manager::reset_caches();
6413 * Store changed settings, this function updates the errors variable in $ADMIN
6415 * @param object $formdata from form
6416 * @return int number of changed settings
6418 function admin_write_settings($formdata) {
6419 global $CFG, $SITE, $DB;
6421 $olddbsessions = !empty($CFG->dbsessions);
6422 $formdata = (array)$formdata;
6424 $data = array();
6425 foreach ($formdata as $fullname=>$value) {
6426 if (strpos($fullname, 's_') !== 0) {
6427 continue; // not a config value
6429 $data[$fullname] = $value;
6432 $adminroot = admin_get_root();
6433 $settings = admin_find_write_settings($adminroot, $data);
6435 $count = 0;
6436 foreach ($settings as $fullname=>$setting) {
6437 /** @var $setting admin_setting */
6438 $original = $setting->get_setting();
6439 $error = $setting->write_setting($data[$fullname]);
6440 if ($error !== '') {
6441 $adminroot->errors[$fullname] = new stdClass();
6442 $adminroot->errors[$fullname]->data = $data[$fullname];
6443 $adminroot->errors[$fullname]->id = $setting->get_id();
6444 $adminroot->errors[$fullname]->error = $error;
6445 } else {
6446 $setting->write_setting_flags($data);
6448 if ($setting->post_write_settings($original)) {
6449 $count++;
6453 if ($olddbsessions != !empty($CFG->dbsessions)) {
6454 require_logout();
6457 // Now update $SITE - just update the fields, in case other people have a
6458 // a reference to it (e.g. $PAGE, $COURSE).
6459 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
6460 foreach (get_object_vars($newsite) as $field => $value) {
6461 $SITE->$field = $value;
6464 // now reload all settings - some of them might depend on the changed
6465 admin_get_root(true);
6466 return $count;
6470 * Internal recursive function - finds all settings from submitted form
6472 * @param object $node Instance of admin_category, or admin_settingpage
6473 * @param array $data
6474 * @return array
6476 function admin_find_write_settings($node, $data) {
6477 $return = array();
6479 if (empty($data)) {
6480 return $return;
6483 if ($node instanceof admin_category) {
6484 $entries = array_keys($node->children);
6485 foreach ($entries as $entry) {
6486 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
6489 } else if ($node instanceof admin_settingpage) {
6490 foreach ($node->settings as $setting) {
6491 $fullname = $setting->get_full_name();
6492 if (array_key_exists($fullname, $data)) {
6493 $return[$fullname] = $setting;
6499 return $return;
6503 * Internal function - prints the search results
6505 * @param string $query String to search for
6506 * @return string empty or XHTML
6508 function admin_search_settings_html($query) {
6509 global $CFG, $OUTPUT;
6511 if (core_text::strlen($query) < 2) {
6512 return '';
6514 $query = core_text::strtolower($query);
6516 $adminroot = admin_get_root();
6517 $findings = $adminroot->search($query);
6518 $return = '';
6519 $savebutton = false;
6521 foreach ($findings as $found) {
6522 $page = $found->page;
6523 $settings = $found->settings;
6524 if ($page->is_hidden()) {
6525 // hidden pages are not displayed in search results
6526 continue;
6528 if ($page instanceof admin_externalpage) {
6529 $return .= $OUTPUT->heading(get_string('searchresults','admin').' - <a href="'.$page->url.'">'.highlight($query, $page->visiblename).'</a>', 2, 'main');
6530 } else if ($page instanceof admin_settingpage) {
6531 $return .= $OUTPUT->heading(get_string('searchresults','admin').' - <a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section='.$page->name.'">'.highlight($query, $page->visiblename).'</a>', 2, 'main');
6532 } else {
6533 continue;
6535 if (!empty($settings)) {
6536 $return .= '<fieldset class="adminsettings">'."\n";
6537 foreach ($settings as $setting) {
6538 if (empty($setting->nosave)) {
6539 $savebutton = true;
6541 $return .= '<div class="clearer"><!-- --></div>'."\n";
6542 $fullname = $setting->get_full_name();
6543 if (array_key_exists($fullname, $adminroot->errors)) {
6544 $data = $adminroot->errors[$fullname]->data;
6545 } else {
6546 $data = $setting->get_setting();
6547 // do not use defaults if settings not available - upgradesettings handles the defaults!
6549 $return .= $setting->output_html($data, $query);
6551 $return .= '</fieldset>';
6555 if ($savebutton) {
6556 $return .= '<div class="form-buttons"><input class="form-submit" type="submit" value="'.get_string('savechanges','admin').'" /></div>';
6559 return $return;
6563 * Internal function - returns arrays of html pages with uninitialised settings
6565 * @param object $node Instance of admin_category or admin_settingpage
6566 * @return array
6568 function admin_output_new_settings_by_page($node) {
6569 global $OUTPUT;
6570 $return = array();
6572 if ($node instanceof admin_category) {
6573 $entries = array_keys($node->children);
6574 foreach ($entries as $entry) {
6575 $return += admin_output_new_settings_by_page($node->children[$entry]);
6578 } else if ($node instanceof admin_settingpage) {
6579 $newsettings = array();
6580 foreach ($node->settings as $setting) {
6581 if (is_null($setting->get_setting())) {
6582 $newsettings[] = $setting;
6585 if (count($newsettings) > 0) {
6586 $adminroot = admin_get_root();
6587 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
6588 $page .= '<fieldset class="adminsettings">'."\n";
6589 foreach ($newsettings as $setting) {
6590 $fullname = $setting->get_full_name();
6591 if (array_key_exists($fullname, $adminroot->errors)) {
6592 $data = $adminroot->errors[$fullname]->data;
6593 } else {
6594 $data = $setting->get_setting();
6595 if (is_null($data)) {
6596 $data = $setting->get_defaultsetting();
6599 $page .= '<div class="clearer"><!-- --></div>'."\n";
6600 $page .= $setting->output_html($data);
6602 $page .= '</fieldset>';
6603 $return[$node->name] = $page;
6607 return $return;
6611 * Format admin settings
6613 * @param object $setting
6614 * @param string $title label element
6615 * @param string $form form fragment, html code - not highlighted automatically
6616 * @param string $description
6617 * @param bool $label link label to id, true by default
6618 * @param string $warning warning text
6619 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
6620 * @param string $query search query to be highlighted
6621 * @return string XHTML
6623 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
6624 global $CFG;
6626 $name = empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name";
6627 $fullname = $setting->get_full_name();
6629 // sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate
6630 if ($label) {
6631 $labelfor = 'for = "'.$setting->get_id().'"';
6632 } else {
6633 $labelfor = '';
6635 $form .= $setting->output_setting_flags();
6637 $override = '';
6638 if (empty($setting->plugin)) {
6639 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
6640 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6642 } else {
6643 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
6644 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6648 if ($warning !== '') {
6649 $warning = '<div class="form-warning">'.$warning.'</div>';
6652 $defaults = array();
6653 if (!is_null($defaultinfo)) {
6654 if ($defaultinfo === '') {
6655 $defaultinfo = get_string('emptysettingvalue', 'admin');
6657 $defaults[] = $defaultinfo;
6660 $setting->get_setting_flag_defaults($defaults);
6662 if (!empty($defaults)) {
6663 $defaultinfo = implode(', ', $defaults);
6664 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
6665 $defaultinfo = '<div class="form-defaultinfo">'.get_string('defaultsettinginfo', 'admin', $defaultinfo).'</div>';
6669 $str = '
6670 <div class="form-item clearfix" id="admin-'.$setting->name.'">
6671 <div class="form-label">
6672 <label '.$labelfor.'>'.highlightfast($query, $title).$override.$warning.'</label>
6673 <span class="form-shortname">'.highlightfast($query, $name).'</span>
6674 </div>
6675 <div class="form-setting">'.$form.$defaultinfo.'</div>
6676 <div class="form-description">'.highlight($query, markdown_to_html($description)).'</div>
6677 </div>';
6679 $adminroot = admin_get_root();
6680 if (array_key_exists($fullname, $adminroot->errors)) {
6681 $str = '<fieldset class="error"><legend>'.$adminroot->errors[$fullname]->error.'</legend>'.$str.'</fieldset>';
6684 return $str;
6688 * Based on find_new_settings{@link ()} in upgradesettings.php
6689 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
6691 * @param object $node Instance of admin_category, or admin_settingpage
6692 * @return boolean true if any settings haven't been initialised, false if they all have
6694 function any_new_admin_settings($node) {
6696 if ($node instanceof admin_category) {
6697 $entries = array_keys($node->children);
6698 foreach ($entries as $entry) {
6699 if (any_new_admin_settings($node->children[$entry])) {
6700 return true;
6704 } else if ($node instanceof admin_settingpage) {
6705 foreach ($node->settings as $setting) {
6706 if ($setting->get_setting() === NULL) {
6707 return true;
6712 return false;
6716 * Moved from admin/replace.php so that we can use this in cron
6718 * @param string $search string to look for
6719 * @param string $replace string to replace
6720 * @return bool success or fail
6722 function db_replace($search, $replace) {
6723 global $DB, $CFG, $OUTPUT;
6725 // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
6726 $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log',
6727 'filter_config', 'sessions', 'events_queue', 'repository_instance_config',
6728 'block_instances', '');
6730 // Turn off time limits, sometimes upgrades can be slow.
6731 @set_time_limit(0);
6733 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
6734 return false;
6736 foreach ($tables as $table) {
6738 if (in_array($table, $skiptables)) { // Don't process these
6739 continue;
6742 if ($columns = $DB->get_columns($table)) {
6743 $DB->set_debug(true);
6744 foreach ($columns as $column => $data) {
6745 if (in_array($data->meta_type, array('C', 'X'))) { // Text stuff only
6746 //TODO: this should be definitively moved to DML driver to do the actual replace, this is not going to work for MSSQL and Oracle...
6747 $DB->execute("UPDATE {".$table."} SET $column = REPLACE($column, ?, ?)", array($search, $replace));
6750 $DB->set_debug(false);
6754 // delete modinfo caches
6755 rebuild_course_cache(0, true);
6757 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
6758 $blocks = core_component::get_plugin_list('block');
6759 foreach ($blocks as $blockname=>$fullblock) {
6760 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
6761 continue;
6764 if (!is_readable($fullblock.'/lib.php')) {
6765 continue;
6768 $function = 'block_'.$blockname.'_global_db_replace';
6769 include_once($fullblock.'/lib.php');
6770 if (!function_exists($function)) {
6771 continue;
6774 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
6775 $function($search, $replace);
6776 echo $OUTPUT->notification("...finished", 'notifysuccess');
6779 return true;
6783 * Manage repository settings
6785 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6787 class admin_setting_managerepository extends admin_setting {
6788 /** @var string */
6789 private $baseurl;
6792 * calls parent::__construct with specific arguments
6794 public function __construct() {
6795 global $CFG;
6796 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
6797 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
6801 * Always returns true, does nothing
6803 * @return true
6805 public function get_setting() {
6806 return true;
6810 * Always returns true does nothing
6812 * @return true
6814 public function get_defaultsetting() {
6815 return true;
6819 * Always returns s_managerepository
6821 * @return string Always return 's_managerepository'
6823 public function get_full_name() {
6824 return 's_managerepository';
6828 * Always returns '' doesn't do anything
6830 public function write_setting($data) {
6831 $url = $this->baseurl . '&amp;new=' . $data;
6832 return '';
6833 // TODO
6834 // Should not use redirect and exit here
6835 // Find a better way to do this.
6836 // redirect($url);
6837 // exit;
6841 * Searches repository plugins for one that matches $query
6843 * @param string $query The string to search for
6844 * @return bool true if found, false if not
6846 public function is_related($query) {
6847 if (parent::is_related($query)) {
6848 return true;
6851 $repositories= core_component::get_plugin_list('repository');
6852 foreach ($repositories as $p => $dir) {
6853 if (strpos($p, $query) !== false) {
6854 return true;
6857 foreach (repository::get_types() as $instance) {
6858 $title = $instance->get_typename();
6859 if (strpos(core_text::strtolower($title), $query) !== false) {
6860 return true;
6863 return false;
6867 * Helper function that generates a moodle_url object
6868 * relevant to the repository
6871 function repository_action_url($repository) {
6872 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
6876 * Builds XHTML to display the control
6878 * @param string $data Unused
6879 * @param string $query
6880 * @return string XHTML
6882 public function output_html($data, $query='') {
6883 global $CFG, $USER, $OUTPUT;
6885 // Get strings that are used
6886 $strshow = get_string('on', 'repository');
6887 $strhide = get_string('off', 'repository');
6888 $strdelete = get_string('disabled', 'repository');
6890 $actionchoicesforexisting = array(
6891 'show' => $strshow,
6892 'hide' => $strhide,
6893 'delete' => $strdelete
6896 $actionchoicesfornew = array(
6897 'newon' => $strshow,
6898 'newoff' => $strhide,
6899 'delete' => $strdelete
6902 $return = '';
6903 $return .= $OUTPUT->box_start('generalbox');
6905 // Set strings that are used multiple times
6906 $settingsstr = get_string('settings');
6907 $disablestr = get_string('disable');
6909 // Table to list plug-ins
6910 $table = new html_table();
6911 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
6912 $table->align = array('left', 'center', 'center', 'center', 'center');
6913 $table->data = array();
6915 // Get list of used plug-ins
6916 $repositorytypes = repository::get_types();
6917 if (!empty($repositorytypes)) {
6918 // Array to store plugins being used
6919 $alreadyplugins = array();
6920 $totalrepositorytypes = count($repositorytypes);
6921 $updowncount = 1;
6922 foreach ($repositorytypes as $i) {
6923 $settings = '';
6924 $typename = $i->get_typename();
6925 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
6926 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
6927 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
6929 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
6930 // Calculate number of instances in order to display them for the Moodle administrator
6931 if (!empty($instanceoptionnames)) {
6932 $params = array();
6933 $params['context'] = array(context_system::instance());
6934 $params['onlyvisible'] = false;
6935 $params['type'] = $typename;
6936 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
6937 // site instances
6938 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
6939 $params['context'] = array();
6940 $instances = repository::static_function($typename, 'get_instances', $params);
6941 $courseinstances = array();
6942 $userinstances = array();
6944 foreach ($instances as $instance) {
6945 $repocontext = context::instance_by_id($instance->instance->contextid);
6946 if ($repocontext->contextlevel == CONTEXT_COURSE) {
6947 $courseinstances[] = $instance;
6948 } else if ($repocontext->contextlevel == CONTEXT_USER) {
6949 $userinstances[] = $instance;
6952 // course instances
6953 $instancenumber = count($courseinstances);
6954 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
6956 // user private instances
6957 $instancenumber = count($userinstances);
6958 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
6959 } else {
6960 $admininstancenumbertext = "";
6961 $courseinstancenumbertext = "";
6962 $userinstancenumbertext = "";
6965 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
6967 $settings .= $OUTPUT->container_start('mdl-left');
6968 $settings .= '<br/>';
6969 $settings .= $admininstancenumbertext;
6970 $settings .= '<br/>';
6971 $settings .= $courseinstancenumbertext;
6972 $settings .= '<br/>';
6973 $settings .= $userinstancenumbertext;
6974 $settings .= $OUTPUT->container_end();
6976 // Get the current visibility
6977 if ($i->get_visible()) {
6978 $currentaction = 'show';
6979 } else {
6980 $currentaction = 'hide';
6983 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
6985 // Display up/down link
6986 $updown = '';
6987 // Should be done with CSS instead.
6988 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
6990 if ($updowncount > 1) {
6991 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
6992 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
6994 else {
6995 $updown .= $spacer;
6997 if ($updowncount < $totalrepositorytypes) {
6998 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
6999 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
7001 else {
7002 $updown .= $spacer;
7005 $updowncount++;
7007 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
7009 if (!in_array($typename, $alreadyplugins)) {
7010 $alreadyplugins[] = $typename;
7015 // Get all the plugins that exist on disk
7016 $plugins = core_component::get_plugin_list('repository');
7017 if (!empty($plugins)) {
7018 foreach ($plugins as $plugin => $dir) {
7019 // Check that it has not already been listed
7020 if (!in_array($plugin, $alreadyplugins)) {
7021 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
7022 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
7027 $return .= html_writer::table($table);
7028 $return .= $OUTPUT->box_end();
7029 return highlight($query, $return);
7034 * Special checkbox for enable mobile web service
7035 * If enable then we store the service id of the mobile service into config table
7036 * If disable then we unstore the service id from the config table
7038 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
7040 /** @var boolean True means that the capability 'webservice/xmlrpc:use' is set for authenticated user role */
7041 private $xmlrpcuse;
7042 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
7043 private $restuse;
7046 * Return true if Authenticated user role has the capability 'webservice/xmlrpc:use' and 'webservice/rest:use', otherwise false.
7048 * @return boolean
7050 private function is_protocol_cap_allowed() {
7051 global $DB, $CFG;
7053 // We keep xmlrpc enabled for backward compatibility.
7054 // If the $this->xmlrpcuse variable is not set, it needs to be set.
7055 if (empty($this->xmlrpcuse) and $this->xmlrpcuse!==false) {
7056 $params = array();
7057 $params['permission'] = CAP_ALLOW;
7058 $params['roleid'] = $CFG->defaultuserroleid;
7059 $params['capability'] = 'webservice/xmlrpc:use';
7060 $this->xmlrpcuse = $DB->record_exists('role_capabilities', $params);
7063 // If the $this->restuse variable is not set, it needs to be set.
7064 if (empty($this->restuse) and $this->restuse!==false) {
7065 $params = array();
7066 $params['permission'] = CAP_ALLOW;
7067 $params['roleid'] = $CFG->defaultuserroleid;
7068 $params['capability'] = 'webservice/rest:use';
7069 $this->restuse = $DB->record_exists('role_capabilities', $params);
7072 return ($this->xmlrpcuse && $this->restuse);
7076 * Set the 'webservice/xmlrpc:use'/'webservice/rest:use' to the Authenticated user role (allow or not)
7077 * @param type $status true to allow, false to not set
7079 private function set_protocol_cap($status) {
7080 global $CFG;
7081 if ($status and !$this->is_protocol_cap_allowed()) {
7082 //need to allow the cap
7083 $permission = CAP_ALLOW;
7084 $assign = true;
7085 } else if (!$status and $this->is_protocol_cap_allowed()){
7086 //need to disallow the cap
7087 $permission = CAP_INHERIT;
7088 $assign = true;
7090 if (!empty($assign)) {
7091 $systemcontext = context_system::instance();
7092 assign_capability('webservice/xmlrpc:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7093 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7098 * Builds XHTML to display the control.
7099 * The main purpose of this overloading is to display a warning when https
7100 * is not supported by the server
7101 * @param string $data Unused
7102 * @param string $query
7103 * @return string XHTML
7105 public function output_html($data, $query='') {
7106 global $CFG, $OUTPUT;
7107 $html = parent::output_html($data, $query);
7109 if ((string)$data === $this->yes) {
7110 require_once($CFG->dirroot . "/lib/filelib.php");
7111 $curl = new curl();
7112 $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); //force https url
7113 $curl->head($httpswwwroot . "/login/index.php");
7114 $info = $curl->get_info();
7115 if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
7116 $html .= $OUTPUT->notification(get_string('nohttpsformobilewarning', 'admin'));
7120 return $html;
7124 * Retrieves the current setting using the objects name
7126 * @return string
7128 public function get_setting() {
7129 global $CFG;
7131 // For install cli script, $CFG->defaultuserroleid is not set so return 0
7132 // Or if web services aren't enabled this can't be,
7133 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
7134 return 0;
7137 require_once($CFG->dirroot . '/webservice/lib.php');
7138 $webservicemanager = new webservice();
7139 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7140 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
7141 return $this->config_read($this->name); //same as returning 1
7142 } else {
7143 return 0;
7148 * Save the selected setting
7150 * @param string $data The selected site
7151 * @return string empty string or error message
7153 public function write_setting($data) {
7154 global $DB, $CFG;
7156 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
7157 if (empty($CFG->defaultuserroleid)) {
7158 return '';
7161 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
7163 require_once($CFG->dirroot . '/webservice/lib.php');
7164 $webservicemanager = new webservice();
7166 $updateprotocol = false;
7167 if ((string)$data === $this->yes) {
7168 //code run when enable mobile web service
7169 //enable web service systeme if necessary
7170 set_config('enablewebservices', true);
7172 //enable mobile service
7173 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7174 $mobileservice->enabled = 1;
7175 $webservicemanager->update_external_service($mobileservice);
7177 //enable xml-rpc server
7178 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7180 if (!in_array('xmlrpc', $activeprotocols)) {
7181 $activeprotocols[] = 'xmlrpc';
7182 $updateprotocol = true;
7185 if (!in_array('rest', $activeprotocols)) {
7186 $activeprotocols[] = 'rest';
7187 $updateprotocol = true;
7190 if ($updateprotocol) {
7191 set_config('webserviceprotocols', implode(',', $activeprotocols));
7194 //allow xml-rpc:use capability for authenticated user
7195 $this->set_protocol_cap(true);
7197 } else {
7198 //disable web service system if no other services are enabled
7199 $otherenabledservices = $DB->get_records_select('external_services',
7200 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
7201 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
7202 if (empty($otherenabledservices)) {
7203 set_config('enablewebservices', false);
7205 //also disable xml-rpc server
7206 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7207 $protocolkey = array_search('xmlrpc', $activeprotocols);
7208 if ($protocolkey !== false) {
7209 unset($activeprotocols[$protocolkey]);
7210 $updateprotocol = true;
7213 $protocolkey = array_search('rest', $activeprotocols);
7214 if ($protocolkey !== false) {
7215 unset($activeprotocols[$protocolkey]);
7216 $updateprotocol = true;
7219 if ($updateprotocol) {
7220 set_config('webserviceprotocols', implode(',', $activeprotocols));
7223 //disallow xml-rpc:use capability for authenticated user
7224 $this->set_protocol_cap(false);
7227 //disable the mobile service
7228 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7229 $mobileservice->enabled = 0;
7230 $webservicemanager->update_external_service($mobileservice);
7233 return (parent::write_setting($data));
7238 * Special class for management of external services
7240 * @author Petr Skoda (skodak)
7242 class admin_setting_manageexternalservices extends admin_setting {
7244 * Calls parent::__construct with specific arguments
7246 public function __construct() {
7247 $this->nosave = true;
7248 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
7252 * Always returns true, does nothing
7254 * @return true
7256 public function get_setting() {
7257 return true;
7261 * Always returns true, does nothing
7263 * @return true
7265 public function get_defaultsetting() {
7266 return true;
7270 * Always returns '', does not write anything
7272 * @return string Always returns ''
7274 public function write_setting($data) {
7275 // do not write any setting
7276 return '';
7280 * Checks if $query is one of the available external services
7282 * @param string $query The string to search for
7283 * @return bool Returns true if found, false if not
7285 public function is_related($query) {
7286 global $DB;
7288 if (parent::is_related($query)) {
7289 return true;
7292 $services = $DB->get_records('external_services', array(), 'id, name');
7293 foreach ($services as $service) {
7294 if (strpos(core_text::strtolower($service->name), $query) !== false) {
7295 return true;
7298 return false;
7302 * Builds the XHTML to display the control
7304 * @param string $data Unused
7305 * @param string $query
7306 * @return string
7308 public function output_html($data, $query='') {
7309 global $CFG, $OUTPUT, $DB;
7311 // display strings
7312 $stradministration = get_string('administration');
7313 $stredit = get_string('edit');
7314 $strservice = get_string('externalservice', 'webservice');
7315 $strdelete = get_string('delete');
7316 $strplugin = get_string('plugin', 'admin');
7317 $stradd = get_string('add');
7318 $strfunctions = get_string('functions', 'webservice');
7319 $strusers = get_string('users');
7320 $strserviceusers = get_string('serviceusers', 'webservice');
7322 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
7323 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
7324 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
7326 // built in services
7327 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
7328 $return = "";
7329 if (!empty($services)) {
7330 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
7334 $table = new html_table();
7335 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
7336 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7337 $table->id = 'builtinservices';
7338 $table->attributes['class'] = 'admintable externalservices generaltable';
7339 $table->data = array();
7341 // iterate through auth plugins and add to the display table
7342 foreach ($services as $service) {
7343 $name = $service->name;
7345 // hide/show link
7346 if ($service->enabled) {
7347 $displayname = "<span>$name</span>";
7348 } else {
7349 $displayname = "<span class=\"dimmed_text\">$name</span>";
7352 $plugin = $service->component;
7354 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7356 if ($service->restrictedusers) {
7357 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7358 } else {
7359 $users = get_string('allusers', 'webservice');
7362 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7364 // add a row to the table
7365 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
7367 $return .= html_writer::table($table);
7370 // Custom services
7371 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
7372 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
7374 $table = new html_table();
7375 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
7376 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7377 $table->id = 'customservices';
7378 $table->attributes['class'] = 'admintable externalservices generaltable';
7379 $table->data = array();
7381 // iterate through auth plugins and add to the display table
7382 foreach ($services as $service) {
7383 $name = $service->name;
7385 // hide/show link
7386 if ($service->enabled) {
7387 $displayname = "<span>$name</span>";
7388 } else {
7389 $displayname = "<span class=\"dimmed_text\">$name</span>";
7392 // delete link
7393 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
7395 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7397 if ($service->restrictedusers) {
7398 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7399 } else {
7400 $users = get_string('allusers', 'webservice');
7403 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7405 // add a row to the table
7406 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
7408 // add new custom service option
7409 $return .= html_writer::table($table);
7411 $return .= '<br />';
7412 // add a token to the table
7413 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
7415 return highlight($query, $return);
7420 * Special class for overview of external services
7422 * @author Jerome Mouneyrac
7424 class admin_setting_webservicesoverview extends admin_setting {
7427 * Calls parent::__construct with specific arguments
7429 public function __construct() {
7430 $this->nosave = true;
7431 parent::__construct('webservicesoverviewui',
7432 get_string('webservicesoverview', 'webservice'), '', '');
7436 * Always returns true, does nothing
7438 * @return true
7440 public function get_setting() {
7441 return true;
7445 * Always returns true, does nothing
7447 * @return true
7449 public function get_defaultsetting() {
7450 return true;
7454 * Always returns '', does not write anything
7456 * @return string Always returns ''
7458 public function write_setting($data) {
7459 // do not write any setting
7460 return '';
7464 * Builds the XHTML to display the control
7466 * @param string $data Unused
7467 * @param string $query
7468 * @return string
7470 public function output_html($data, $query='') {
7471 global $CFG, $OUTPUT;
7473 $return = "";
7474 $brtag = html_writer::empty_tag('br');
7476 // Enable mobile web service
7477 $enablemobile = new admin_setting_enablemobileservice('enablemobilewebservice',
7478 get_string('enablemobilewebservice', 'admin'),
7479 get_string('configenablemobilewebservice',
7480 'admin', ''), 0); //we don't want to display it but to know the ws mobile status
7481 $manageserviceurl = new moodle_url("/admin/settings.php?section=externalservices");
7482 $wsmobileparam = new stdClass();
7483 $wsmobileparam->enablemobileservice = get_string('enablemobilewebservice', 'admin');
7484 $wsmobileparam->manageservicelink = html_writer::link($manageserviceurl,
7485 get_string('externalservices', 'webservice'));
7486 $mobilestatus = $enablemobile->get_setting()?get_string('mobilewsenabled', 'webservice'):get_string('mobilewsdisabled', 'webservice');
7487 $wsmobileparam->wsmobilestatus = html_writer::tag('strong', $mobilestatus);
7488 $return .= $OUTPUT->heading(get_string('enablemobilewebservice', 'admin'), 3, 'main');
7489 $return .= $brtag . get_string('enablemobilewsoverview', 'webservice', $wsmobileparam)
7490 . $brtag . $brtag;
7492 /// One system controlling Moodle with Token
7493 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
7494 $table = new html_table();
7495 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7496 get_string('description'));
7497 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7498 $table->id = 'onesystemcontrol';
7499 $table->attributes['class'] = 'admintable wsoverview generaltable';
7500 $table->data = array();
7502 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
7503 . $brtag . $brtag;
7505 /// 1. Enable Web Services
7506 $row = array();
7507 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7508 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7509 array('href' => $url));
7510 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7511 if ($CFG->enablewebservices) {
7512 $status = get_string('yes');
7514 $row[1] = $status;
7515 $row[2] = get_string('enablewsdescription', 'webservice');
7516 $table->data[] = $row;
7518 /// 2. Enable protocols
7519 $row = array();
7520 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7521 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7522 array('href' => $url));
7523 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7524 //retrieve activated protocol
7525 $active_protocols = empty($CFG->webserviceprotocols) ?
7526 array() : explode(',', $CFG->webserviceprotocols);
7527 if (!empty($active_protocols)) {
7528 $status = "";
7529 foreach ($active_protocols as $protocol) {
7530 $status .= $protocol . $brtag;
7533 $row[1] = $status;
7534 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7535 $table->data[] = $row;
7537 /// 3. Create user account
7538 $row = array();
7539 $url = new moodle_url("/user/editadvanced.php?id=-1");
7540 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
7541 array('href' => $url));
7542 $row[1] = "";
7543 $row[2] = get_string('createuserdescription', 'webservice');
7544 $table->data[] = $row;
7546 /// 4. Add capability to users
7547 $row = array();
7548 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7549 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
7550 array('href' => $url));
7551 $row[1] = "";
7552 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
7553 $table->data[] = $row;
7555 /// 5. Select a web service
7556 $row = array();
7557 $url = new moodle_url("/admin/settings.php?section=externalservices");
7558 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7559 array('href' => $url));
7560 $row[1] = "";
7561 $row[2] = get_string('createservicedescription', 'webservice');
7562 $table->data[] = $row;
7564 /// 6. Add functions
7565 $row = array();
7566 $url = new moodle_url("/admin/settings.php?section=externalservices");
7567 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7568 array('href' => $url));
7569 $row[1] = "";
7570 $row[2] = get_string('addfunctionsdescription', 'webservice');
7571 $table->data[] = $row;
7573 /// 7. Add the specific user
7574 $row = array();
7575 $url = new moodle_url("/admin/settings.php?section=externalservices");
7576 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
7577 array('href' => $url));
7578 $row[1] = "";
7579 $row[2] = get_string('selectspecificuserdescription', 'webservice');
7580 $table->data[] = $row;
7582 /// 8. Create token for the specific user
7583 $row = array();
7584 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
7585 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
7586 array('href' => $url));
7587 $row[1] = "";
7588 $row[2] = get_string('createtokenforuserdescription', 'webservice');
7589 $table->data[] = $row;
7591 /// 9. Enable the documentation
7592 $row = array();
7593 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
7594 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
7595 array('href' => $url));
7596 $status = '<span class="warning">' . get_string('no') . '</span>';
7597 if ($CFG->enablewsdocumentation) {
7598 $status = get_string('yes');
7600 $row[1] = $status;
7601 $row[2] = get_string('enabledocumentationdescription', 'webservice');
7602 $table->data[] = $row;
7604 /// 10. Test the service
7605 $row = array();
7606 $url = new moodle_url("/admin/webservice/testclient.php");
7607 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7608 array('href' => $url));
7609 $row[1] = "";
7610 $row[2] = get_string('testwithtestclientdescription', 'webservice');
7611 $table->data[] = $row;
7613 $return .= html_writer::table($table);
7615 /// Users as clients with token
7616 $return .= $brtag . $brtag . $brtag;
7617 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
7618 $table = new html_table();
7619 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7620 get_string('description'));
7621 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7622 $table->id = 'userasclients';
7623 $table->attributes['class'] = 'admintable wsoverview generaltable';
7624 $table->data = array();
7626 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
7627 $brtag . $brtag;
7629 /// 1. Enable Web Services
7630 $row = array();
7631 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7632 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7633 array('href' => $url));
7634 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7635 if ($CFG->enablewebservices) {
7636 $status = get_string('yes');
7638 $row[1] = $status;
7639 $row[2] = get_string('enablewsdescription', 'webservice');
7640 $table->data[] = $row;
7642 /// 2. Enable protocols
7643 $row = array();
7644 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7645 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7646 array('href' => $url));
7647 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7648 //retrieve activated protocol
7649 $active_protocols = empty($CFG->webserviceprotocols) ?
7650 array() : explode(',', $CFG->webserviceprotocols);
7651 if (!empty($active_protocols)) {
7652 $status = "";
7653 foreach ($active_protocols as $protocol) {
7654 $status .= $protocol . $brtag;
7657 $row[1] = $status;
7658 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7659 $table->data[] = $row;
7662 /// 3. Select a web service
7663 $row = array();
7664 $url = new moodle_url("/admin/settings.php?section=externalservices");
7665 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7666 array('href' => $url));
7667 $row[1] = "";
7668 $row[2] = get_string('createserviceforusersdescription', 'webservice');
7669 $table->data[] = $row;
7671 /// 4. Add functions
7672 $row = array();
7673 $url = new moodle_url("/admin/settings.php?section=externalservices");
7674 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7675 array('href' => $url));
7676 $row[1] = "";
7677 $row[2] = get_string('addfunctionsdescription', 'webservice');
7678 $table->data[] = $row;
7680 /// 5. Add capability to users
7681 $row = array();
7682 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7683 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
7684 array('href' => $url));
7685 $row[1] = "";
7686 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
7687 $table->data[] = $row;
7689 /// 6. Test the service
7690 $row = array();
7691 $url = new moodle_url("/admin/webservice/testclient.php");
7692 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7693 array('href' => $url));
7694 $row[1] = "";
7695 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
7696 $table->data[] = $row;
7698 $return .= html_writer::table($table);
7700 return highlight($query, $return);
7707 * Special class for web service protocol administration.
7709 * @author Petr Skoda (skodak)
7711 class admin_setting_managewebserviceprotocols extends admin_setting {
7714 * Calls parent::__construct with specific arguments
7716 public function __construct() {
7717 $this->nosave = true;
7718 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
7722 * Always returns true, does nothing
7724 * @return true
7726 public function get_setting() {
7727 return true;
7731 * Always returns true, does nothing
7733 * @return true
7735 public function get_defaultsetting() {
7736 return true;
7740 * Always returns '', does not write anything
7742 * @return string Always returns ''
7744 public function write_setting($data) {
7745 // do not write any setting
7746 return '';
7750 * Checks if $query is one of the available webservices
7752 * @param string $query The string to search for
7753 * @return bool Returns true if found, false if not
7755 public function is_related($query) {
7756 if (parent::is_related($query)) {
7757 return true;
7760 $protocols = core_component::get_plugin_list('webservice');
7761 foreach ($protocols as $protocol=>$location) {
7762 if (strpos($protocol, $query) !== false) {
7763 return true;
7765 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
7766 if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
7767 return true;
7770 return false;
7774 * Builds the XHTML to display the control
7776 * @param string $data Unused
7777 * @param string $query
7778 * @return string
7780 public function output_html($data, $query='') {
7781 global $CFG, $OUTPUT;
7783 // display strings
7784 $stradministration = get_string('administration');
7785 $strsettings = get_string('settings');
7786 $stredit = get_string('edit');
7787 $strprotocol = get_string('protocol', 'webservice');
7788 $strenable = get_string('enable');
7789 $strdisable = get_string('disable');
7790 $strversion = get_string('version');
7792 $protocols_available = core_component::get_plugin_list('webservice');
7793 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7794 ksort($protocols_available);
7796 foreach ($active_protocols as $key=>$protocol) {
7797 if (empty($protocols_available[$protocol])) {
7798 unset($active_protocols[$key]);
7802 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
7803 $return .= $OUTPUT->box_start('generalbox webservicesui');
7805 $table = new html_table();
7806 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
7807 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7808 $table->id = 'webserviceprotocols';
7809 $table->attributes['class'] = 'admintable generaltable';
7810 $table->data = array();
7812 // iterate through auth plugins and add to the display table
7813 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
7814 foreach ($protocols_available as $protocol => $location) {
7815 $name = get_string('pluginname', 'webservice_'.$protocol);
7817 $plugin = new stdClass();
7818 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
7819 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
7821 $version = isset($plugin->version) ? $plugin->version : '';
7823 // hide/show link
7824 if (in_array($protocol, $active_protocols)) {
7825 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
7826 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
7827 $displayname = "<span>$name</span>";
7828 } else {
7829 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
7830 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
7831 $displayname = "<span class=\"dimmed_text\">$name</span>";
7834 // settings link
7835 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
7836 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
7837 } else {
7838 $settings = '';
7841 // add a row to the table
7842 $table->data[] = array($displayname, $version, $hideshow, $settings);
7844 $return .= html_writer::table($table);
7845 $return .= get_string('configwebserviceplugins', 'webservice');
7846 $return .= $OUTPUT->box_end();
7848 return highlight($query, $return);
7854 * Special class for web service token administration.
7856 * @author Jerome Mouneyrac
7858 class admin_setting_managewebservicetokens extends admin_setting {
7861 * Calls parent::__construct with specific arguments
7863 public function __construct() {
7864 $this->nosave = true;
7865 parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
7869 * Always returns true, does nothing
7871 * @return true
7873 public function get_setting() {
7874 return true;
7878 * Always returns true, does nothing
7880 * @return true
7882 public function get_defaultsetting() {
7883 return true;
7887 * Always returns '', does not write anything
7889 * @return string Always returns ''
7891 public function write_setting($data) {
7892 // do not write any setting
7893 return '';
7897 * Builds the XHTML to display the control
7899 * @param string $data Unused
7900 * @param string $query
7901 * @return string
7903 public function output_html($data, $query='') {
7904 global $CFG, $OUTPUT, $DB, $USER;
7906 // display strings
7907 $stroperation = get_string('operation', 'webservice');
7908 $strtoken = get_string('token', 'webservice');
7909 $strservice = get_string('service', 'webservice');
7910 $struser = get_string('user');
7911 $strcontext = get_string('context', 'webservice');
7912 $strvaliduntil = get_string('validuntil', 'webservice');
7913 $striprestriction = get_string('iprestriction', 'webservice');
7915 $return = $OUTPUT->box_start('generalbox webservicestokenui');
7917 $table = new html_table();
7918 $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation);
7919 $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'centeralign');
7920 $table->id = 'webservicetokens';
7921 $table->attributes['class'] = 'admintable generaltable';
7922 $table->data = array();
7924 $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
7926 //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN
7928 //here retrieve token list (including linked users firstname/lastname and linked services name)
7929 $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid
7930 FROM {external_tokens} t, {user} u, {external_services} s
7931 WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id";
7932 $tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT));
7933 if (!empty($tokens)) {
7934 foreach ($tokens as $token) {
7935 //TODO: retrieve context
7937 $delete = "<a href=\"".$tokenpageurl."&amp;action=delete&amp;tokenid=".$token->id."\">";
7938 $delete .= get_string('delete')."</a>";
7940 $validuntil = '';
7941 if (!empty($token->validuntil)) {
7942 $validuntil = userdate($token->validuntil, get_string('strftimedatetime', 'langconfig'));
7945 $iprestriction = '';
7946 if (!empty($token->iprestriction)) {
7947 $iprestriction = $token->iprestriction;
7950 $userprofilurl = new moodle_url('/user/profile.php?id='.$token->userid);
7951 $useratag = html_writer::start_tag('a', array('href' => $userprofilurl));
7952 $useratag .= $token->firstname." ".$token->lastname;
7953 $useratag .= html_writer::end_tag('a');
7955 //check user missing capabilities
7956 require_once($CFG->dirroot . '/webservice/lib.php');
7957 $webservicemanager = new webservice();
7958 $usermissingcaps = $webservicemanager->get_missing_capabilities_by_users(
7959 array(array('id' => $token->userid)), $token->serviceid);
7961 if (!is_siteadmin($token->userid) and
7962 array_key_exists($token->userid, $usermissingcaps)) {
7963 $missingcapabilities = implode(', ',
7964 $usermissingcaps[$token->userid]);
7965 if (!empty($missingcapabilities)) {
7966 $useratag .= html_writer::tag('div',
7967 get_string('usermissingcaps', 'webservice',
7968 $missingcapabilities)
7969 . '&nbsp;' . $OUTPUT->help_icon('missingcaps', 'webservice'),
7970 array('class' => 'missingcaps'));
7974 $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete);
7977 $return .= html_writer::table($table);
7978 } else {
7979 $return .= get_string('notoken', 'webservice');
7982 $return .= $OUTPUT->box_end();
7983 // add a token to the table
7984 $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
7985 $return .= get_string('add')."</a>";
7987 return highlight($query, $return);
7993 * Colour picker
7995 * @copyright 2010 Sam Hemelryk
7996 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7998 class admin_setting_configcolourpicker extends admin_setting {
8001 * Information for previewing the colour
8003 * @var array|null
8005 protected $previewconfig = null;
8009 * @param string $name
8010 * @param string $visiblename
8011 * @param string $description
8012 * @param string $defaultsetting
8013 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
8015 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig=null) {
8016 $this->previewconfig = $previewconfig;
8017 parent::__construct($name, $visiblename, $description, $defaultsetting);
8021 * Return the setting
8023 * @return mixed returns config if successful else null
8025 public function get_setting() {
8026 return $this->config_read($this->name);
8030 * Saves the setting
8032 * @param string $data
8033 * @return bool
8035 public function write_setting($data) {
8036 $data = $this->validate($data);
8037 if ($data === false) {
8038 return get_string('validateerror', 'admin');
8040 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
8044 * Validates the colour that was entered by the user
8046 * @param string $data
8047 * @return string|false
8049 protected function validate($data) {
8051 * List of valid HTML colour names
8053 * @var array
8055 $colornames = array(
8056 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
8057 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
8058 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
8059 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
8060 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
8061 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
8062 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
8063 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
8064 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
8065 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
8066 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
8067 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
8068 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
8069 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
8070 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
8071 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
8072 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
8073 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
8074 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
8075 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
8076 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
8077 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
8078 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
8079 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
8080 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
8081 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
8082 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
8083 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
8084 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
8085 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
8086 'whitesmoke', 'yellow', 'yellowgreen'
8089 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
8090 if (strpos($data, '#')!==0) {
8091 $data = '#'.$data;
8093 return $data;
8094 } else if (in_array(strtolower($data), $colornames)) {
8095 return $data;
8096 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
8097 return $data;
8098 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
8099 return $data;
8100 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
8101 return $data;
8102 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
8103 return $data;
8104 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
8105 return $data;
8106 } else if (empty($data)) {
8107 return $this->defaultsetting;
8108 } else {
8109 return false;
8114 * Generates the HTML for the setting
8116 * @global moodle_page $PAGE
8117 * @global core_renderer $OUTPUT
8118 * @param string $data
8119 * @param string $query
8121 public function output_html($data, $query = '') {
8122 global $PAGE, $OUTPUT;
8123 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
8124 $content = html_writer::start_tag('div', array('class'=>'form-colourpicker defaultsnext'));
8125 $content .= html_writer::tag('div', $OUTPUT->pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', array('class'=>'loadingicon')), array('class'=>'admin_colourpicker clearfix'));
8126 $content .= html_writer::empty_tag('input', array('type'=>'text','id'=>$this->get_id(), 'name'=>$this->get_full_name(), 'value'=>$data, 'size'=>'12'));
8127 if (!empty($this->previewconfig)) {
8128 $content .= html_writer::empty_tag('input', array('type'=>'button','id'=>$this->get_id().'_preview', 'value'=>get_string('preview'), 'class'=>'admin_colourpicker_preview'));
8130 $content .= html_writer::end_tag('div');
8131 return format_admin_setting($this, $this->visiblename, $content, $this->description, false, '', $this->get_defaultsetting(), $query);
8137 * Class used for uploading of one file into file storage,
8138 * the file name is stored in config table.
8140 * Please note you need to implement your own '_pluginfile' callback function,
8141 * this setting only stores the file, it does not deal with file serving.
8143 * @copyright 2013 Petr Skoda {@link http://skodak.org}
8144 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8146 class admin_setting_configstoredfile extends admin_setting {
8147 /** @var array file area options - should be one file only */
8148 protected $options;
8149 /** @var string name of the file area */
8150 protected $filearea;
8151 /** @var int intemid */
8152 protected $itemid;
8153 /** @var string used for detection of changes */
8154 protected $oldhashes;
8157 * Create new stored file setting.
8159 * @param string $name low level setting name
8160 * @param string $visiblename human readable setting name
8161 * @param string $description description of setting
8162 * @param mixed $filearea file area for file storage
8163 * @param int $itemid itemid for file storage
8164 * @param array $options file area options
8166 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
8167 parent::__construct($name, $visiblename, $description, '');
8168 $this->filearea = $filearea;
8169 $this->itemid = $itemid;
8170 $this->options = (array)$options;
8174 * Applies defaults and returns all options.
8175 * @return array
8177 protected function get_options() {
8178 global $CFG;
8180 require_once("$CFG->libdir/filelib.php");
8181 require_once("$CFG->dirroot/repository/lib.php");
8182 $defaults = array(
8183 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
8184 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
8185 'context' => context_system::instance());
8186 foreach($this->options as $k => $v) {
8187 $defaults[$k] = $v;
8190 return $defaults;
8193 public function get_setting() {
8194 return $this->config_read($this->name);
8197 public function write_setting($data) {
8198 global $USER;
8200 // Let's not deal with validation here, this is for admins only.
8201 $current = $this->get_setting();
8202 if (empty($data)) {
8203 // Most probably applying default settings.
8204 if ($current === null) {
8205 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
8207 return '';
8208 } else if (!is_number($data)) {
8209 // Draft item id is expected here!
8210 return get_string('errorsetting', 'admin');
8213 $options = $this->get_options();
8214 $fs = get_file_storage();
8215 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8217 $this->oldhashes = null;
8218 if ($current) {
8219 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8220 if ($file = $fs->get_file_by_hash($hash)) {
8221 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
8223 unset($file);
8226 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
8227 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
8228 $usercontext = context_user::instance($USER->id);
8229 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.')) {
8230 return get_string('errorsetting', 'admin');
8234 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8235 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
8237 $filepath = '';
8238 if ($files) {
8239 /** @var stored_file $file */
8240 $file = reset($files);
8241 $filepath = $file->get_filepath().$file->get_filename();
8244 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
8247 public function post_write_settings($original) {
8248 $options = $this->get_options();
8249 $fs = get_file_storage();
8250 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8252 $current = $this->get_setting();
8253 $newhashes = null;
8254 if ($current) {
8255 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8256 if ($file = $fs->get_file_by_hash($hash)) {
8257 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
8259 unset($file);
8262 if ($this->oldhashes === $newhashes) {
8263 $this->oldhashes = null;
8264 return false;
8266 $this->oldhashes = null;
8268 $callbackfunction = $this->updatedcallback;
8269 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
8270 $callbackfunction($this->get_full_name());
8272 return true;
8275 public function output_html($data, $query = '') {
8276 global $PAGE, $CFG;
8278 $options = $this->get_options();
8279 $id = $this->get_id();
8280 $elname = $this->get_full_name();
8281 $draftitemid = file_get_submitted_draft_itemid($elname);
8282 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8283 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8285 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
8286 require_once("$CFG->dirroot/lib/form/filemanager.php");
8288 $fmoptions = new stdClass();
8289 $fmoptions->mainfile = $options['mainfile'];
8290 $fmoptions->maxbytes = $options['maxbytes'];
8291 $fmoptions->maxfiles = $options['maxfiles'];
8292 $fmoptions->client_id = uniqid();
8293 $fmoptions->itemid = $draftitemid;
8294 $fmoptions->subdirs = $options['subdirs'];
8295 $fmoptions->target = $id;
8296 $fmoptions->accepted_types = $options['accepted_types'];
8297 $fmoptions->return_types = $options['return_types'];
8298 $fmoptions->context = $options['context'];
8299 $fmoptions->areamaxbytes = $options['areamaxbytes'];
8301 $fm = new form_filemanager($fmoptions);
8302 $output = $PAGE->get_renderer('core', 'files');
8303 $html = $output->render($fm);
8305 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
8306 $html .= '<input value="" id="'.$id.'" type="hidden" />';
8308 return format_admin_setting($this, $this->visiblename,
8309 '<div class="form-filemanager">'.$html.'</div>', $this->description, true, '', '', $query);
8315 * Administration interface for user specified regular expressions for device detection.
8317 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8319 class admin_setting_devicedetectregex extends admin_setting {
8322 * Calls parent::__construct with specific args
8324 * @param string $name
8325 * @param string $visiblename
8326 * @param string $description
8327 * @param mixed $defaultsetting
8329 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
8330 global $CFG;
8331 parent::__construct($name, $visiblename, $description, $defaultsetting);
8335 * Return the current setting(s)
8337 * @return array Current settings array
8339 public function get_setting() {
8340 global $CFG;
8342 $config = $this->config_read($this->name);
8343 if (is_null($config)) {
8344 return null;
8347 return $this->prepare_form_data($config);
8351 * Save selected settings
8353 * @param array $data Array of settings to save
8354 * @return bool
8356 public function write_setting($data) {
8357 if (empty($data)) {
8358 $data = array();
8361 if ($this->config_write($this->name, $this->process_form_data($data))) {
8362 return ''; // success
8363 } else {
8364 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
8369 * Return XHTML field(s) for regexes
8371 * @param array $data Array of options to set in HTML
8372 * @return string XHTML string for the fields and wrapping div(s)
8374 public function output_html($data, $query='') {
8375 global $OUTPUT;
8377 $out = html_writer::start_tag('table', array('border' => 1, 'class' => 'generaltable'));
8378 $out .= html_writer::start_tag('thead');
8379 $out .= html_writer::start_tag('tr');
8380 $out .= html_writer::tag('th', get_string('devicedetectregexexpression', 'admin'));
8381 $out .= html_writer::tag('th', get_string('devicedetectregexvalue', 'admin'));
8382 $out .= html_writer::end_tag('tr');
8383 $out .= html_writer::end_tag('thead');
8384 $out .= html_writer::start_tag('tbody');
8386 if (empty($data)) {
8387 $looplimit = 1;
8388 } else {
8389 $looplimit = (count($data)/2)+1;
8392 for ($i=0; $i<$looplimit; $i++) {
8393 $out .= html_writer::start_tag('tr');
8395 $expressionname = 'expression'.$i;
8397 if (!empty($data[$expressionname])){
8398 $expression = $data[$expressionname];
8399 } else {
8400 $expression = '';
8403 $out .= html_writer::tag('td',
8404 html_writer::empty_tag('input',
8405 array(
8406 'type' => 'text',
8407 'class' => 'form-text',
8408 'name' => $this->get_full_name().'[expression'.$i.']',
8409 'value' => $expression,
8411 ), array('class' => 'c'.$i)
8414 $valuename = 'value'.$i;
8416 if (!empty($data[$valuename])){
8417 $value = $data[$valuename];
8418 } else {
8419 $value= '';
8422 $out .= html_writer::tag('td',
8423 html_writer::empty_tag('input',
8424 array(
8425 'type' => 'text',
8426 'class' => 'form-text',
8427 'name' => $this->get_full_name().'[value'.$i.']',
8428 'value' => $value,
8430 ), array('class' => 'c'.$i)
8433 $out .= html_writer::end_tag('tr');
8436 $out .= html_writer::end_tag('tbody');
8437 $out .= html_writer::end_tag('table');
8439 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', null, $query);
8443 * Converts the string of regexes
8445 * @see self::process_form_data()
8446 * @param $regexes string of regexes
8447 * @return array of form fields and their values
8449 protected function prepare_form_data($regexes) {
8451 $regexes = json_decode($regexes);
8453 $form = array();
8455 $i = 0;
8457 foreach ($regexes as $value => $regex) {
8458 $expressionname = 'expression'.$i;
8459 $valuename = 'value'.$i;
8461 $form[$expressionname] = $regex;
8462 $form[$valuename] = $value;
8463 $i++;
8466 return $form;
8470 * Converts the data from admin settings form into a string of regexes
8472 * @see self::prepare_form_data()
8473 * @param array $data array of admin form fields and values
8474 * @return false|string of regexes
8476 protected function process_form_data(array $form) {
8478 $count = count($form); // number of form field values
8480 if ($count % 2) {
8481 // we must get five fields per expression
8482 return false;
8485 $regexes = array();
8486 for ($i = 0; $i < $count / 2; $i++) {
8487 $expressionname = "expression".$i;
8488 $valuename = "value".$i;
8490 $expression = trim($form['expression'.$i]);
8491 $value = trim($form['value'.$i]);
8493 if (empty($expression)){
8494 continue;
8497 $regexes[$value] = $expression;
8500 $regexes = json_encode($regexes);
8502 return $regexes;
8507 * Multiselect for current modules
8509 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8511 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
8512 private $excludesystem;
8515 * Calls parent::__construct - note array $choices is not required
8517 * @param string $name setting name
8518 * @param string $visiblename localised setting name
8519 * @param string $description setting description
8520 * @param array $defaultsetting a plain array of default module ids
8521 * @param bool $excludesystem If true, excludes modules with 'system' archetype
8523 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
8524 $excludesystem = true) {
8525 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
8526 $this->excludesystem = $excludesystem;
8530 * Loads an array of current module choices
8532 * @return bool always return true
8534 public function load_choices() {
8535 if (is_array($this->choices)) {
8536 return true;
8538 $this->choices = array();
8540 global $CFG, $DB;
8541 $records = $DB->get_records('modules', array('visible'=>1), 'name');
8542 foreach ($records as $record) {
8543 // Exclude modules if the code doesn't exist
8544 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
8545 // Also exclude system modules (if specified)
8546 if (!($this->excludesystem &&
8547 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
8548 MOD_ARCHETYPE_SYSTEM)) {
8549 $this->choices[$record->id] = $record->name;
8553 return true;