MDL-40490 - database: Fixed a check on database port options.
[moodle.git] / lib / adminlib.php
blobba67ddd0768e09330eed0ad5193cf293bfb0e4fa
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 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
119 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
120 * @uses global $OUTPUT to produce notices and other messages
121 * @return void
123 function uninstall_plugin($type, $name) {
124 global $CFG, $DB, $OUTPUT;
126 // This may take a long time.
127 @set_time_limit(0);
129 // recursively uninstall all module/editor subplugins first
130 if ($type === 'mod' || $type === 'editor') {
131 $base = get_component_directory($type . '_' . $name);
132 if (file_exists("$base/db/subplugins.php")) {
133 $subplugins = array();
134 include("$base/db/subplugins.php");
135 foreach ($subplugins as $subplugintype=>$dir) {
136 $instances = get_plugin_list($subplugintype);
137 foreach ($instances as $subpluginname => $notusedpluginpath) {
138 uninstall_plugin($subplugintype, $subpluginname);
145 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
147 if ($type === 'mod') {
148 $pluginname = $name; // eg. 'forum'
149 if (get_string_manager()->string_exists('modulename', $component)) {
150 $strpluginname = get_string('modulename', $component);
151 } else {
152 $strpluginname = $component;
155 } else {
156 $pluginname = $component;
157 if (get_string_manager()->string_exists('pluginname', $component)) {
158 $strpluginname = get_string('pluginname', $component);
159 } else {
160 $strpluginname = $component;
164 echo $OUTPUT->heading($pluginname);
166 $plugindirectory = get_plugin_directory($type, $name);
167 $uninstalllib = $plugindirectory . '/db/uninstall.php';
168 if (file_exists($uninstalllib)) {
169 require_once($uninstalllib);
170 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
171 if (function_exists($uninstallfunction)) {
172 if (!$uninstallfunction()) {
173 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $pluginname);
178 if ($type === 'mod') {
179 // perform cleanup tasks specific for activity modules
181 if (!$module = $DB->get_record('modules', array('name' => $name))) {
182 print_error('moduledoesnotexist', 'error');
185 // delete all the relevant instances from all course sections
186 if ($coursemods = $DB->get_records('course_modules', array('module' => $module->id))) {
187 foreach ($coursemods as $coursemod) {
188 if (!delete_mod_from_section($coursemod->id, $coursemod->section)) {
189 echo $OUTPUT->notification("Could not delete the $strpluginname with id = $coursemod->id from section $coursemod->section");
194 // clear course.modinfo for courses that used this module
195 $sql = "UPDATE {course}
196 SET modinfo=''
197 WHERE id IN (SELECT DISTINCT course
198 FROM {course_modules}
199 WHERE module=?)";
200 $DB->execute($sql, array($module->id));
202 // delete all the course module records
203 $DB->delete_records('course_modules', array('module' => $module->id));
205 // delete module contexts
206 if ($coursemods) {
207 foreach ($coursemods as $coursemod) {
208 if (!delete_context(CONTEXT_MODULE, $coursemod->id)) {
209 echo $OUTPUT->notification("Could not delete the context for $strpluginname with id = $coursemod->id");
214 // delete the module entry itself
215 $DB->delete_records('modules', array('name' => $module->name));
217 // cleanup the gradebook
218 require_once($CFG->libdir.'/gradelib.php');
219 grade_uninstalled_module($module->name);
221 // Perform any custom uninstall tasks
222 if (file_exists($CFG->dirroot . '/mod/' . $module->name . '/lib.php')) {
223 require_once($CFG->dirroot . '/mod/' . $module->name . '/lib.php');
224 $uninstallfunction = $module->name . '_uninstall';
225 if (function_exists($uninstallfunction)) {
226 debugging("{$uninstallfunction}() has been deprecated. Use the plugin's db/uninstall.php instead", DEBUG_DEVELOPER);
227 if (!$uninstallfunction()) {
228 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $module->name.'!');
233 } else if ($type === 'enrol') {
234 // NOTE: this is a bit brute force way - it will not trigger events and hooks properly
235 // nuke all role assignments
236 role_unassign_all(array('component'=>$component));
237 // purge participants
238 $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($name));
239 // purge enrol instances
240 $DB->delete_records('enrol', array('enrol'=>$name));
241 // tweak enrol settings
242 if (!empty($CFG->enrol_plugins_enabled)) {
243 $enabledenrols = explode(',', $CFG->enrol_plugins_enabled);
244 $enabledenrols = array_unique($enabledenrols);
245 $enabledenrols = array_flip($enabledenrols);
246 unset($enabledenrols[$name]);
247 $enabledenrols = array_flip($enabledenrols);
248 if (is_array($enabledenrols)) {
249 set_config('enrol_plugins_enabled', implode(',', $enabledenrols));
253 } else if ($type === 'block') {
254 if ($block = $DB->get_record('block', array('name'=>$name))) {
255 // Inform block it's about to be deleted
256 if (file_exists("$CFG->dirroot/blocks/$block->name/block_$block->name.php")) {
257 $blockobject = block_instance($block->name);
258 if ($blockobject) {
259 $blockobject->before_delete(); //only if we can create instance, block might have been already removed
263 // First delete instances and related contexts
264 $instances = $DB->get_records('block_instances', array('blockname' => $block->name));
265 foreach($instances as $instance) {
266 blocks_delete_instance($instance);
269 // Delete block
270 $DB->delete_records('block', array('id'=>$block->id));
272 } else if ($type === 'format') {
273 if (($defaultformat = get_config('moodlecourse', 'format')) && $defaultformat !== $name) {
274 $courses = $DB->get_records('course', array('format' => $name), 'id');
275 $data = (object)array('id' => null, 'format' => $defaultformat);
276 foreach ($courses as $record) {
277 $data->id = $record->id;
278 update_course($data);
281 $DB->delete_records('course_format_options', array('format' => $name));
284 // perform clean-up task common for all the plugin/subplugin types
286 //delete the web service functions and pre-built services
287 require_once($CFG->dirroot.'/lib/externallib.php');
288 external_delete_descriptions($component);
290 // delete calendar events
291 $DB->delete_records('event', array('modulename' => $pluginname));
293 // delete all the logs
294 $DB->delete_records('log', array('module' => $pluginname));
296 // delete log_display information
297 $DB->delete_records('log_display', array('component' => $component));
299 // delete the module configuration records
300 unset_all_config_for_plugin($pluginname);
302 // delete message provider
303 message_provider_uninstall($component);
305 // delete message processor
306 if ($type === 'message') {
307 message_processor_uninstall($name);
310 // delete the plugin tables
311 $xmldbfilepath = $plugindirectory . '/db/install.xml';
312 drop_plugin_tables($component, $xmldbfilepath, false);
313 if ($type === 'mod' or $type === 'block') {
314 // non-frankenstyle table prefixes
315 drop_plugin_tables($name, $xmldbfilepath, false);
318 // delete the capabilities that were defined by this module
319 capabilities_cleanup($component);
321 // remove event handlers and dequeue pending events
322 events_uninstall($component);
324 // Delete all remaining files in the filepool owned by the component.
325 $fs = get_file_storage();
326 $fs->delete_component_files($component);
328 // Finally purge all caches.
329 purge_all_caches();
331 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
335 * Returns the version of installed component
337 * @param string $component component name
338 * @param string $source either 'disk' or 'installed' - where to get the version information from
339 * @return string|bool version number or false if the component is not found
341 function get_component_version($component, $source='installed') {
342 global $CFG, $DB;
344 list($type, $name) = normalize_component($component);
346 // moodle core or a core subsystem
347 if ($type === 'core') {
348 if ($source === 'installed') {
349 if (empty($CFG->version)) {
350 return false;
351 } else {
352 return $CFG->version;
354 } else {
355 if (!is_readable($CFG->dirroot.'/version.php')) {
356 return false;
357 } else {
358 $version = null; //initialize variable for IDEs
359 include($CFG->dirroot.'/version.php');
360 return $version;
365 // activity module
366 if ($type === 'mod') {
367 if ($source === 'installed') {
368 return $DB->get_field('modules', 'version', array('name'=>$name));
369 } else {
370 $mods = get_plugin_list('mod');
371 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
372 return false;
373 } else {
374 $module = new stdclass();
375 include($mods[$name].'/version.php');
376 return $module->version;
381 // block
382 if ($type === 'block') {
383 if ($source === 'installed') {
384 return $DB->get_field('block', 'version', array('name'=>$name));
385 } else {
386 $blocks = get_plugin_list('block');
387 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
388 return false;
389 } else {
390 $plugin = new stdclass();
391 include($blocks[$name].'/version.php');
392 return $plugin->version;
397 // all other plugin types
398 if ($source === 'installed') {
399 return get_config($type.'_'.$name, 'version');
400 } else {
401 $plugins = get_plugin_list($type);
402 if (empty($plugins[$name])) {
403 return false;
404 } else {
405 $plugin = new stdclass();
406 include($plugins[$name].'/version.php');
407 return $plugin->version;
413 * Delete all plugin tables
415 * @param string $name Name of plugin, used as table prefix
416 * @param string $file Path to install.xml file
417 * @param bool $feedback defaults to true
418 * @return bool Always returns true
420 function drop_plugin_tables($name, $file, $feedback=true) {
421 global $CFG, $DB;
423 // first try normal delete
424 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
425 return true;
428 // then try to find all tables that start with name and are not in any xml file
429 $used_tables = get_used_table_names();
431 $tables = $DB->get_tables();
433 /// Iterate over, fixing id fields as necessary
434 foreach ($tables as $table) {
435 if (in_array($table, $used_tables)) {
436 continue;
439 if (strpos($table, $name) !== 0) {
440 continue;
443 // found orphan table --> delete it
444 if ($DB->get_manager()->table_exists($table)) {
445 $xmldb_table = new xmldb_table($table);
446 $DB->get_manager()->drop_table($xmldb_table);
450 return true;
454 * Returns names of all known tables == tables that moodle knows about.
456 * @return array Array of lowercase table names
458 function get_used_table_names() {
459 $table_names = array();
460 $dbdirs = get_db_directories();
462 foreach ($dbdirs as $dbdir) {
463 $file = $dbdir.'/install.xml';
465 $xmldb_file = new xmldb_file($file);
467 if (!$xmldb_file->fileExists()) {
468 continue;
471 $loaded = $xmldb_file->loadXMLStructure();
472 $structure = $xmldb_file->getStructure();
474 if ($loaded and $tables = $structure->getTables()) {
475 foreach($tables as $table) {
476 $table_names[] = strtolower($table->getName());
481 return $table_names;
485 * Returns list of all directories where we expect install.xml files
486 * @return array Array of paths
488 function get_db_directories() {
489 global $CFG;
491 $dbdirs = array();
493 /// First, the main one (lib/db)
494 $dbdirs[] = $CFG->libdir.'/db';
496 /// Then, all the ones defined by get_plugin_types()
497 $plugintypes = get_plugin_types();
498 foreach ($plugintypes as $plugintype => $pluginbasedir) {
499 if ($plugins = get_plugin_list($plugintype)) {
500 foreach ($plugins as $plugin => $plugindir) {
501 $dbdirs[] = $plugindir.'/db';
506 return $dbdirs;
510 * Try to obtain or release the cron lock.
511 * @param string $name name of lock
512 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
513 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
514 * @return bool true if lock obtained
516 function set_cron_lock($name, $until, $ignorecurrent=false) {
517 global $DB;
518 if (empty($name)) {
519 debugging("Tried to get a cron lock for a null fieldname");
520 return false;
523 // remove lock by force == remove from config table
524 if (is_null($until)) {
525 set_config($name, null);
526 return true;
529 if (!$ignorecurrent) {
530 // read value from db - other processes might have changed it
531 $value = $DB->get_field('config', 'value', array('name'=>$name));
533 if ($value and $value > time()) {
534 //lock active
535 return false;
539 set_config($name, $until);
540 return true;
544 * Test if and critical warnings are present
545 * @return bool
547 function admin_critical_warnings_present() {
548 global $SESSION;
550 if (!has_capability('moodle/site:config', context_system::instance())) {
551 return 0;
554 if (!isset($SESSION->admin_critical_warning)) {
555 $SESSION->admin_critical_warning = 0;
556 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
557 $SESSION->admin_critical_warning = 1;
561 return $SESSION->admin_critical_warning;
565 * Detects if float supports at least 10 decimal digits
567 * Detects if float supports at least 10 decimal digits
568 * and also if float-->string conversion works as expected.
570 * @return bool true if problem found
572 function is_float_problem() {
573 $num1 = 2009010200.01;
574 $num2 = 2009010200.02;
576 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
580 * Try to verify that dataroot is not accessible from web.
582 * Try to verify that dataroot is not accessible from web.
583 * It is not 100% correct but might help to reduce number of vulnerable sites.
584 * Protection from httpd.conf and .htaccess is not detected properly.
586 * @uses INSECURE_DATAROOT_WARNING
587 * @uses INSECURE_DATAROOT_ERROR
588 * @param bool $fetchtest try to test public access by fetching file, default false
589 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
591 function is_dataroot_insecure($fetchtest=false) {
592 global $CFG;
594 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
596 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
597 $rp = strrev(trim($rp, '/'));
598 $rp = explode('/', $rp);
599 foreach($rp as $r) {
600 if (strpos($siteroot, '/'.$r.'/') === 0) {
601 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
602 } else {
603 break; // probably alias root
607 $siteroot = strrev($siteroot);
608 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
610 if (strpos($dataroot, $siteroot) !== 0) {
611 return false;
614 if (!$fetchtest) {
615 return INSECURE_DATAROOT_WARNING;
618 // now try all methods to fetch a test file using http protocol
620 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
621 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
622 $httpdocroot = $matches[1];
623 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
624 make_upload_directory('diag');
625 $testfile = $CFG->dataroot.'/diag/public.txt';
626 if (!file_exists($testfile)) {
627 file_put_contents($testfile, 'test file, do not delete');
629 $teststr = trim(file_get_contents($testfile));
630 if (empty($teststr)) {
631 // hmm, strange
632 return INSECURE_DATAROOT_WARNING;
635 $testurl = $datarooturl.'/diag/public.txt';
636 if (extension_loaded('curl') and
637 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
638 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
639 ($ch = @curl_init($testurl)) !== false) {
640 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
641 curl_setopt($ch, CURLOPT_HEADER, false);
642 $data = curl_exec($ch);
643 if (!curl_errno($ch)) {
644 $data = trim($data);
645 if ($data === $teststr) {
646 curl_close($ch);
647 return INSECURE_DATAROOT_ERROR;
650 curl_close($ch);
653 if ($data = @file_get_contents($testurl)) {
654 $data = trim($data);
655 if ($data === $teststr) {
656 return INSECURE_DATAROOT_ERROR;
660 preg_match('|https?://([^/]+)|i', $testurl, $matches);
661 $sitename = $matches[1];
662 $error = 0;
663 if ($fp = @fsockopen($sitename, 80, $error)) {
664 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
665 $localurl = $matches[1];
666 $out = "GET $localurl HTTP/1.1\r\n";
667 $out .= "Host: $sitename\r\n";
668 $out .= "Connection: Close\r\n\r\n";
669 fwrite($fp, $out);
670 $data = '';
671 $incoming = false;
672 while (!feof($fp)) {
673 if ($incoming) {
674 $data .= fgets($fp, 1024);
675 } else if (@fgets($fp, 1024) === "\r\n") {
676 $incoming = true;
679 fclose($fp);
680 $data = trim($data);
681 if ($data === $teststr) {
682 return INSECURE_DATAROOT_ERROR;
686 return INSECURE_DATAROOT_WARNING;
690 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
692 function enable_cli_maintenance_mode() {
693 global $CFG;
695 if (file_exists("$CFG->dataroot/climaintenance.html")) {
696 unlink("$CFG->dataroot/climaintenance.html");
699 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
700 $data = $CFG->maintenance_message;
701 $data = bootstrap_renderer::early_error_content($data, null, null, null);
702 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
704 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
705 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
707 } else {
708 $data = get_string('sitemaintenance', 'admin');
709 $data = bootstrap_renderer::early_error_content($data, null, null, null);
710 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
713 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
714 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
717 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
721 * Interface for anything appearing in the admin tree
723 * The interface that is implemented by anything that appears in the admin tree
724 * block. It forces inheriting classes to define a method for checking user permissions
725 * and methods for finding something in the admin tree.
727 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
729 interface part_of_admin_tree {
732 * Finds a named part_of_admin_tree.
734 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
735 * and not parentable_part_of_admin_tree, then this function should only check if
736 * $this->name matches $name. If it does, it should return a reference to $this,
737 * otherwise, it should return a reference to NULL.
739 * If a class inherits parentable_part_of_admin_tree, this method should be called
740 * recursively on all child objects (assuming, of course, the parent object's name
741 * doesn't match the search criterion).
743 * @param string $name The internal name of the part_of_admin_tree we're searching for.
744 * @return mixed An object reference or a NULL reference.
746 public function locate($name);
749 * Removes named part_of_admin_tree.
751 * @param string $name The internal name of the part_of_admin_tree we want to remove.
752 * @return bool success.
754 public function prune($name);
757 * Search using query
758 * @param string $query
759 * @return mixed array-object structure of found settings and pages
761 public function search($query);
764 * Verifies current user's access to this part_of_admin_tree.
766 * Used to check if the current user has access to this part of the admin tree or
767 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
768 * then this method is usually just a call to has_capability() in the site context.
770 * If a class inherits parentable_part_of_admin_tree, this method should return the
771 * logical OR of the return of check_access() on all child objects.
773 * @return bool True if the user has access, false if she doesn't.
775 public function check_access();
778 * Mostly useful for removing of some parts of the tree in admin tree block.
780 * @return True is hidden from normal list view
782 public function is_hidden();
785 * Show we display Save button at the page bottom?
786 * @return bool
788 public function show_save();
793 * Interface implemented by any part_of_admin_tree that has children.
795 * The interface implemented by any part_of_admin_tree that can be a parent
796 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
797 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
798 * include an add method for adding other part_of_admin_tree objects as children.
800 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
802 interface parentable_part_of_admin_tree extends part_of_admin_tree {
805 * Adds a part_of_admin_tree object to the admin tree.
807 * Used to add a part_of_admin_tree object to this object or a child of this
808 * object. $something should only be added if $destinationname matches
809 * $this->name. If it doesn't, add should be called on child objects that are
810 * also parentable_part_of_admin_tree's.
812 * $something should be appended as the last child in the $destinationname. If the
813 * $beforesibling is specified, $something should be prepended to it. If the given
814 * sibling is not found, $something should be appended to the end of $destinationname
815 * and a developer debugging message should be displayed.
817 * @param string $destinationname The internal name of the new parent for $something.
818 * @param part_of_admin_tree $something The object to be added.
819 * @return bool True on success, false on failure.
821 public function add($destinationname, $something, $beforesibling = null);
827 * The object used to represent folders (a.k.a. categories) in the admin tree block.
829 * Each admin_category object contains a number of part_of_admin_tree objects.
831 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
833 class admin_category implements parentable_part_of_admin_tree {
835 /** @var mixed An array of part_of_admin_tree objects that are this object's children */
836 public $children;
837 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
838 public $name;
839 /** @var string The displayed name for this category. Usually obtained through get_string() */
840 public $visiblename;
841 /** @var bool Should this category be hidden in admin tree block? */
842 public $hidden;
843 /** @var mixed Either a string or an array or strings */
844 public $path;
845 /** @var mixed Either a string or an array or strings */
846 public $visiblepath;
848 /** @var array fast lookup category cache, all categories of one tree point to one cache */
849 protected $category_cache;
852 * Constructor for an empty admin category
854 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
855 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
856 * @param bool $hidden hide category in admin tree block, defaults to false
858 public function __construct($name, $visiblename, $hidden=false) {
859 $this->children = array();
860 $this->name = $name;
861 $this->visiblename = $visiblename;
862 $this->hidden = $hidden;
866 * Returns a reference to the part_of_admin_tree object with internal name $name.
868 * @param string $name The internal name of the object we want.
869 * @param bool $findpath initialize path and visiblepath arrays
870 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
871 * defaults to false
873 public function locate($name, $findpath=false) {
874 if (!isset($this->category_cache[$this->name])) {
875 // somebody much have purged the cache
876 $this->category_cache[$this->name] = $this;
879 if ($this->name == $name) {
880 if ($findpath) {
881 $this->visiblepath[] = $this->visiblename;
882 $this->path[] = $this->name;
884 return $this;
887 // quick category lookup
888 if (!$findpath and isset($this->category_cache[$name])) {
889 return $this->category_cache[$name];
892 $return = NULL;
893 foreach($this->children as $childid=>$unused) {
894 if ($return = $this->children[$childid]->locate($name, $findpath)) {
895 break;
899 if (!is_null($return) and $findpath) {
900 $return->visiblepath[] = $this->visiblename;
901 $return->path[] = $this->name;
904 return $return;
908 * Search using query
910 * @param string query
911 * @return mixed array-object structure of found settings and pages
913 public function search($query) {
914 $result = array();
915 foreach ($this->children as $child) {
916 $subsearch = $child->search($query);
917 if (!is_array($subsearch)) {
918 debugging('Incorrect search result from '.$child->name);
919 continue;
921 $result = array_merge($result, $subsearch);
923 return $result;
927 * Removes part_of_admin_tree object with internal name $name.
929 * @param string $name The internal name of the object we want to remove.
930 * @return bool success
932 public function prune($name) {
934 if ($this->name == $name) {
935 return false; //can not remove itself
938 foreach($this->children as $precedence => $child) {
939 if ($child->name == $name) {
940 // clear cache and delete self
941 while($this->category_cache) {
942 // delete the cache, but keep the original array address
943 array_pop($this->category_cache);
945 unset($this->children[$precedence]);
946 return true;
947 } else if ($this->children[$precedence]->prune($name)) {
948 return true;
951 return false;
955 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
957 * By default the new part of the tree is appended as the last child of the parent. You
958 * can specify a sibling node that the new part should be prepended to. If the given
959 * sibling is not found, the part is appended to the end (as it would be by default) and
960 * a developer debugging message is displayed.
962 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
963 * @param string $destinationame The internal name of the immediate parent that we want for $something.
964 * @param mixed $something A part_of_admin_tree or setting instance to be added.
965 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
966 * @return bool True if successfully added, false if $something can not be added.
968 public function add($parentname, $something, $beforesibling = null) {
969 $parent = $this->locate($parentname);
970 if (is_null($parent)) {
971 debugging('parent does not exist!');
972 return false;
975 if ($something instanceof part_of_admin_tree) {
976 if (!($parent instanceof parentable_part_of_admin_tree)) {
977 debugging('error - parts of tree can be inserted only into parentable parts');
978 return false;
980 if (debugging('', DEBUG_DEVELOPER) && !is_null($this->locate($something->name))) {
981 // The name of the node is already used, simply warn the developer that this should not happen.
982 // It is intentional to check for the debug level before performing the check.
983 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
985 if (is_null($beforesibling)) {
986 // Append $something as the parent's last child.
987 $parent->children[] = $something;
988 } else {
989 if (!is_string($beforesibling) or trim($beforesibling) === '') {
990 throw new coding_exception('Unexpected value of the beforesibling parameter');
992 // Try to find the position of the sibling.
993 $siblingposition = null;
994 foreach ($parent->children as $childposition => $child) {
995 if ($child->name === $beforesibling) {
996 $siblingposition = $childposition;
997 break;
1000 if (is_null($siblingposition)) {
1001 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
1002 $parent->children[] = $something;
1003 } else {
1004 $parent->children = array_merge(
1005 array_slice($parent->children, 0, $siblingposition),
1006 array($something),
1007 array_slice($parent->children, $siblingposition)
1011 if ($something instanceof admin_category) {
1012 if (isset($this->category_cache[$something->name])) {
1013 debugging('Duplicate admin category name: '.$something->name);
1014 } else {
1015 $this->category_cache[$something->name] = $something;
1016 $something->category_cache =& $this->category_cache;
1017 foreach ($something->children as $child) {
1018 // just in case somebody already added subcategories
1019 if ($child instanceof admin_category) {
1020 if (isset($this->category_cache[$child->name])) {
1021 debugging('Duplicate admin category name: '.$child->name);
1022 } else {
1023 $this->category_cache[$child->name] = $child;
1024 $child->category_cache =& $this->category_cache;
1030 return true;
1032 } else {
1033 debugging('error - can not add this element');
1034 return false;
1040 * Checks if the user has access to anything in this category.
1042 * @return bool True if the user has access to at least one child in this category, false otherwise.
1044 public function check_access() {
1045 foreach ($this->children as $child) {
1046 if ($child->check_access()) {
1047 return true;
1050 return false;
1054 * Is this category hidden in admin tree block?
1056 * @return bool True if hidden
1058 public function is_hidden() {
1059 return $this->hidden;
1063 * Show we display Save button at the page bottom?
1064 * @return bool
1066 public function show_save() {
1067 foreach ($this->children as $child) {
1068 if ($child->show_save()) {
1069 return true;
1072 return false;
1078 * Root of admin settings tree, does not have any parent.
1080 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1082 class admin_root extends admin_category {
1083 /** @var array List of errors */
1084 public $errors;
1085 /** @var string search query */
1086 public $search;
1087 /** @var bool full tree flag - true means all settings required, false only pages required */
1088 public $fulltree;
1089 /** @var bool flag indicating loaded tree */
1090 public $loaded;
1091 /** @var mixed site custom defaults overriding defaults in settings files*/
1092 public $custom_defaults;
1095 * @param bool $fulltree true means all settings required,
1096 * false only pages required
1098 public function __construct($fulltree) {
1099 global $CFG;
1101 parent::__construct('root', get_string('administration'), false);
1102 $this->errors = array();
1103 $this->search = '';
1104 $this->fulltree = $fulltree;
1105 $this->loaded = false;
1107 $this->category_cache = array();
1109 // load custom defaults if found
1110 $this->custom_defaults = null;
1111 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1112 if (is_readable($defaultsfile)) {
1113 $defaults = array();
1114 include($defaultsfile);
1115 if (is_array($defaults) and count($defaults)) {
1116 $this->custom_defaults = $defaults;
1122 * Empties children array, and sets loaded to false
1124 * @param bool $requirefulltree
1126 public function purge_children($requirefulltree) {
1127 $this->children = array();
1128 $this->fulltree = ($requirefulltree || $this->fulltree);
1129 $this->loaded = false;
1130 //break circular dependencies - this helps PHP 5.2
1131 while($this->category_cache) {
1132 array_pop($this->category_cache);
1134 $this->category_cache = array();
1140 * Links external PHP pages into the admin tree.
1142 * See detailed usage example at the top of this document (adminlib.php)
1144 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1146 class admin_externalpage implements part_of_admin_tree {
1148 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1149 public $name;
1151 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1152 public $visiblename;
1154 /** @var string The external URL that we should link to when someone requests this external page. */
1155 public $url;
1157 /** @var string The role capability/permission a user must have to access this external page. */
1158 public $req_capability;
1160 /** @var object The context in which capability/permission should be checked, default is site context. */
1161 public $context;
1163 /** @var bool hidden in admin tree block. */
1164 public $hidden;
1166 /** @var mixed either string or array of string */
1167 public $path;
1169 /** @var array list of visible names of page parents */
1170 public $visiblepath;
1173 * Constructor for adding an external page into the admin tree.
1175 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1176 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1177 * @param string $url The external URL that we should link to when someone requests this external page.
1178 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1179 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1180 * @param stdClass $context The context the page relates to. Not sure what happens
1181 * if you specify something other than system or front page. Defaults to system.
1183 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1184 $this->name = $name;
1185 $this->visiblename = $visiblename;
1186 $this->url = $url;
1187 if (is_array($req_capability)) {
1188 $this->req_capability = $req_capability;
1189 } else {
1190 $this->req_capability = array($req_capability);
1192 $this->hidden = $hidden;
1193 $this->context = $context;
1197 * Returns a reference to the part_of_admin_tree object with internal name $name.
1199 * @param string $name The internal name of the object we want.
1200 * @param bool $findpath defaults to false
1201 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1203 public function locate($name, $findpath=false) {
1204 if ($this->name == $name) {
1205 if ($findpath) {
1206 $this->visiblepath = array($this->visiblename);
1207 $this->path = array($this->name);
1209 return $this;
1210 } else {
1211 $return = NULL;
1212 return $return;
1217 * This function always returns false, required function by interface
1219 * @param string $name
1220 * @return false
1222 public function prune($name) {
1223 return false;
1227 * Search using query
1229 * @param string $query
1230 * @return mixed array-object structure of found settings and pages
1232 public function search($query) {
1233 $found = false;
1234 if (strpos(strtolower($this->name), $query) !== false) {
1235 $found = true;
1236 } else if (strpos(textlib::strtolower($this->visiblename), $query) !== false) {
1237 $found = true;
1239 if ($found) {
1240 $result = new stdClass();
1241 $result->page = $this;
1242 $result->settings = array();
1243 return array($this->name => $result);
1244 } else {
1245 return array();
1250 * Determines if the current user has access to this external page based on $this->req_capability.
1252 * @return bool True if user has access, false otherwise.
1254 public function check_access() {
1255 global $CFG;
1256 $context = empty($this->context) ? context_system::instance() : $this->context;
1257 foreach($this->req_capability as $cap) {
1258 if (has_capability($cap, $context)) {
1259 return true;
1262 return false;
1266 * Is this external page hidden in admin tree block?
1268 * @return bool True if hidden
1270 public function is_hidden() {
1271 return $this->hidden;
1275 * Show we display Save button at the page bottom?
1276 * @return bool
1278 public function show_save() {
1279 return false;
1285 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1287 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1289 class admin_settingpage implements part_of_admin_tree {
1291 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1292 public $name;
1294 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1295 public $visiblename;
1297 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1298 public $settings;
1300 /** @var string The role capability/permission a user must have to access this external page. */
1301 public $req_capability;
1303 /** @var object The context in which capability/permission should be checked, default is site context. */
1304 public $context;
1306 /** @var bool hidden in admin tree block. */
1307 public $hidden;
1309 /** @var mixed string of paths or array of strings of paths */
1310 public $path;
1312 /** @var array list of visible names of page parents */
1313 public $visiblepath;
1316 * see admin_settingpage for details of this function
1318 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1319 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1320 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1321 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1322 * @param stdClass $context The context the page relates to. Not sure what happens
1323 * if you specify something other than system or front page. Defaults to system.
1325 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1326 $this->settings = new stdClass();
1327 $this->name = $name;
1328 $this->visiblename = $visiblename;
1329 if (is_array($req_capability)) {
1330 $this->req_capability = $req_capability;
1331 } else {
1332 $this->req_capability = array($req_capability);
1334 $this->hidden = $hidden;
1335 $this->context = $context;
1339 * see admin_category
1341 * @param string $name
1342 * @param bool $findpath
1343 * @return mixed Object (this) if name == this->name, else returns null
1345 public function locate($name, $findpath=false) {
1346 if ($this->name == $name) {
1347 if ($findpath) {
1348 $this->visiblepath = array($this->visiblename);
1349 $this->path = array($this->name);
1351 return $this;
1352 } else {
1353 $return = NULL;
1354 return $return;
1359 * Search string in settings page.
1361 * @param string $query
1362 * @return array
1364 public function search($query) {
1365 $found = array();
1367 foreach ($this->settings as $setting) {
1368 if ($setting->is_related($query)) {
1369 $found[] = $setting;
1373 if ($found) {
1374 $result = new stdClass();
1375 $result->page = $this;
1376 $result->settings = $found;
1377 return array($this->name => $result);
1380 $found = false;
1381 if (strpos(strtolower($this->name), $query) !== false) {
1382 $found = true;
1383 } else if (strpos(textlib::strtolower($this->visiblename), $query) !== false) {
1384 $found = true;
1386 if ($found) {
1387 $result = new stdClass();
1388 $result->page = $this;
1389 $result->settings = array();
1390 return array($this->name => $result);
1391 } else {
1392 return array();
1397 * This function always returns false, required by interface
1399 * @param string $name
1400 * @return bool Always false
1402 public function prune($name) {
1403 return false;
1407 * adds an admin_setting to this admin_settingpage
1409 * 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
1410 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1412 * @param object $setting is the admin_setting object you want to add
1413 * @return bool true if successful, false if not
1415 public function add($setting) {
1416 if (!($setting instanceof admin_setting)) {
1417 debugging('error - not a setting instance');
1418 return false;
1421 $this->settings->{$setting->name} = $setting;
1422 return true;
1426 * see admin_externalpage
1428 * @return bool Returns true for yes false for no
1430 public function check_access() {
1431 global $CFG;
1432 $context = empty($this->context) ? context_system::instance() : $this->context;
1433 foreach($this->req_capability as $cap) {
1434 if (has_capability($cap, $context)) {
1435 return true;
1438 return false;
1442 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1443 * @return string Returns an XHTML string
1445 public function output_html() {
1446 $adminroot = admin_get_root();
1447 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1448 foreach($this->settings as $setting) {
1449 $fullname = $setting->get_full_name();
1450 if (array_key_exists($fullname, $adminroot->errors)) {
1451 $data = $adminroot->errors[$fullname]->data;
1452 } else {
1453 $data = $setting->get_setting();
1454 // do not use defaults if settings not available - upgrade settings handles the defaults!
1456 $return .= $setting->output_html($data);
1458 $return .= '</fieldset>';
1459 return $return;
1463 * Is this settings page hidden in admin tree block?
1465 * @return bool True if hidden
1467 public function is_hidden() {
1468 return $this->hidden;
1472 * Show we display Save button at the page bottom?
1473 * @return bool
1475 public function show_save() {
1476 foreach($this->settings as $setting) {
1477 if (empty($setting->nosave)) {
1478 return true;
1481 return false;
1487 * Admin settings class. Only exists on setting pages.
1488 * Read & write happens at this level; no authentication.
1490 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1492 abstract class admin_setting {
1493 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1494 public $name;
1495 /** @var string localised name */
1496 public $visiblename;
1497 /** @var string localised long description in Markdown format */
1498 public $description;
1499 /** @var mixed Can be string or array of string */
1500 public $defaultsetting;
1501 /** @var string */
1502 public $updatedcallback;
1503 /** @var mixed can be String or Null. Null means main config table */
1504 public $plugin; // null means main config table
1505 /** @var bool true indicates this setting does not actually save anything, just information */
1506 public $nosave = false;
1507 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1508 public $affectsmodinfo = false;
1509 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1510 private $flags = array();
1513 * Constructor
1514 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1515 * or 'myplugin/mysetting' for ones in config_plugins.
1516 * @param string $visiblename localised name
1517 * @param string $description localised long description
1518 * @param mixed $defaultsetting string or array depending on implementation
1520 public function __construct($name, $visiblename, $description, $defaultsetting) {
1521 $this->parse_setting_name($name);
1522 $this->visiblename = $visiblename;
1523 $this->description = $description;
1524 $this->defaultsetting = $defaultsetting;
1528 * Generic function to add a flag to this admin setting.
1530 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1531 * @param bool $default - The default for the flag
1532 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1533 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1535 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1536 if (empty($this->flags[$shortname])) {
1537 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1538 } else {
1539 $this->flags[$shortname]->set_options($enabled, $default);
1544 * Set the enabled options flag on this admin setting.
1546 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1547 * @param bool $default - The default for the flag
1549 public function set_enabled_flag_options($enabled, $default) {
1550 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1554 * Set the advanced options flag on this admin setting.
1556 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1557 * @param bool $default - The default for the flag
1559 public function set_advanced_flag_options($enabled, $default) {
1560 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1565 * Set the locked options flag on this admin setting.
1567 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1568 * @param bool $default - The default for the flag
1570 public function set_locked_flag_options($enabled, $default) {
1571 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1575 * Get the currently saved value for a setting flag
1577 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1578 * @return bool
1580 public function get_setting_flag_value(admin_setting_flag $flag) {
1581 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1582 if (!isset($value)) {
1583 $value = $flag->get_default();
1586 return !empty($value);
1590 * Get the list of defaults for the flags on this setting.
1592 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1594 public function get_setting_flag_defaults(& $defaults) {
1595 foreach ($this->flags as $flag) {
1596 if ($flag->is_enabled() && $flag->get_default()) {
1597 $defaults[] = $flag->get_displayname();
1603 * Output the input fields for the advanced and locked flags on this setting.
1605 * @param bool $adv - The current value of the advanced flag.
1606 * @param bool $locked - The current value of the locked flag.
1607 * @return string $output - The html for the flags.
1609 public function output_setting_flags() {
1610 $output = '';
1612 foreach ($this->flags as $flag) {
1613 if ($flag->is_enabled()) {
1614 $output .= $flag->output_setting_flag($this);
1618 if (!empty($output)) {
1619 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1621 return $output;
1625 * Write the values of the flags for this admin setting.
1627 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1628 * @return bool - true if successful.
1630 public function write_setting_flags($data) {
1631 $result = true;
1632 foreach ($this->flags as $flag) {
1633 $result = $result && $flag->write_setting_flag($this, $data);
1635 return $result;
1639 * Set up $this->name and potentially $this->plugin
1641 * Set up $this->name and possibly $this->plugin based on whether $name looks
1642 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1643 * on the names, that is, output a developer debug warning if the name
1644 * contains anything other than [a-zA-Z0-9_]+.
1646 * @param string $name the setting name passed in to the constructor.
1648 private function parse_setting_name($name) {
1649 $bits = explode('/', $name);
1650 if (count($bits) > 2) {
1651 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1653 $this->name = array_pop($bits);
1654 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1655 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1657 if (!empty($bits)) {
1658 $this->plugin = array_pop($bits);
1659 if ($this->plugin === 'moodle') {
1660 $this->plugin = null;
1661 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1662 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1668 * Returns the fullname prefixed by the plugin
1669 * @return string
1671 public function get_full_name() {
1672 return 's_'.$this->plugin.'_'.$this->name;
1676 * Returns the ID string based on plugin and name
1677 * @return string
1679 public function get_id() {
1680 return 'id_s_'.$this->plugin.'_'.$this->name;
1684 * @param bool $affectsmodinfo If true, changes to this setting will
1685 * cause the course cache to be rebuilt
1687 public function set_affects_modinfo($affectsmodinfo) {
1688 $this->affectsmodinfo = $affectsmodinfo;
1692 * Returns the config if possible
1694 * @return mixed returns config if successful else null
1696 public function config_read($name) {
1697 global $CFG;
1698 if (!empty($this->plugin)) {
1699 $value = get_config($this->plugin, $name);
1700 return $value === false ? NULL : $value;
1702 } else {
1703 if (isset($CFG->$name)) {
1704 return $CFG->$name;
1705 } else {
1706 return NULL;
1712 * Used to set a config pair and log change
1714 * @param string $name
1715 * @param mixed $value Gets converted to string if not null
1716 * @return bool Write setting to config table
1718 public function config_write($name, $value) {
1719 global $DB, $USER, $CFG;
1721 if ($this->nosave) {
1722 return true;
1725 // make sure it is a real change
1726 $oldvalue = get_config($this->plugin, $name);
1727 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1728 $value = is_null($value) ? null : (string)$value;
1730 if ($oldvalue === $value) {
1731 return true;
1734 // store change
1735 set_config($name, $value, $this->plugin);
1737 // Some admin settings affect course modinfo
1738 if ($this->affectsmodinfo) {
1739 // Clear course cache for all courses
1740 rebuild_course_cache(0, true);
1743 add_to_config_log($name, $oldvalue, $value, $this->plugin);
1745 return true; // BC only
1749 * Returns current value of this setting
1750 * @return mixed array or string depending on instance, NULL means not set yet
1752 public abstract function get_setting();
1755 * Returns default setting if exists
1756 * @return mixed array or string depending on instance; NULL means no default, user must supply
1758 public function get_defaultsetting() {
1759 $adminroot = admin_get_root(false, false);
1760 if (!empty($adminroot->custom_defaults)) {
1761 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1762 if (isset($adminroot->custom_defaults[$plugin])) {
1763 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1764 return $adminroot->custom_defaults[$plugin][$this->name];
1768 return $this->defaultsetting;
1772 * Store new setting
1774 * @param mixed $data string or array, must not be NULL
1775 * @return string empty string if ok, string error message otherwise
1777 public abstract function write_setting($data);
1780 * Return part of form with setting
1781 * This function should always be overwritten
1783 * @param mixed $data array or string depending on setting
1784 * @param string $query
1785 * @return string
1787 public function output_html($data, $query='') {
1788 // should be overridden
1789 return;
1793 * Function called if setting updated - cleanup, cache reset, etc.
1794 * @param string $functionname Sets the function name
1795 * @return void
1797 public function set_updatedcallback($functionname) {
1798 $this->updatedcallback = $functionname;
1802 * Execute postupdatecallback if necessary.
1803 * @param mixed $original original value before write_setting()
1804 * @return bool true if changed, false if not.
1806 public function post_write_settings($original) {
1807 // Comparison must work for arrays too.
1808 if (serialize($original) === serialize($this->get_setting())) {
1809 return false;
1812 $callbackfunction = $this->updatedcallback;
1813 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
1814 $callbackfunction($this->get_full_name());
1816 return true;
1820 * Is setting related to query text - used when searching
1821 * @param string $query
1822 * @return bool
1824 public function is_related($query) {
1825 if (strpos(strtolower($this->name), $query) !== false) {
1826 return true;
1828 if (strpos(textlib::strtolower($this->visiblename), $query) !== false) {
1829 return true;
1831 if (strpos(textlib::strtolower($this->description), $query) !== false) {
1832 return true;
1834 $current = $this->get_setting();
1835 if (!is_null($current)) {
1836 if (is_string($current)) {
1837 if (strpos(textlib::strtolower($current), $query) !== false) {
1838 return true;
1842 $default = $this->get_defaultsetting();
1843 if (!is_null($default)) {
1844 if (is_string($default)) {
1845 if (strpos(textlib::strtolower($default), $query) !== false) {
1846 return true;
1850 return false;
1855 * An additional option that can be applied to an admin setting.
1856 * The currently supported options are 'ADVANCED' and 'LOCKED'.
1858 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1860 class admin_setting_flag {
1861 /** @var bool Flag to indicate if this option can be toggled for this setting */
1862 private $enabled = false;
1863 /** @var bool Flag to indicate if this option defaults to true or false */
1864 private $default = false;
1865 /** @var string Short string used to create setting name - e.g. 'adv' */
1866 private $shortname = '';
1867 /** @var string String used as the label for this flag */
1868 private $displayname = '';
1869 /** @const Checkbox for this flag is displayed in admin page */
1870 const ENABLED = true;
1871 /** @const Checkbox for this flag is not displayed in admin page */
1872 const DISABLED = false;
1875 * Constructor
1877 * @param bool $enabled Can this option can be toggled.
1878 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
1879 * @param bool $default The default checked state for this setting option.
1880 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
1881 * @param string $displayname The displayname of this flag. Used as a label for the flag.
1883 public function __construct($enabled, $default, $shortname, $displayname) {
1884 $this->shortname = $shortname;
1885 $this->displayname = $displayname;
1886 $this->set_options($enabled, $default);
1890 * Update the values of this setting options class
1892 * @param bool $enabled Can this option can be toggled.
1893 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
1894 * @param bool $default The default checked state for this setting option.
1896 public function set_options($enabled, $default) {
1897 $this->enabled = $enabled;
1898 $this->default = $default;
1902 * Should this option appear in the interface and be toggleable?
1904 * @return bool Is it enabled?
1906 public function is_enabled() {
1907 return $this->enabled;
1911 * Should this option be checked by default?
1913 * @return bool Is it on by default?
1915 public function get_default() {
1916 return $this->default;
1920 * Return the short name for this flag. e.g. 'adv' or 'locked'
1922 * @return string
1924 public function get_shortname() {
1925 return $this->shortname;
1929 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
1931 * @return string
1933 public function get_displayname() {
1934 return $this->displayname;
1938 * Save the submitted data for this flag - or set it to the default if $data is null.
1940 * @param admin_setting $setting - The admin setting for this flag
1941 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1942 * @return bool
1944 public function write_setting_flag(admin_setting $setting, $data) {
1945 $result = true;
1946 if ($this->is_enabled()) {
1947 if (!isset($data)) {
1948 $value = $this->get_default();
1949 } else {
1950 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
1952 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
1955 return $result;
1960 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
1962 * @param admin_setting $setting - The admin setting for this flag
1963 * @return string - The html for the checkbox.
1965 public function output_setting_flag(admin_setting $setting) {
1966 $value = $setting->get_setting_flag_value($this);
1967 $output = ' <input type="checkbox" class="form-checkbox" ' .
1968 ' id="' . $setting->get_id() . '_' . $this->get_shortname() . '" ' .
1969 ' name="' . $setting->get_full_name() . '_' . $this->get_shortname() . '" ' .
1970 ' value="1" ' . ($value ? 'checked="checked"' : '') . ' />' .
1971 ' <label for="' . $setting->get_id() . '_' . $this->get_shortname() . '">' .
1972 $this->get_displayname() .
1973 ' </label> ';
1974 return $output;
1980 * No setting - just heading and text.
1982 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1984 class admin_setting_heading extends admin_setting {
1987 * not a setting, just text
1988 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1989 * @param string $heading heading
1990 * @param string $information text in box
1992 public function __construct($name, $heading, $information) {
1993 $this->nosave = true;
1994 parent::__construct($name, $heading, $information, '');
1998 * Always returns true
1999 * @return bool Always returns true
2001 public function get_setting() {
2002 return true;
2006 * Always returns true
2007 * @return bool Always returns true
2009 public function get_defaultsetting() {
2010 return true;
2014 * Never write settings
2015 * @return string Always returns an empty string
2017 public function write_setting($data) {
2018 // do not write any setting
2019 return '';
2023 * Returns an HTML string
2024 * @return string Returns an HTML string
2026 public function output_html($data, $query='') {
2027 global $OUTPUT;
2028 $return = '';
2029 if ($this->visiblename != '') {
2030 $return .= $OUTPUT->heading($this->visiblename, 3, 'main');
2032 if ($this->description != '') {
2033 $return .= $OUTPUT->box(highlight($query, markdown_to_html($this->description)), 'generalbox formsettingheading');
2035 return $return;
2041 * The most flexibly setting, user is typing text
2043 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2045 class admin_setting_configtext extends admin_setting {
2047 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
2048 public $paramtype;
2049 /** @var int default field size */
2050 public $size;
2053 * Config text constructor
2055 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2056 * @param string $visiblename localised
2057 * @param string $description long localised info
2058 * @param string $defaultsetting
2059 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2060 * @param int $size default field size
2062 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2063 $this->paramtype = $paramtype;
2064 if (!is_null($size)) {
2065 $this->size = $size;
2066 } else {
2067 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
2069 parent::__construct($name, $visiblename, $description, $defaultsetting);
2073 * Return the setting
2075 * @return mixed returns config if successful else null
2077 public function get_setting() {
2078 return $this->config_read($this->name);
2081 public function write_setting($data) {
2082 if ($this->paramtype === PARAM_INT and $data === '') {
2083 // do not complain if '' used instead of 0
2084 $data = 0;
2086 // $data is a string
2087 $validated = $this->validate($data);
2088 if ($validated !== true) {
2089 return $validated;
2091 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2095 * Validate data before storage
2096 * @param string data
2097 * @return mixed true if ok string if error found
2099 public function validate($data) {
2100 // allow paramtype to be a custom regex if it is the form of /pattern/
2101 if (preg_match('#^/.*/$#', $this->paramtype)) {
2102 if (preg_match($this->paramtype, $data)) {
2103 return true;
2104 } else {
2105 return get_string('validateerror', 'admin');
2108 } else if ($this->paramtype === PARAM_RAW) {
2109 return true;
2111 } else {
2112 $cleaned = clean_param($data, $this->paramtype);
2113 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2114 return true;
2115 } else {
2116 return get_string('validateerror', 'admin');
2122 * Return an XHTML string for the setting
2123 * @return string Returns an XHTML string
2125 public function output_html($data, $query='') {
2126 $default = $this->get_defaultsetting();
2128 return format_admin_setting($this, $this->visiblename,
2129 '<div class="form-text defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" /></div>',
2130 $this->description, true, '', $default, $query);
2136 * General text area without html editor.
2138 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2140 class admin_setting_configtextarea extends admin_setting_configtext {
2141 private $rows;
2142 private $cols;
2145 * @param string $name
2146 * @param string $visiblename
2147 * @param string $description
2148 * @param mixed $defaultsetting string or array
2149 * @param mixed $paramtype
2150 * @param string $cols The number of columns to make the editor
2151 * @param string $rows The number of rows to make the editor
2153 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2154 $this->rows = $rows;
2155 $this->cols = $cols;
2156 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2160 * Returns an XHTML string for the editor
2162 * @param string $data
2163 * @param string $query
2164 * @return string XHTML string for the editor
2166 public function output_html($data, $query='') {
2167 $default = $this->get_defaultsetting();
2169 $defaultinfo = $default;
2170 if (!is_null($default) and $default !== '') {
2171 $defaultinfo = "\n".$default;
2174 return format_admin_setting($this, $this->visiblename,
2175 '<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>',
2176 $this->description, true, '', $defaultinfo, $query);
2182 * General text area with html editor.
2184 class admin_setting_confightmleditor extends admin_setting_configtext {
2185 private $rows;
2186 private $cols;
2189 * @param string $name
2190 * @param string $visiblename
2191 * @param string $description
2192 * @param mixed $defaultsetting string or array
2193 * @param mixed $paramtype
2195 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2196 $this->rows = $rows;
2197 $this->cols = $cols;
2198 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2199 editors_head_setup();
2203 * Returns an XHTML string for the editor
2205 * @param string $data
2206 * @param string $query
2207 * @return string XHTML string for the editor
2209 public function output_html($data, $query='') {
2210 $default = $this->get_defaultsetting();
2212 $defaultinfo = $default;
2213 if (!is_null($default) and $default !== '') {
2214 $defaultinfo = "\n".$default;
2217 $editor = editors_get_preferred_editor(FORMAT_HTML);
2218 $editor->use_editor($this->get_id(), array('noclean'=>true));
2220 return format_admin_setting($this, $this->visiblename,
2221 '<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>',
2222 $this->description, true, '', $defaultinfo, $query);
2228 * Password field, allows unmasking of password
2230 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2232 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2234 * Constructor
2235 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2236 * @param string $visiblename localised
2237 * @param string $description long localised info
2238 * @param string $defaultsetting default password
2240 public function __construct($name, $visiblename, $description, $defaultsetting) {
2241 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2245 * Returns XHTML for the field
2246 * Writes Javascript into the HTML below right before the last div
2248 * @todo Make javascript available through newer methods if possible
2249 * @param string $data Value for the field
2250 * @param string $query Passed as final argument for format_admin_setting
2251 * @return string XHTML field
2253 public function output_html($data, $query='') {
2254 $id = $this->get_id();
2255 $unmask = get_string('unmaskpassword', 'form');
2256 $unmaskjs = '<script type="text/javascript">
2257 //<![CDATA[
2258 var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
2260 document.getElementById("'.$id.'").setAttribute("autocomplete", "off");
2262 var unmaskdiv = document.getElementById("'.$id.'unmaskdiv");
2264 var unmaskchb = document.createElement("input");
2265 unmaskchb.setAttribute("type", "checkbox");
2266 unmaskchb.setAttribute("id", "'.$id.'unmask");
2267 unmaskchb.onchange = function() {unmaskPassword("'.$id.'");};
2268 unmaskdiv.appendChild(unmaskchb);
2270 var unmasklbl = document.createElement("label");
2271 unmasklbl.innerHTML = "'.addslashes_js($unmask).'";
2272 if (is_ie) {
2273 unmasklbl.setAttribute("htmlFor", "'.$id.'unmask");
2274 } else {
2275 unmasklbl.setAttribute("for", "'.$id.'unmask");
2277 unmaskdiv.appendChild(unmasklbl);
2279 if (is_ie) {
2280 // ugly hack to work around the famous onchange IE bug
2281 unmaskchb.onclick = function() {this.blur();};
2282 unmaskdiv.onclick = function() {this.blur();};
2284 //]]>
2285 </script>';
2286 return format_admin_setting($this, $this->visiblename,
2287 '<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>',
2288 $this->description, true, '', NULL, $query);
2293 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2294 * Note: Only advanced makes sense right now - locked does not.
2296 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2298 class admin_setting_configempty extends admin_setting_configtext {
2301 * @param string $name
2302 * @param string $visiblename
2303 * @param string $description
2305 public function __construct($name, $visiblename, $description) {
2306 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2310 * Returns an XHTML string for the hidden field
2312 * @param string $data
2313 * @param string $query
2314 * @return string XHTML string for the editor
2316 public function output_html($data, $query='') {
2317 return format_admin_setting($this,
2318 $this->visiblename,
2319 '<div class="form-empty" >' .
2320 '<input type="hidden"' .
2321 ' id="'. $this->get_id() .'"' .
2322 ' name="'. $this->get_full_name() .'"' .
2323 ' value=""/></div>',
2324 $this->description,
2325 true,
2327 get_string('none'),
2328 $query);
2334 * Path to directory
2336 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2338 class admin_setting_configfile extends admin_setting_configtext {
2340 * Constructor
2341 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2342 * @param string $visiblename localised
2343 * @param string $description long localised info
2344 * @param string $defaultdirectory default directory location
2346 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2347 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2351 * Returns XHTML for the field
2353 * Returns XHTML for the field and also checks whether the file
2354 * specified in $data exists using file_exists()
2356 * @param string $data File name and path to use in value attr
2357 * @param string $query
2358 * @return string XHTML field
2360 public function output_html($data, $query='') {
2361 $default = $this->get_defaultsetting();
2363 if ($data) {
2364 if (file_exists($data)) {
2365 $executable = '<span class="pathok">&#x2714;</span>';
2366 } else {
2367 $executable = '<span class="patherror">&#x2718;</span>';
2369 } else {
2370 $executable = '';
2373 return format_admin_setting($this, $this->visiblename,
2374 '<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>',
2375 $this->description, true, '', $default, $query);
2378 * checks if execpatch has been disabled in config.php
2380 public function write_setting($data) {
2381 global $CFG;
2382 if (!empty($CFG->preventexecpath)) {
2383 return '';
2385 return parent::write_setting($data);
2391 * Path to executable file
2393 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2395 class admin_setting_configexecutable extends admin_setting_configfile {
2398 * Returns an XHTML field
2400 * @param string $data This is the value for the field
2401 * @param string $query
2402 * @return string XHTML field
2404 public function output_html($data, $query='') {
2405 global $CFG;
2406 $default = $this->get_defaultsetting();
2408 if ($data) {
2409 if (file_exists($data) and is_executable($data)) {
2410 $executable = '<span class="pathok">&#x2714;</span>';
2411 } else {
2412 $executable = '<span class="patherror">&#x2718;</span>';
2414 } else {
2415 $executable = '';
2417 if (!empty($CFG->preventexecpath)) {
2418 $this->visiblename .= '<div class="form-overridden">'.get_string('execpathnotallowed', 'admin').'</div>';
2421 return format_admin_setting($this, $this->visiblename,
2422 '<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>',
2423 $this->description, true, '', $default, $query);
2429 * Path to directory
2431 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2433 class admin_setting_configdirectory extends admin_setting_configfile {
2436 * Returns an XHTML field
2438 * @param string $data This is the value for the field
2439 * @param string $query
2440 * @return string XHTML
2442 public function output_html($data, $query='') {
2443 $default = $this->get_defaultsetting();
2445 if ($data) {
2446 if (file_exists($data) and is_dir($data)) {
2447 $executable = '<span class="pathok">&#x2714;</span>';
2448 } else {
2449 $executable = '<span class="patherror">&#x2718;</span>';
2451 } else {
2452 $executable = '';
2455 return format_admin_setting($this, $this->visiblename,
2456 '<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>',
2457 $this->description, true, '', $default, $query);
2463 * Checkbox
2465 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2467 class admin_setting_configcheckbox extends admin_setting {
2468 /** @var string Value used when checked */
2469 public $yes;
2470 /** @var string Value used when not checked */
2471 public $no;
2474 * Constructor
2475 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2476 * @param string $visiblename localised
2477 * @param string $description long localised info
2478 * @param string $defaultsetting
2479 * @param string $yes value used when checked
2480 * @param string $no value used when not checked
2482 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2483 parent::__construct($name, $visiblename, $description, $defaultsetting);
2484 $this->yes = (string)$yes;
2485 $this->no = (string)$no;
2489 * Retrieves the current setting using the objects name
2491 * @return string
2493 public function get_setting() {
2494 return $this->config_read($this->name);
2498 * Sets the value for the setting
2500 * Sets the value for the setting to either the yes or no values
2501 * of the object by comparing $data to yes
2503 * @param mixed $data Gets converted to str for comparison against yes value
2504 * @return string empty string or error
2506 public function write_setting($data) {
2507 if ((string)$data === $this->yes) { // convert to strings before comparison
2508 $data = $this->yes;
2509 } else {
2510 $data = $this->no;
2512 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2516 * Returns an XHTML checkbox field
2518 * @param string $data If $data matches yes then checkbox is checked
2519 * @param string $query
2520 * @return string XHTML field
2522 public function output_html($data, $query='') {
2523 $default = $this->get_defaultsetting();
2525 if (!is_null($default)) {
2526 if ((string)$default === $this->yes) {
2527 $defaultinfo = get_string('checkboxyes', 'admin');
2528 } else {
2529 $defaultinfo = get_string('checkboxno', 'admin');
2531 } else {
2532 $defaultinfo = NULL;
2535 if ((string)$data === $this->yes) { // convert to strings before comparison
2536 $checked = 'checked="checked"';
2537 } else {
2538 $checked = '';
2541 return format_admin_setting($this, $this->visiblename,
2542 '<div class="form-checkbox defaultsnext" ><input type="hidden" name="'.$this->get_full_name().'" value="'.s($this->no).'" /> '
2543 .'<input type="checkbox" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($this->yes).'" '.$checked.' /></div>',
2544 $this->description, true, '', $defaultinfo, $query);
2550 * Multiple checkboxes, each represents different value, stored in csv format
2552 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2554 class admin_setting_configmulticheckbox extends admin_setting {
2555 /** @var array Array of choices value=>label */
2556 public $choices;
2559 * Constructor: uses parent::__construct
2561 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2562 * @param string $visiblename localised
2563 * @param string $description long localised info
2564 * @param array $defaultsetting array of selected
2565 * @param array $choices array of $value=>$label for each checkbox
2567 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2568 $this->choices = $choices;
2569 parent::__construct($name, $visiblename, $description, $defaultsetting);
2573 * This public function may be used in ancestors for lazy loading of choices
2575 * @todo Check if this function is still required content commented out only returns true
2576 * @return bool true if loaded, false if error
2578 public function load_choices() {
2580 if (is_array($this->choices)) {
2581 return true;
2583 .... load choices here
2585 return true;
2589 * Is setting related to query text - used when searching
2591 * @param string $query
2592 * @return bool true on related, false on not or failure
2594 public function is_related($query) {
2595 if (!$this->load_choices() or empty($this->choices)) {
2596 return false;
2598 if (parent::is_related($query)) {
2599 return true;
2602 foreach ($this->choices as $desc) {
2603 if (strpos(textlib::strtolower($desc), $query) !== false) {
2604 return true;
2607 return false;
2611 * Returns the current setting if it is set
2613 * @return mixed null if null, else an array
2615 public function get_setting() {
2616 $result = $this->config_read($this->name);
2618 if (is_null($result)) {
2619 return NULL;
2621 if ($result === '') {
2622 return array();
2624 $enabled = explode(',', $result);
2625 $setting = array();
2626 foreach ($enabled as $option) {
2627 $setting[$option] = 1;
2629 return $setting;
2633 * Saves the setting(s) provided in $data
2635 * @param array $data An array of data, if not array returns empty str
2636 * @return mixed empty string on useless data or bool true=success, false=failed
2638 public function write_setting($data) {
2639 if (!is_array($data)) {
2640 return ''; // ignore it
2642 if (!$this->load_choices() or empty($this->choices)) {
2643 return '';
2645 unset($data['xxxxx']);
2646 $result = array();
2647 foreach ($data as $key => $value) {
2648 if ($value and array_key_exists($key, $this->choices)) {
2649 $result[] = $key;
2652 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
2656 * Returns XHTML field(s) as required by choices
2658 * Relies on data being an array should data ever be another valid vartype with
2659 * acceptable value this may cause a warning/error
2660 * if (!is_array($data)) would fix the problem
2662 * @todo Add vartype handling to ensure $data is an array
2664 * @param array $data An array of checked values
2665 * @param string $query
2666 * @return string XHTML field
2668 public function output_html($data, $query='') {
2669 if (!$this->load_choices() or empty($this->choices)) {
2670 return '';
2672 $default = $this->get_defaultsetting();
2673 if (is_null($default)) {
2674 $default = array();
2676 if (is_null($data)) {
2677 $data = array();
2679 $options = array();
2680 $defaults = array();
2681 foreach ($this->choices as $key=>$description) {
2682 if (!empty($data[$key])) {
2683 $checked = 'checked="checked"';
2684 } else {
2685 $checked = '';
2687 if (!empty($default[$key])) {
2688 $defaults[] = $description;
2691 $options[] = '<input type="checkbox" id="'.$this->get_id().'_'.$key.'" name="'.$this->get_full_name().'['.$key.']" value="1" '.$checked.' />'
2692 .'<label for="'.$this->get_id().'_'.$key.'">'.highlightfast($query, $description).'</label>';
2695 if (is_null($default)) {
2696 $defaultinfo = NULL;
2697 } else if (!empty($defaults)) {
2698 $defaultinfo = implode(', ', $defaults);
2699 } else {
2700 $defaultinfo = get_string('none');
2703 $return = '<div class="form-multicheckbox">';
2704 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2705 if ($options) {
2706 $return .= '<ul>';
2707 foreach ($options as $option) {
2708 $return .= '<li>'.$option.'</li>';
2710 $return .= '</ul>';
2712 $return .= '</div>';
2714 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
2721 * Multiple checkboxes 2, value stored as string 00101011
2723 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2725 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
2728 * Returns the setting if set
2730 * @return mixed null if not set, else an array of set settings
2732 public function get_setting() {
2733 $result = $this->config_read($this->name);
2734 if (is_null($result)) {
2735 return NULL;
2737 if (!$this->load_choices()) {
2738 return NULL;
2740 $result = str_pad($result, count($this->choices), '0');
2741 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
2742 $setting = array();
2743 foreach ($this->choices as $key=>$unused) {
2744 $value = array_shift($result);
2745 if ($value) {
2746 $setting[$key] = 1;
2749 return $setting;
2753 * Save setting(s) provided in $data param
2755 * @param array $data An array of settings to save
2756 * @return mixed empty string for bad data or bool true=>success, false=>error
2758 public function write_setting($data) {
2759 if (!is_array($data)) {
2760 return ''; // ignore it
2762 if (!$this->load_choices() or empty($this->choices)) {
2763 return '';
2765 $result = '';
2766 foreach ($this->choices as $key=>$unused) {
2767 if (!empty($data[$key])) {
2768 $result .= '1';
2769 } else {
2770 $result .= '0';
2773 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
2779 * Select one value from list
2781 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2783 class admin_setting_configselect extends admin_setting {
2784 /** @var array Array of choices value=>label */
2785 public $choices;
2788 * Constructor
2789 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2790 * @param string $visiblename localised
2791 * @param string $description long localised info
2792 * @param string|int $defaultsetting
2793 * @param array $choices array of $value=>$label for each selection
2795 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2796 $this->choices = $choices;
2797 parent::__construct($name, $visiblename, $description, $defaultsetting);
2801 * This function may be used in ancestors for lazy loading of choices
2803 * Override this method if loading of choices is expensive, such
2804 * as when it requires multiple db requests.
2806 * @return bool true if loaded, false if error
2808 public function load_choices() {
2810 if (is_array($this->choices)) {
2811 return true;
2813 .... load choices here
2815 return true;
2819 * Check if this is $query is related to a choice
2821 * @param string $query
2822 * @return bool true if related, false if not
2824 public function is_related($query) {
2825 if (parent::is_related($query)) {
2826 return true;
2828 if (!$this->load_choices()) {
2829 return false;
2831 foreach ($this->choices as $key=>$value) {
2832 if (strpos(textlib::strtolower($key), $query) !== false) {
2833 return true;
2835 if (strpos(textlib::strtolower($value), $query) !== false) {
2836 return true;
2839 return false;
2843 * Return the setting
2845 * @return mixed returns config if successful else null
2847 public function get_setting() {
2848 return $this->config_read($this->name);
2852 * Save a setting
2854 * @param string $data
2855 * @return string empty of error string
2857 public function write_setting($data) {
2858 if (!$this->load_choices() or empty($this->choices)) {
2859 return '';
2861 if (!array_key_exists($data, $this->choices)) {
2862 return ''; // ignore it
2865 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2869 * Returns XHTML select field
2871 * Ensure the options are loaded, and generate the XHTML for the select
2872 * element and any warning message. Separating this out from output_html
2873 * makes it easier to subclass this class.
2875 * @param string $data the option to show as selected.
2876 * @param string $current the currently selected option in the database, null if none.
2877 * @param string $default the default selected option.
2878 * @return array the HTML for the select element, and a warning message.
2880 public function output_select_html($data, $current, $default, $extraname = '') {
2881 if (!$this->load_choices() or empty($this->choices)) {
2882 return array('', '');
2885 $warning = '';
2886 if (is_null($current)) {
2887 // first run
2888 } else if (empty($current) and (array_key_exists('', $this->choices) or array_key_exists(0, $this->choices))) {
2889 // no warning
2890 } else if (!array_key_exists($current, $this->choices)) {
2891 $warning = get_string('warningcurrentsetting', 'admin', s($current));
2892 if (!is_null($default) and $data == $current) {
2893 $data = $default; // use default instead of first value when showing the form
2897 $selecthtml = '<select id="'.$this->get_id().'" name="'.$this->get_full_name().$extraname.'">';
2898 foreach ($this->choices as $key => $value) {
2899 // the string cast is needed because key may be integer - 0 is equal to most strings!
2900 $selecthtml .= '<option value="'.$key.'"'.((string)$key==$data ? ' selected="selected"' : '').'>'.$value.'</option>';
2902 $selecthtml .= '</select>';
2903 return array($selecthtml, $warning);
2907 * Returns XHTML select field and wrapping div(s)
2909 * @see output_select_html()
2911 * @param string $data the option to show as selected
2912 * @param string $query
2913 * @return string XHTML field and wrapping div
2915 public function output_html($data, $query='') {
2916 $default = $this->get_defaultsetting();
2917 $current = $this->get_setting();
2919 list($selecthtml, $warning) = $this->output_select_html($data, $current, $default);
2920 if (!$selecthtml) {
2921 return '';
2924 if (!is_null($default) and array_key_exists($default, $this->choices)) {
2925 $defaultinfo = $this->choices[$default];
2926 } else {
2927 $defaultinfo = NULL;
2930 $return = '<div class="form-select defaultsnext">' . $selecthtml . '</div>';
2932 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
2938 * Select multiple items from list
2940 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2942 class admin_setting_configmultiselect extends admin_setting_configselect {
2944 * Constructor
2945 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2946 * @param string $visiblename localised
2947 * @param string $description long localised info
2948 * @param array $defaultsetting array of selected items
2949 * @param array $choices array of $value=>$label for each list item
2951 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2952 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
2956 * Returns the select setting(s)
2958 * @return mixed null or array. Null if no settings else array of setting(s)
2960 public function get_setting() {
2961 $result = $this->config_read($this->name);
2962 if (is_null($result)) {
2963 return NULL;
2965 if ($result === '') {
2966 return array();
2968 return explode(',', $result);
2972 * Saves setting(s) provided through $data
2974 * Potential bug in the works should anyone call with this function
2975 * using a vartype that is not an array
2977 * @param array $data
2979 public function write_setting($data) {
2980 if (!is_array($data)) {
2981 return ''; //ignore it
2983 if (!$this->load_choices() or empty($this->choices)) {
2984 return '';
2987 unset($data['xxxxx']);
2989 $save = array();
2990 foreach ($data as $value) {
2991 if (!array_key_exists($value, $this->choices)) {
2992 continue; // ignore it
2994 $save[] = $value;
2997 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3001 * Is setting related to query text - used when searching
3003 * @param string $query
3004 * @return bool true if related, false if not
3006 public function is_related($query) {
3007 if (!$this->load_choices() or empty($this->choices)) {
3008 return false;
3010 if (parent::is_related($query)) {
3011 return true;
3014 foreach ($this->choices as $desc) {
3015 if (strpos(textlib::strtolower($desc), $query) !== false) {
3016 return true;
3019 return false;
3023 * Returns XHTML multi-select field
3025 * @todo Add vartype handling to ensure $data is an array
3026 * @param array $data Array of values to select by default
3027 * @param string $query
3028 * @return string XHTML multi-select field
3030 public function output_html($data, $query='') {
3031 if (!$this->load_choices() or empty($this->choices)) {
3032 return '';
3034 $choices = $this->choices;
3035 $default = $this->get_defaultsetting();
3036 if (is_null($default)) {
3037 $default = array();
3039 if (is_null($data)) {
3040 $data = array();
3043 $defaults = array();
3044 $size = min(10, count($this->choices));
3045 $return = '<div class="form-select"><input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
3046 $return .= '<select id="'.$this->get_id().'" name="'.$this->get_full_name().'[]" size="'.$size.'" multiple="multiple">';
3047 foreach ($this->choices as $key => $description) {
3048 if (in_array($key, $data)) {
3049 $selected = 'selected="selected"';
3050 } else {
3051 $selected = '';
3053 if (in_array($key, $default)) {
3054 $defaults[] = $description;
3057 $return .= '<option value="'.s($key).'" '.$selected.'>'.$description.'</option>';
3060 if (is_null($default)) {
3061 $defaultinfo = NULL;
3062 } if (!empty($defaults)) {
3063 $defaultinfo = implode(', ', $defaults);
3064 } else {
3065 $defaultinfo = get_string('none');
3068 $return .= '</select></div>';
3069 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
3074 * Time selector
3076 * This is a liiitle bit messy. we're using two selects, but we're returning
3077 * them as an array named after $name (so we only use $name2 internally for the setting)
3079 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3081 class admin_setting_configtime extends admin_setting {
3082 /** @var string Used for setting second select (minutes) */
3083 public $name2;
3086 * Constructor
3087 * @param string $hoursname setting for hours
3088 * @param string $minutesname setting for hours
3089 * @param string $visiblename localised
3090 * @param string $description long localised info
3091 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3093 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3094 $this->name2 = $minutesname;
3095 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3099 * Get the selected time
3101 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3103 public function get_setting() {
3104 $result1 = $this->config_read($this->name);
3105 $result2 = $this->config_read($this->name2);
3106 if (is_null($result1) or is_null($result2)) {
3107 return NULL;
3110 return array('h' => $result1, 'm' => $result2);
3114 * Store the time (hours and minutes)
3116 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3117 * @return bool true if success, false if not
3119 public function write_setting($data) {
3120 if (!is_array($data)) {
3121 return '';
3124 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3125 return ($result ? '' : get_string('errorsetting', 'admin'));
3129 * Returns XHTML time select fields
3131 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3132 * @param string $query
3133 * @return string XHTML time select fields and wrapping div(s)
3135 public function output_html($data, $query='') {
3136 $default = $this->get_defaultsetting();
3138 if (is_array($default)) {
3139 $defaultinfo = $default['h'].':'.$default['m'];
3140 } else {
3141 $defaultinfo = NULL;
3144 $return = '<div class="form-time defaultsnext">'.
3145 '<select id="'.$this->get_id().'h" name="'.$this->get_full_name().'[h]">';
3146 for ($i = 0; $i < 24; $i++) {
3147 $return .= '<option value="'.$i.'"'.($i == $data['h'] ? ' selected="selected"' : '').'>'.$i.'</option>';
3149 $return .= '</select>:<select id="'.$this->get_id().'m" name="'.$this->get_full_name().'[m]">';
3150 for ($i = 0; $i < 60; $i += 5) {
3151 $return .= '<option value="'.$i.'"'.($i == $data['m'] ? ' selected="selected"' : '').'>'.$i.'</option>';
3153 $return .= '</select></div>';
3154 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
3161 * Seconds duration setting.
3163 * @copyright 2012 Petr Skoda (http://skodak.org)
3164 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3166 class admin_setting_configduration extends admin_setting {
3168 /** @var int default duration unit */
3169 protected $defaultunit;
3172 * Constructor
3173 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3174 * or 'myplugin/mysetting' for ones in config_plugins.
3175 * @param string $visiblename localised name
3176 * @param string $description localised long description
3177 * @param mixed $defaultsetting string or array depending on implementation
3178 * @param int $defaultunit - day, week, etc. (in seconds)
3180 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3181 if (is_number($defaultsetting)) {
3182 $defaultsetting = self::parse_seconds($defaultsetting);
3184 $units = self::get_units();
3185 if (isset($units[$defaultunit])) {
3186 $this->defaultunit = $defaultunit;
3187 } else {
3188 $this->defaultunit = 86400;
3190 parent::__construct($name, $visiblename, $description, $defaultsetting);
3194 * Returns selectable units.
3195 * @static
3196 * @return array
3198 protected static function get_units() {
3199 return array(
3200 604800 => get_string('weeks'),
3201 86400 => get_string('days'),
3202 3600 => get_string('hours'),
3203 60 => get_string('minutes'),
3204 1 => get_string('seconds'),
3209 * Converts seconds to some more user friendly string.
3210 * @static
3211 * @param int $seconds
3212 * @return string
3214 protected static function get_duration_text($seconds) {
3215 if (empty($seconds)) {
3216 return get_string('none');
3218 $data = self::parse_seconds($seconds);
3219 switch ($data['u']) {
3220 case (60*60*24*7):
3221 return get_string('numweeks', '', $data['v']);
3222 case (60*60*24):
3223 return get_string('numdays', '', $data['v']);
3224 case (60*60):
3225 return get_string('numhours', '', $data['v']);
3226 case (60):
3227 return get_string('numminutes', '', $data['v']);
3228 default:
3229 return get_string('numseconds', '', $data['v']*$data['u']);
3234 * Finds suitable units for given duration.
3235 * @static
3236 * @param int $seconds
3237 * @return array
3239 protected static function parse_seconds($seconds) {
3240 foreach (self::get_units() as $unit => $unused) {
3241 if ($seconds % $unit === 0) {
3242 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3245 return array('v'=>(int)$seconds, 'u'=>1);
3249 * Get the selected duration as array.
3251 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3253 public function get_setting() {
3254 $seconds = $this->config_read($this->name);
3255 if (is_null($seconds)) {
3256 return null;
3259 return self::parse_seconds($seconds);
3263 * Store the duration as seconds.
3265 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3266 * @return bool true if success, false if not
3268 public function write_setting($data) {
3269 if (!is_array($data)) {
3270 return '';
3273 $seconds = (int)($data['v']*$data['u']);
3274 if ($seconds < 0) {
3275 return get_string('errorsetting', 'admin');
3278 $result = $this->config_write($this->name, $seconds);
3279 return ($result ? '' : get_string('errorsetting', 'admin'));
3283 * Returns duration text+select fields.
3285 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3286 * @param string $query
3287 * @return string duration text+select fields and wrapping div(s)
3289 public function output_html($data, $query='') {
3290 $default = $this->get_defaultsetting();
3292 if (is_number($default)) {
3293 $defaultinfo = self::get_duration_text($default);
3294 } else if (is_array($default)) {
3295 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3296 } else {
3297 $defaultinfo = null;
3300 $units = self::get_units();
3302 $return = '<div class="form-duration defaultsnext">';
3303 $return .= '<input type="text" size="5" id="'.$this->get_id().'v" name="'.$this->get_full_name().'[v]" value="'.s($data['v']).'" />';
3304 $return .= '<select id="'.$this->get_id().'u" name="'.$this->get_full_name().'[u]">';
3305 foreach ($units as $val => $text) {
3306 $selected = '';
3307 if ($data['v'] == 0) {
3308 if ($val == $this->defaultunit) {
3309 $selected = ' selected="selected"';
3311 } else if ($val == $data['u']) {
3312 $selected = ' selected="selected"';
3314 $return .= '<option value="'.$val.'"'.$selected.'>'.$text.'</option>';
3316 $return .= '</select></div>';
3317 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
3323 * Used to validate a textarea used for ip addresses
3325 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3327 class admin_setting_configiplist extends admin_setting_configtextarea {
3330 * Validate the contents of the textarea as IP addresses
3332 * Used to validate a new line separated list of IP addresses collected from
3333 * a textarea control
3335 * @param string $data A list of IP Addresses separated by new lines
3336 * @return mixed bool true for success or string:error on failure
3338 public function validate($data) {
3339 if(!empty($data)) {
3340 $ips = explode("\n", $data);
3341 } else {
3342 return true;
3344 $result = true;
3345 foreach($ips as $ip) {
3346 $ip = trim($ip);
3347 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3348 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3349 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3350 $result = true;
3351 } else {
3352 $result = false;
3353 break;
3356 if($result) {
3357 return true;
3358 } else {
3359 return get_string('validateerror', 'admin');
3366 * An admin setting for selecting one or more users who have a capability
3367 * in the system context
3369 * An admin setting for selecting one or more users, who have a particular capability
3370 * in the system context. Warning, make sure the list will never be too long. There is
3371 * no paging or searching of this list.
3373 * To correctly get a list of users from this config setting, you need to call the
3374 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
3376 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3378 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
3379 /** @var string The capabilities name */
3380 protected $capability;
3381 /** @var int include admin users too */
3382 protected $includeadmins;
3385 * Constructor.
3387 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3388 * @param string $visiblename localised name
3389 * @param string $description localised long description
3390 * @param array $defaultsetting array of usernames
3391 * @param string $capability string capability name.
3392 * @param bool $includeadmins include administrators
3394 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
3395 $this->capability = $capability;
3396 $this->includeadmins = $includeadmins;
3397 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
3401 * Load all of the uses who have the capability into choice array
3403 * @return bool Always returns true
3405 function load_choices() {
3406 if (is_array($this->choices)) {
3407 return true;
3409 list($sort, $sortparams) = users_order_by_sql('u');
3410 if (!empty($sortparams)) {
3411 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
3412 'This is unexpected, and a problem because there is no way to pass these ' .
3413 'parameters to get_users_by_capability. See MDL-34657.');
3415 $users = get_users_by_capability(context_system::instance(),
3416 $this->capability, 'u.id,u.username,u.firstname,u.lastname', $sort);
3417 $this->choices = array(
3418 '$@NONE@$' => get_string('nobody'),
3419 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
3421 if ($this->includeadmins) {
3422 $admins = get_admins();
3423 foreach ($admins as $user) {
3424 $this->choices[$user->id] = fullname($user);
3427 if (is_array($users)) {
3428 foreach ($users as $user) {
3429 $this->choices[$user->id] = fullname($user);
3432 return true;
3436 * Returns the default setting for class
3438 * @return mixed Array, or string. Empty string if no default
3440 public function get_defaultsetting() {
3441 $this->load_choices();
3442 $defaultsetting = parent::get_defaultsetting();
3443 if (empty($defaultsetting)) {
3444 return array('$@NONE@$');
3445 } else if (array_key_exists($defaultsetting, $this->choices)) {
3446 return $defaultsetting;
3447 } else {
3448 return '';
3453 * Returns the current setting
3455 * @return mixed array or string
3457 public function get_setting() {
3458 $result = parent::get_setting();
3459 if ($result === null) {
3460 // this is necessary for settings upgrade
3461 return null;
3463 if (empty($result)) {
3464 $result = array('$@NONE@$');
3466 return $result;
3470 * Save the chosen setting provided as $data
3472 * @param array $data
3473 * @return mixed string or array
3475 public function write_setting($data) {
3476 // If all is selected, remove any explicit options.
3477 if (in_array('$@ALL@$', $data)) {
3478 $data = array('$@ALL@$');
3480 // None never needs to be written to the DB.
3481 if (in_array('$@NONE@$', $data)) {
3482 unset($data[array_search('$@NONE@$', $data)]);
3484 return parent::write_setting($data);
3490 * Special checkbox for calendar - resets SESSION vars.
3492 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3494 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
3496 * Calls the parent::__construct with default values
3498 * name => calendar_adminseesall
3499 * visiblename => get_string('adminseesall', 'admin')
3500 * description => get_string('helpadminseesall', 'admin')
3501 * defaultsetting => 0
3503 public function __construct() {
3504 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
3505 get_string('helpadminseesall', 'admin'), '0');
3509 * Stores the setting passed in $data
3511 * @param mixed gets converted to string for comparison
3512 * @return string empty string or error message
3514 public function write_setting($data) {
3515 global $SESSION;
3516 return parent::write_setting($data);
3521 * Special select for settings that are altered in setup.php and can not be altered on the fly
3523 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3525 class admin_setting_special_selectsetup extends admin_setting_configselect {
3527 * Reads the setting directly from the database
3529 * @return mixed
3531 public function get_setting() {
3532 // read directly from db!
3533 return get_config(NULL, $this->name);
3537 * Save the setting passed in $data
3539 * @param string $data The setting to save
3540 * @return string empty or error message
3542 public function write_setting($data) {
3543 global $CFG;
3544 // do not change active CFG setting!
3545 $current = $CFG->{$this->name};
3546 $result = parent::write_setting($data);
3547 $CFG->{$this->name} = $current;
3548 return $result;
3554 * Special select for frontpage - stores data in course table
3556 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3558 class admin_setting_sitesetselect extends admin_setting_configselect {
3560 * Returns the site name for the selected site
3562 * @see get_site()
3563 * @return string The site name of the selected site
3565 public function get_setting() {
3566 $site = course_get_format(get_site())->get_course();
3567 return $site->{$this->name};
3571 * Updates the database and save the setting
3573 * @param string data
3574 * @return string empty or error message
3576 public function write_setting($data) {
3577 global $DB, $SITE, $COURSE;
3578 if (!in_array($data, array_keys($this->choices))) {
3579 return get_string('errorsetting', 'admin');
3581 $record = new stdClass();
3582 $record->id = SITEID;
3583 $temp = $this->name;
3584 $record->$temp = $data;
3585 $record->timemodified = time();
3587 course_get_format($SITE)->update_course_format_options($record);
3588 $DB->update_record('course', $record);
3590 // Reset caches.
3591 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3592 if ($SITE->id == $COURSE->id) {
3593 $COURSE = $SITE;
3595 format_base::reset_course_cache($SITE->id);
3597 return '';
3604 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
3605 * block to hidden.
3607 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3609 class admin_setting_bloglevel extends admin_setting_configselect {
3611 * Updates the database and save the setting
3613 * @param string data
3614 * @return string empty or error message
3616 public function write_setting($data) {
3617 global $DB, $CFG;
3618 if ($data == 0) {
3619 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
3620 foreach ($blogblocks as $block) {
3621 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
3623 } else {
3624 // reenable all blocks only when switching from disabled blogs
3625 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
3626 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
3627 foreach ($blogblocks as $block) {
3628 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
3632 return parent::write_setting($data);
3638 * Special select - lists on the frontpage - hacky
3640 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3642 class admin_setting_courselist_frontpage extends admin_setting {
3643 /** @var array Array of choices value=>label */
3644 public $choices;
3647 * Construct override, requires one param
3649 * @param bool $loggedin Is the user logged in
3651 public function __construct($loggedin) {
3652 global $CFG;
3653 require_once($CFG->dirroot.'/course/lib.php');
3654 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
3655 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
3656 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
3657 $defaults = array(FRONTPAGEALLCOURSELIST);
3658 parent::__construct($name, $visiblename, $description, $defaults);
3662 * Loads the choices available
3664 * @return bool always returns true
3666 public function load_choices() {
3667 if (is_array($this->choices)) {
3668 return true;
3670 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
3671 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
3672 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
3673 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
3674 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
3675 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
3676 'none' => get_string('none'));
3677 if ($this->name === 'frontpage') {
3678 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
3680 return true;
3684 * Returns the selected settings
3686 * @param mixed array or setting or null
3688 public function get_setting() {
3689 $result = $this->config_read($this->name);
3690 if (is_null($result)) {
3691 return NULL;
3693 if ($result === '') {
3694 return array();
3696 return explode(',', $result);
3700 * Save the selected options
3702 * @param array $data
3703 * @return mixed empty string (data is not an array) or bool true=success false=failure
3705 public function write_setting($data) {
3706 if (!is_array($data)) {
3707 return '';
3709 $this->load_choices();
3710 $save = array();
3711 foreach($data as $datum) {
3712 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
3713 continue;
3715 $save[$datum] = $datum; // no duplicates
3717 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3721 * Return XHTML select field and wrapping div
3723 * @todo Add vartype handling to make sure $data is an array
3724 * @param array $data Array of elements to select by default
3725 * @return string XHTML select field and wrapping div
3727 public function output_html($data, $query='') {
3728 $this->load_choices();
3729 $currentsetting = array();
3730 foreach ($data as $key) {
3731 if ($key != 'none' and array_key_exists($key, $this->choices)) {
3732 $currentsetting[] = $key; // already selected first
3736 $return = '<div class="form-group">';
3737 for ($i = 0; $i < count($this->choices) - 1; $i++) {
3738 if (!array_key_exists($i, $currentsetting)) {
3739 $currentsetting[$i] = 'none'; //none
3741 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
3742 foreach ($this->choices as $key => $value) {
3743 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
3745 $return .= '</select>';
3746 if ($i !== count($this->choices) - 2) {
3747 $return .= '<br />';
3750 $return .= '</div>';
3752 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3758 * Special checkbox for frontpage - stores data in course table
3760 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3762 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
3764 * Returns the current sites name
3766 * @return string
3768 public function get_setting() {
3769 $site = course_get_format(get_site())->get_course();
3770 return $site->{$this->name};
3774 * Save the selected setting
3776 * @param string $data The selected site
3777 * @return string empty string or error message
3779 public function write_setting($data) {
3780 global $DB, $SITE, $COURSE;
3781 $record = new stdClass();
3782 $record->id = $SITE->id;
3783 $record->{$this->name} = ($data == '1' ? 1 : 0);
3784 $record->timemodified = time();
3786 course_get_format($SITE)->update_course_format_options($record);
3787 $DB->update_record('course', $record);
3789 // Reset caches.
3790 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3791 if ($SITE->id == $COURSE->id) {
3792 $COURSE = $SITE;
3794 format_base::reset_course_cache($SITE->id);
3796 return '';
3801 * Special text for frontpage - stores data in course table.
3802 * Empty string means not set here. Manual setting is required.
3804 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3806 class admin_setting_sitesettext extends admin_setting_configtext {
3808 * Return the current setting
3810 * @return mixed string or null
3812 public function get_setting() {
3813 $site = course_get_format(get_site())->get_course();
3814 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
3818 * Validate the selected data
3820 * @param string $data The selected value to validate
3821 * @return mixed true or message string
3823 public function validate($data) {
3824 $cleaned = clean_param($data, PARAM_TEXT);
3825 if ($cleaned === '') {
3826 return get_string('required');
3828 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
3829 return true;
3830 } else {
3831 return get_string('validateerror', 'admin');
3836 * Save the selected setting
3838 * @param string $data The selected value
3839 * @return string empty or error message
3841 public function write_setting($data) {
3842 global $DB, $SITE, $COURSE;
3843 $data = trim($data);
3844 $validated = $this->validate($data);
3845 if ($validated !== true) {
3846 return $validated;
3849 $record = new stdClass();
3850 $record->id = $SITE->id;
3851 $record->{$this->name} = $data;
3852 $record->timemodified = time();
3854 course_get_format($SITE)->update_course_format_options($record);
3855 $DB->update_record('course', $record);
3857 // Reset caches.
3858 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3859 if ($SITE->id == $COURSE->id) {
3860 $COURSE = $SITE;
3862 format_base::reset_course_cache($SITE->id);
3864 return '';
3870 * Special text editor for site description.
3872 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3874 class admin_setting_special_frontpagedesc extends admin_setting {
3876 * Calls parent::__construct with specific arguments
3878 public function __construct() {
3879 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
3880 editors_head_setup();
3884 * Return the current setting
3885 * @return string The current setting
3887 public function get_setting() {
3888 $site = course_get_format(get_site())->get_course();
3889 return $site->{$this->name};
3893 * Save the new setting
3895 * @param string $data The new value to save
3896 * @return string empty or error message
3898 public function write_setting($data) {
3899 global $DB, $SITE, $COURSE;
3900 $record = new stdClass();
3901 $record->id = $SITE->id;
3902 $record->{$this->name} = $data;
3903 $record->timemodified = time();
3905 course_get_format($SITE)->update_course_format_options($record);
3906 $DB->update_record('course', $record);
3908 // Reset caches.
3909 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
3910 if ($SITE->id == $COURSE->id) {
3911 $COURSE = $SITE;
3913 format_base::reset_course_cache($SITE->id);
3915 return '';
3919 * Returns XHTML for the field plus wrapping div
3921 * @param string $data The current value
3922 * @param string $query
3923 * @return string The XHTML output
3925 public function output_html($data, $query='') {
3926 global $CFG;
3928 $CFG->adminusehtmleditor = can_use_html_editor();
3929 $return = '<div class="form-htmlarea">'.print_textarea($CFG->adminusehtmleditor, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
3931 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3937 * Administration interface for emoticon_manager settings.
3939 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3941 class admin_setting_emoticons extends admin_setting {
3944 * Calls parent::__construct with specific args
3946 public function __construct() {
3947 global $CFG;
3949 $manager = get_emoticon_manager();
3950 $defaults = $this->prepare_form_data($manager->default_emoticons());
3951 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
3955 * Return the current setting(s)
3957 * @return array Current settings array
3959 public function get_setting() {
3960 global $CFG;
3962 $manager = get_emoticon_manager();
3964 $config = $this->config_read($this->name);
3965 if (is_null($config)) {
3966 return null;
3969 $config = $manager->decode_stored_config($config);
3970 if (is_null($config)) {
3971 return null;
3974 return $this->prepare_form_data($config);
3978 * Save selected settings
3980 * @param array $data Array of settings to save
3981 * @return bool
3983 public function write_setting($data) {
3985 $manager = get_emoticon_manager();
3986 $emoticons = $this->process_form_data($data);
3988 if ($emoticons === false) {
3989 return false;
3992 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
3993 return ''; // success
3994 } else {
3995 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
4000 * Return XHTML field(s) for options
4002 * @param array $data Array of options to set in HTML
4003 * @return string XHTML string for the fields and wrapping div(s)
4005 public function output_html($data, $query='') {
4006 global $OUTPUT;
4008 $out = html_writer::start_tag('table', array('id' => 'emoticonsetting', 'class' => 'admintable generaltable'));
4009 $out .= html_writer::start_tag('thead');
4010 $out .= html_writer::start_tag('tr');
4011 $out .= html_writer::tag('th', get_string('emoticontext', 'admin'));
4012 $out .= html_writer::tag('th', get_string('emoticonimagename', 'admin'));
4013 $out .= html_writer::tag('th', get_string('emoticoncomponent', 'admin'));
4014 $out .= html_writer::tag('th', get_string('emoticonalt', 'admin'), array('colspan' => 2));
4015 $out .= html_writer::tag('th', '');
4016 $out .= html_writer::end_tag('tr');
4017 $out .= html_writer::end_tag('thead');
4018 $out .= html_writer::start_tag('tbody');
4019 $i = 0;
4020 foreach($data as $field => $value) {
4021 switch ($i) {
4022 case 0:
4023 $out .= html_writer::start_tag('tr');
4024 $current_text = $value;
4025 $current_filename = '';
4026 $current_imagecomponent = '';
4027 $current_altidentifier = '';
4028 $current_altcomponent = '';
4029 case 1:
4030 $current_filename = $value;
4031 case 2:
4032 $current_imagecomponent = $value;
4033 case 3:
4034 $current_altidentifier = $value;
4035 case 4:
4036 $current_altcomponent = $value;
4039 $out .= html_writer::tag('td',
4040 html_writer::empty_tag('input',
4041 array(
4042 'type' => 'text',
4043 'class' => 'form-text',
4044 'name' => $this->get_full_name().'['.$field.']',
4045 'value' => $value,
4047 ), array('class' => 'c'.$i)
4050 if ($i == 4) {
4051 if (get_string_manager()->string_exists($current_altidentifier, $current_altcomponent)) {
4052 $alt = get_string($current_altidentifier, $current_altcomponent);
4053 } else {
4054 $alt = $current_text;
4056 if ($current_filename) {
4057 $out .= html_writer::tag('td', $OUTPUT->render(new pix_emoticon($current_filename, $alt, $current_imagecomponent)));
4058 } else {
4059 $out .= html_writer::tag('td', '');
4061 $out .= html_writer::end_tag('tr');
4062 $i = 0;
4063 } else {
4064 $i++;
4068 $out .= html_writer::end_tag('tbody');
4069 $out .= html_writer::end_tag('table');
4070 $out = html_writer::tag('div', $out, array('class' => 'form-group'));
4071 $out .= html_writer::tag('div', html_writer::link(new moodle_url('/admin/resetemoticons.php'), get_string('emoticonsreset', 'admin')));
4073 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', NULL, $query);
4077 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
4079 * @see self::process_form_data()
4080 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4081 * @return array of form fields and their values
4083 protected function prepare_form_data(array $emoticons) {
4085 $form = array();
4086 $i = 0;
4087 foreach ($emoticons as $emoticon) {
4088 $form['text'.$i] = $emoticon->text;
4089 $form['imagename'.$i] = $emoticon->imagename;
4090 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
4091 $form['altidentifier'.$i] = $emoticon->altidentifier;
4092 $form['altcomponent'.$i] = $emoticon->altcomponent;
4093 $i++;
4095 // add one more blank field set for new object
4096 $form['text'.$i] = '';
4097 $form['imagename'.$i] = '';
4098 $form['imagecomponent'.$i] = '';
4099 $form['altidentifier'.$i] = '';
4100 $form['altcomponent'.$i] = '';
4102 return $form;
4106 * Converts the data from admin settings form into an array of emoticon objects
4108 * @see self::prepare_form_data()
4109 * @param array $data array of admin form fields and values
4110 * @return false|array of emoticon objects
4112 protected function process_form_data(array $form) {
4114 $count = count($form); // number of form field values
4116 if ($count % 5) {
4117 // we must get five fields per emoticon object
4118 return false;
4121 $emoticons = array();
4122 for ($i = 0; $i < $count / 5; $i++) {
4123 $emoticon = new stdClass();
4124 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
4125 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
4126 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
4127 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
4128 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
4130 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
4131 // prevent from breaking http://url.addresses by accident
4132 $emoticon->text = '';
4135 if (strlen($emoticon->text) < 2) {
4136 // do not allow single character emoticons
4137 $emoticon->text = '';
4140 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
4141 // emoticon text must contain some non-alphanumeric character to prevent
4142 // breaking HTML tags
4143 $emoticon->text = '';
4146 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
4147 $emoticons[] = $emoticon;
4150 return $emoticons;
4156 * Special setting for limiting of the list of available languages.
4158 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4160 class admin_setting_langlist extends admin_setting_configtext {
4162 * Calls parent::__construct with specific arguments
4164 public function __construct() {
4165 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4169 * Save the new setting
4171 * @param string $data The new setting
4172 * @return bool
4174 public function write_setting($data) {
4175 $return = parent::write_setting($data);
4176 get_string_manager()->reset_caches();
4177 return $return;
4183 * Selection of one of the recognised countries using the list
4184 * returned by {@link get_list_of_countries()}.
4186 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4188 class admin_settings_country_select extends admin_setting_configselect {
4189 protected $includeall;
4190 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
4191 $this->includeall = $includeall;
4192 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4196 * Lazy-load the available choices for the select box
4198 public function load_choices() {
4199 global $CFG;
4200 if (is_array($this->choices)) {
4201 return true;
4203 $this->choices = array_merge(
4204 array('0' => get_string('choosedots')),
4205 get_string_manager()->get_list_of_countries($this->includeall));
4206 return true;
4212 * admin_setting_configselect for the default number of sections in a course,
4213 * simply so we can lazy-load the choices.
4215 * @copyright 2011 The Open University
4216 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4218 class admin_settings_num_course_sections extends admin_setting_configselect {
4219 public function __construct($name, $visiblename, $description, $defaultsetting) {
4220 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
4223 /** Lazy-load the available choices for the select box */
4224 public function load_choices() {
4225 $max = get_config('moodlecourse', 'maxsections');
4226 if (!isset($max) || !is_numeric($max)) {
4227 $max = 52;
4229 for ($i = 0; $i <= $max; $i++) {
4230 $this->choices[$i] = "$i";
4232 return true;
4238 * Course category selection
4240 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4242 class admin_settings_coursecat_select extends admin_setting_configselect {
4244 * Calls parent::__construct with specific arguments
4246 public function __construct($name, $visiblename, $description, $defaultsetting) {
4247 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4251 * Load the available choices for the select box
4253 * @return bool
4255 public function load_choices() {
4256 global $CFG;
4257 require_once($CFG->dirroot.'/course/lib.php');
4258 if (is_array($this->choices)) {
4259 return true;
4261 $this->choices = make_categories_options();
4262 return true;
4268 * Special control for selecting days to backup
4270 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4272 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
4274 * Calls parent::__construct with specific arguments
4276 public function __construct() {
4277 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
4278 $this->plugin = 'backup';
4282 * Load the available choices for the select box
4284 * @return bool Always returns true
4286 public function load_choices() {
4287 if (is_array($this->choices)) {
4288 return true;
4290 $this->choices = array();
4291 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
4292 foreach ($days as $day) {
4293 $this->choices[$day] = get_string($day, 'calendar');
4295 return true;
4301 * Special debug setting
4303 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4305 class admin_setting_special_debug extends admin_setting_configselect {
4307 * Calls parent::__construct with specific arguments
4309 public function __construct() {
4310 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
4314 * Load the available choices for the select box
4316 * @return bool
4318 public function load_choices() {
4319 if (is_array($this->choices)) {
4320 return true;
4322 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
4323 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
4324 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
4325 DEBUG_ALL => get_string('debugall', 'admin'),
4326 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
4327 return true;
4333 * Special admin control
4335 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4337 class admin_setting_special_calendar_weekend extends admin_setting {
4339 * Calls parent::__construct with specific arguments
4341 public function __construct() {
4342 $name = 'calendar_weekend';
4343 $visiblename = get_string('calendar_weekend', 'admin');
4344 $description = get_string('helpweekenddays', 'admin');
4345 $default = array ('0', '6'); // Saturdays and Sundays
4346 parent::__construct($name, $visiblename, $description, $default);
4350 * Gets the current settings as an array
4352 * @return mixed Null if none, else array of settings
4354 public function get_setting() {
4355 $result = $this->config_read($this->name);
4356 if (is_null($result)) {
4357 return NULL;
4359 if ($result === '') {
4360 return array();
4362 $settings = array();
4363 for ($i=0; $i<7; $i++) {
4364 if ($result & (1 << $i)) {
4365 $settings[] = $i;
4368 return $settings;
4372 * Save the new settings
4374 * @param array $data Array of new settings
4375 * @return bool
4377 public function write_setting($data) {
4378 if (!is_array($data)) {
4379 return '';
4381 unset($data['xxxxx']);
4382 $result = 0;
4383 foreach($data as $index) {
4384 $result |= 1 << $index;
4386 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
4390 * Return XHTML to display the control
4392 * @param array $data array of selected days
4393 * @param string $query
4394 * @return string XHTML for display (field + wrapping div(s)
4396 public function output_html($data, $query='') {
4397 // The order matters very much because of the implied numeric keys
4398 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
4399 $return = '<table><thead><tr>';
4400 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
4401 foreach($days as $index => $day) {
4402 $return .= '<td><label for="'.$this->get_id().$index.'">'.get_string($day, 'calendar').'</label></td>';
4404 $return .= '</tr></thead><tbody><tr>';
4405 foreach($days as $index => $day) {
4406 $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>';
4408 $return .= '</tr></tbody></table>';
4410 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
4417 * Admin setting that allows a user to pick a behaviour.
4419 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4421 class admin_setting_question_behaviour extends admin_setting_configselect {
4423 * @param string $name name of config variable
4424 * @param string $visiblename display name
4425 * @param string $description description
4426 * @param string $default default.
4428 public function __construct($name, $visiblename, $description, $default) {
4429 parent::__construct($name, $visiblename, $description, $default, NULL);
4433 * Load list of behaviours as choices
4434 * @return bool true => success, false => error.
4436 public function load_choices() {
4437 global $CFG;
4438 require_once($CFG->dirroot . '/question/engine/lib.php');
4439 $this->choices = question_engine::get_behaviour_options('');
4440 return true;
4446 * Admin setting that allows a user to pick appropriate roles for something.
4448 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4450 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
4451 /** @var array Array of capabilities which identify roles */
4452 private $types;
4455 * @param string $name Name of config variable
4456 * @param string $visiblename Display name
4457 * @param string $description Description
4458 * @param array $types Array of archetypes which identify
4459 * roles that will be enabled by default.
4461 public function __construct($name, $visiblename, $description, $types) {
4462 parent::__construct($name, $visiblename, $description, NULL, NULL);
4463 $this->types = $types;
4467 * Load roles as choices
4469 * @return bool true=>success, false=>error
4471 public function load_choices() {
4472 global $CFG, $DB;
4473 if (during_initial_install()) {
4474 return false;
4476 if (is_array($this->choices)) {
4477 return true;
4479 if ($roles = get_all_roles()) {
4480 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
4481 return true;
4482 } else {
4483 return false;
4488 * Return the default setting for this control
4490 * @return array Array of default settings
4492 public function get_defaultsetting() {
4493 global $CFG;
4495 if (during_initial_install()) {
4496 return null;
4498 $result = array();
4499 foreach($this->types as $archetype) {
4500 if ($caproles = get_archetype_roles($archetype)) {
4501 foreach ($caproles as $caprole) {
4502 $result[$caprole->id] = 1;
4506 return $result;
4512 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
4514 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4516 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
4518 * Constructor
4519 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4520 * @param string $visiblename localised
4521 * @param string $description long localised info
4522 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
4523 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
4524 * @param int $size default field size
4526 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
4527 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
4528 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4534 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
4536 * @copyright 2009 Petr Skoda (http://skodak.org)
4537 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4539 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
4542 * Constructor
4543 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4544 * @param string $visiblename localised
4545 * @param string $description long localised info
4546 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
4547 * @param string $yes value used when checked
4548 * @param string $no value used when not checked
4550 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4551 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
4552 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4559 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
4561 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
4563 * @copyright 2010 Sam Hemelryk
4564 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4566 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
4568 * Constructor
4569 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4570 * @param string $visiblename localised
4571 * @param string $description long localised info
4572 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
4573 * @param string $yes value used when checked
4574 * @param string $no value used when not checked
4576 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4577 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
4578 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
4585 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
4587 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4589 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
4591 * Calls parent::__construct with specific arguments
4593 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4594 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
4595 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4602 * Graded roles in gradebook
4604 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4606 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
4608 * Calls parent::__construct with specific arguments
4610 public function __construct() {
4611 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
4612 get_string('configgradebookroles', 'admin'),
4613 array('student'));
4620 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4622 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
4624 * Saves the new settings passed in $data
4626 * @param string $data
4627 * @return mixed string or Array
4629 public function write_setting($data) {
4630 global $CFG, $DB;
4632 $oldvalue = $this->config_read($this->name);
4633 $return = parent::write_setting($data);
4634 $newvalue = $this->config_read($this->name);
4636 if ($oldvalue !== $newvalue) {
4637 // force full regrading
4638 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
4641 return $return;
4647 * Which roles to show on course description page
4649 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4651 class admin_setting_special_coursecontact extends admin_setting_pickroles {
4653 * Calls parent::__construct with specific arguments
4655 public function __construct() {
4656 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
4657 get_string('coursecontact_desc', 'admin'),
4658 array('editingteacher'));
4665 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4667 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
4669 * Calls parent::__construct with specific arguments
4671 function admin_setting_special_gradelimiting() {
4672 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
4673 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
4677 * Force site regrading
4679 function regrade_all() {
4680 global $CFG;
4681 require_once("$CFG->libdir/gradelib.php");
4682 grade_force_site_regrading();
4686 * Saves the new settings
4688 * @param mixed $data
4689 * @return string empty string or error message
4691 function write_setting($data) {
4692 $previous = $this->get_setting();
4694 if ($previous === null) {
4695 if ($data) {
4696 $this->regrade_all();
4698 } else {
4699 if ($data != $previous) {
4700 $this->regrade_all();
4703 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4710 * Primary grade export plugin - has state tracking.
4712 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4714 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
4716 * Calls parent::__construct with specific arguments
4718 public function __construct() {
4719 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
4720 get_string('configgradeexport', 'admin'), array(), NULL);
4724 * Load the available choices for the multicheckbox
4726 * @return bool always returns true
4728 public function load_choices() {
4729 if (is_array($this->choices)) {
4730 return true;
4732 $this->choices = array();
4734 if ($plugins = get_plugin_list('gradeexport')) {
4735 foreach($plugins as $plugin => $unused) {
4736 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
4739 return true;
4745 * Grade category settings
4747 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4749 class admin_setting_gradecat_combo extends admin_setting {
4750 /** @var array Array of choices */
4751 public $choices;
4754 * Sets choices and calls parent::__construct with passed arguments
4755 * @param string $name
4756 * @param string $visiblename
4757 * @param string $description
4758 * @param mixed $defaultsetting string or array depending on implementation
4759 * @param array $choices An array of choices for the control
4761 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4762 $this->choices = $choices;
4763 parent::__construct($name, $visiblename, $description, $defaultsetting);
4767 * Return the current setting(s) array
4769 * @return array Array of value=>xx, forced=>xx, adv=>xx
4771 public function get_setting() {
4772 global $CFG;
4774 $value = $this->config_read($this->name);
4775 $flag = $this->config_read($this->name.'_flag');
4777 if (is_null($value) or is_null($flag)) {
4778 return NULL;
4781 $flag = (int)$flag;
4782 $forced = (boolean)(1 & $flag); // first bit
4783 $adv = (boolean)(2 & $flag); // second bit
4785 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
4789 * Save the new settings passed in $data
4791 * @todo Add vartype handling to ensure $data is array
4792 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4793 * @return string empty or error message
4795 public function write_setting($data) {
4796 global $CFG;
4798 $value = $data['value'];
4799 $forced = empty($data['forced']) ? 0 : 1;
4800 $adv = empty($data['adv']) ? 0 : 2;
4801 $flag = ($forced | $adv); //bitwise or
4803 if (!in_array($value, array_keys($this->choices))) {
4804 return 'Error setting ';
4807 $oldvalue = $this->config_read($this->name);
4808 $oldflag = (int)$this->config_read($this->name.'_flag');
4809 $oldforced = (1 & $oldflag); // first bit
4811 $result1 = $this->config_write($this->name, $value);
4812 $result2 = $this->config_write($this->name.'_flag', $flag);
4814 // force regrade if needed
4815 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
4816 require_once($CFG->libdir.'/gradelib.php');
4817 grade_category::updated_forced_settings();
4820 if ($result1 and $result2) {
4821 return '';
4822 } else {
4823 return get_string('errorsetting', 'admin');
4828 * Return XHTML to display the field and wrapping div
4830 * @todo Add vartype handling to ensure $data is array
4831 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4832 * @param string $query
4833 * @return string XHTML to display control
4835 public function output_html($data, $query='') {
4836 $value = $data['value'];
4837 $forced = !empty($data['forced']);
4838 $adv = !empty($data['adv']);
4840 $default = $this->get_defaultsetting();
4841 if (!is_null($default)) {
4842 $defaultinfo = array();
4843 if (isset($this->choices[$default['value']])) {
4844 $defaultinfo[] = $this->choices[$default['value']];
4846 if (!empty($default['forced'])) {
4847 $defaultinfo[] = get_string('force');
4849 if (!empty($default['adv'])) {
4850 $defaultinfo[] = get_string('advanced');
4852 $defaultinfo = implode(', ', $defaultinfo);
4854 } else {
4855 $defaultinfo = NULL;
4859 $return = '<div class="form-group">';
4860 $return .= '<select class="form-select" id="'.$this->get_id().'" name="'.$this->get_full_name().'[value]">';
4861 foreach ($this->choices as $key => $val) {
4862 // the string cast is needed because key may be integer - 0 is equal to most strings!
4863 $return .= '<option value="'.$key.'"'.((string)$key==$value ? ' selected="selected"' : '').'>'.$val.'</option>';
4865 $return .= '</select>';
4866 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'force" name="'.$this->get_full_name().'[forced]" value="1" '.($forced ? 'checked="checked"' : '').' />'
4867 .'<label for="'.$this->get_id().'force">'.get_string('force').'</label>';
4868 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'adv" name="'.$this->get_full_name().'[adv]" value="1" '.($adv ? 'checked="checked"' : '').' />'
4869 .'<label for="'.$this->get_id().'adv">'.get_string('advanced').'</label>';
4870 $return .= '</div>';
4872 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
4878 * Selection of grade report in user profiles
4880 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4882 class admin_setting_grade_profilereport extends admin_setting_configselect {
4884 * Calls parent::__construct with specific arguments
4886 public function __construct() {
4887 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
4891 * Loads an array of choices for the configselect control
4893 * @return bool always return true
4895 public function load_choices() {
4896 if (is_array($this->choices)) {
4897 return true;
4899 $this->choices = array();
4901 global $CFG;
4902 require_once($CFG->libdir.'/gradelib.php');
4904 foreach (get_plugin_list('gradereport') as $plugin => $plugindir) {
4905 if (file_exists($plugindir.'/lib.php')) {
4906 require_once($plugindir.'/lib.php');
4907 $functionname = 'grade_report_'.$plugin.'_profilereport';
4908 if (function_exists($functionname)) {
4909 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
4913 return true;
4919 * Special class for register auth selection
4921 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4923 class admin_setting_special_registerauth extends admin_setting_configselect {
4925 * Calls parent::__construct with specific arguments
4927 public function __construct() {
4928 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
4932 * Returns the default option
4934 * @return string empty or default option
4936 public function get_defaultsetting() {
4937 $this->load_choices();
4938 $defaultsetting = parent::get_defaultsetting();
4939 if (array_key_exists($defaultsetting, $this->choices)) {
4940 return $defaultsetting;
4941 } else {
4942 return '';
4947 * Loads the possible choices for the array
4949 * @return bool always returns true
4951 public function load_choices() {
4952 global $CFG;
4954 if (is_array($this->choices)) {
4955 return true;
4957 $this->choices = array();
4958 $this->choices[''] = get_string('disable');
4960 $authsenabled = get_enabled_auth_plugins(true);
4962 foreach ($authsenabled as $auth) {
4963 $authplugin = get_auth_plugin($auth);
4964 if (!$authplugin->can_signup()) {
4965 continue;
4967 // Get the auth title (from core or own auth lang files)
4968 $authtitle = $authplugin->get_title();
4969 $this->choices[$auth] = $authtitle;
4971 return true;
4977 * General plugins manager
4979 class admin_page_pluginsoverview extends admin_externalpage {
4982 * Sets basic information about the external page
4984 public function __construct() {
4985 global $CFG;
4986 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
4987 "$CFG->wwwroot/$CFG->admin/plugins.php");
4992 * Module manage page
4994 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4996 class admin_page_managemods extends admin_externalpage {
4998 * Calls parent::__construct with specific arguments
5000 public function __construct() {
5001 global $CFG;
5002 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
5006 * Try to find the specified module
5008 * @param string $query The module to search for
5009 * @return array
5011 public function search($query) {
5012 global $CFG, $DB;
5013 if ($result = parent::search($query)) {
5014 return $result;
5017 $found = false;
5018 if ($modules = $DB->get_records('modules')) {
5019 foreach ($modules as $module) {
5020 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
5021 continue;
5023 if (strpos($module->name, $query) !== false) {
5024 $found = true;
5025 break;
5027 $strmodulename = get_string('modulename', $module->name);
5028 if (strpos(textlib::strtolower($strmodulename), $query) !== false) {
5029 $found = true;
5030 break;
5034 if ($found) {
5035 $result = new stdClass();
5036 $result->page = $this;
5037 $result->settings = array();
5038 return array($this->name => $result);
5039 } else {
5040 return array();
5047 * Special class for enrol plugins management.
5049 * @copyright 2010 Petr Skoda {@link http://skodak.org}
5050 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5052 class admin_setting_manageenrols extends admin_setting {
5054 * Calls parent::__construct with specific arguments
5056 public function __construct() {
5057 $this->nosave = true;
5058 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
5062 * Always returns true, does nothing
5064 * @return true
5066 public function get_setting() {
5067 return true;
5071 * Always returns true, does nothing
5073 * @return true
5075 public function get_defaultsetting() {
5076 return true;
5080 * Always returns '', does not write anything
5082 * @return string Always returns ''
5084 public function write_setting($data) {
5085 // do not write any setting
5086 return '';
5090 * Checks if $query is one of the available enrol plugins
5092 * @param string $query The string to search for
5093 * @return bool Returns true if found, false if not
5095 public function is_related($query) {
5096 if (parent::is_related($query)) {
5097 return true;
5100 $query = textlib::strtolower($query);
5101 $enrols = enrol_get_plugins(false);
5102 foreach ($enrols as $name=>$enrol) {
5103 $localised = get_string('pluginname', 'enrol_'.$name);
5104 if (strpos(textlib::strtolower($name), $query) !== false) {
5105 return true;
5107 if (strpos(textlib::strtolower($localised), $query) !== false) {
5108 return true;
5111 return false;
5115 * Builds the XHTML to display the control
5117 * @param string $data Unused
5118 * @param string $query
5119 * @return string
5121 public function output_html($data, $query='') {
5122 global $CFG, $OUTPUT, $DB, $PAGE;
5124 // Display strings.
5125 $strup = get_string('up');
5126 $strdown = get_string('down');
5127 $strsettings = get_string('settings');
5128 $strenable = get_string('enable');
5129 $strdisable = get_string('disable');
5130 $struninstall = get_string('uninstallplugin', 'admin');
5131 $strusage = get_string('enrolusage', 'enrol');
5132 $strversion = get_string('version');
5134 $pluginmanager = plugin_manager::instance();
5136 $enrols_available = enrol_get_plugins(false);
5137 $active_enrols = enrol_get_plugins(true);
5139 $allenrols = array();
5140 foreach ($active_enrols as $key=>$enrol) {
5141 $allenrols[$key] = true;
5143 foreach ($enrols_available as $key=>$enrol) {
5144 $allenrols[$key] = true;
5146 // Now find all borked plugins and at least allow then to uninstall.
5147 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
5148 foreach ($condidates as $candidate) {
5149 if (empty($allenrols[$candidate])) {
5150 $allenrols[$candidate] = true;
5154 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
5155 $return .= $OUTPUT->box_start('generalbox enrolsui');
5157 $table = new html_table();
5158 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
5159 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
5160 $table->id = 'courseenrolmentplugins';
5161 $table->attributes['class'] = 'admintable generaltable';
5162 $table->data = array();
5164 // Iterate through enrol plugins and add to the display table.
5165 $updowncount = 1;
5166 $enrolcount = count($active_enrols);
5167 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
5168 $printed = array();
5169 foreach($allenrols as $enrol => $unused) {
5170 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
5171 $version = get_config('enrol_'.$enrol, 'version');
5172 if ($version === false) {
5173 $version = '';
5176 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
5177 $name = get_string('pluginname', 'enrol_'.$enrol);
5178 } else {
5179 $name = $enrol;
5181 // Usage.
5182 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
5183 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
5184 $usage = "$ci / $cp";
5186 // Hide/show links.
5187 if (isset($active_enrols[$enrol])) {
5188 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
5189 $hideshow = "<a href=\"$aurl\">";
5190 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
5191 $enabled = true;
5192 $displayname = "<span>$name</span>";
5193 } else if (isset($enrols_available[$enrol])) {
5194 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
5195 $hideshow = "<a href=\"$aurl\">";
5196 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
5197 $enabled = false;
5198 $displayname = "<span class=\"dimmed_text\">$name</span>";
5199 } else {
5200 $hideshow = '';
5201 $enabled = false;
5202 $displayname = '<span class="notifyproblem">'.$name.'</span>';
5204 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
5205 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
5206 } else {
5207 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
5210 // Up/down link (only if enrol is enabled).
5211 $updown = '';
5212 if ($enabled) {
5213 if ($updowncount > 1) {
5214 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
5215 $updown .= "<a href=\"$aurl\">";
5216 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" class=\"iconsmall\" /></a>&nbsp;";
5217 } else {
5218 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5220 if ($updowncount < $enrolcount) {
5221 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
5222 $updown .= "<a href=\"$aurl\">";
5223 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" class=\"iconsmall\" /></a>";
5224 } else {
5225 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5227 ++$updowncount;
5230 // Add settings link.
5231 if (!$version) {
5232 $settings = '';
5233 } else if ($url = $plugininfo->get_settings_url()) {
5234 $settings = html_writer::link($url, $strsettings);
5235 } else {
5236 $settings = '';
5239 // Add uninstall info.
5240 if ($version) {
5241 $url = new moodle_url($plugininfo->get_uninstall_url(), array('return'=>'settings'));
5242 $uninstall = html_writer::link($url, $struninstall);
5243 } else {
5244 $uninstall = '';
5247 // Add a row to the table.
5248 $table->data[] = array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $uninstall);
5250 $printed[$enrol] = true;
5253 $return .= html_writer::table($table);
5254 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
5255 $return .= $OUTPUT->box_end();
5256 return highlight($query, $return);
5262 * Blocks manage page
5264 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5266 class admin_page_manageblocks extends admin_externalpage {
5268 * Calls parent::__construct with specific arguments
5270 public function __construct() {
5271 global $CFG;
5272 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
5276 * Search for a specific block
5278 * @param string $query The string to search for
5279 * @return array
5281 public function search($query) {
5282 global $CFG, $DB;
5283 if ($result = parent::search($query)) {
5284 return $result;
5287 $found = false;
5288 if ($blocks = $DB->get_records('block')) {
5289 foreach ($blocks as $block) {
5290 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
5291 continue;
5293 if (strpos($block->name, $query) !== false) {
5294 $found = true;
5295 break;
5297 $strblockname = get_string('pluginname', 'block_'.$block->name);
5298 if (strpos(textlib::strtolower($strblockname), $query) !== false) {
5299 $found = true;
5300 break;
5304 if ($found) {
5305 $result = new stdClass();
5306 $result->page = $this;
5307 $result->settings = array();
5308 return array($this->name => $result);
5309 } else {
5310 return array();
5316 * Message outputs configuration
5318 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5320 class admin_page_managemessageoutputs extends admin_externalpage {
5322 * Calls parent::__construct with specific arguments
5324 public function __construct() {
5325 global $CFG;
5326 parent::__construct('managemessageoutputs', get_string('managemessageoutputs', 'message'), new moodle_url('/admin/message.php'));
5330 * Search for a specific message processor
5332 * @param string $query The string to search for
5333 * @return array
5335 public function search($query) {
5336 global $CFG, $DB;
5337 if ($result = parent::search($query)) {
5338 return $result;
5341 $found = false;
5342 if ($processors = get_message_processors()) {
5343 foreach ($processors as $processor) {
5344 if (!$processor->available) {
5345 continue;
5347 if (strpos($processor->name, $query) !== false) {
5348 $found = true;
5349 break;
5351 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
5352 if (strpos(textlib::strtolower($strprocessorname), $query) !== false) {
5353 $found = true;
5354 break;
5358 if ($found) {
5359 $result = new stdClass();
5360 $result->page = $this;
5361 $result->settings = array();
5362 return array($this->name => $result);
5363 } else {
5364 return array();
5370 * Default message outputs configuration
5372 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5374 class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
5376 * Calls parent::__construct with specific arguments
5378 public function __construct() {
5379 global $CFG;
5380 admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
5386 * Manage question behaviours page
5388 * @copyright 2011 The Open University
5389 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5391 class admin_page_manageqbehaviours extends admin_externalpage {
5393 * Constructor
5395 public function __construct() {
5396 global $CFG;
5397 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
5398 new moodle_url('/admin/qbehaviours.php'));
5402 * Search question behaviours for the specified string
5404 * @param string $query The string to search for in question behaviours
5405 * @return array
5407 public function search($query) {
5408 global $CFG;
5409 if ($result = parent::search($query)) {
5410 return $result;
5413 $found = false;
5414 require_once($CFG->dirroot . '/question/engine/lib.php');
5415 foreach (get_plugin_list('qbehaviour') as $behaviour => $notused) {
5416 if (strpos(textlib::strtolower(question_engine::get_behaviour_name($behaviour)),
5417 $query) !== false) {
5418 $found = true;
5419 break;
5422 if ($found) {
5423 $result = new stdClass();
5424 $result->page = $this;
5425 $result->settings = array();
5426 return array($this->name => $result);
5427 } else {
5428 return array();
5435 * Question type manage page
5437 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5439 class admin_page_manageqtypes extends admin_externalpage {
5441 * Calls parent::__construct with specific arguments
5443 public function __construct() {
5444 global $CFG;
5445 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
5446 new moodle_url('/admin/qtypes.php'));
5450 * Search question types for the specified string
5452 * @param string $query The string to search for in question types
5453 * @return array
5455 public function search($query) {
5456 global $CFG;
5457 if ($result = parent::search($query)) {
5458 return $result;
5461 $found = false;
5462 require_once($CFG->dirroot . '/question/engine/bank.php');
5463 foreach (question_bank::get_all_qtypes() as $qtype) {
5464 if (strpos(textlib::strtolower($qtype->local_name()), $query) !== false) {
5465 $found = true;
5466 break;
5469 if ($found) {
5470 $result = new stdClass();
5471 $result->page = $this;
5472 $result->settings = array();
5473 return array($this->name => $result);
5474 } else {
5475 return array();
5481 class admin_page_manageportfolios extends admin_externalpage {
5483 * Calls parent::__construct with specific arguments
5485 public function __construct() {
5486 global $CFG;
5487 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
5488 "$CFG->wwwroot/$CFG->admin/portfolio.php");
5492 * Searches page for the specified string.
5493 * @param string $query The string to search for
5494 * @return bool True if it is found on this page
5496 public function search($query) {
5497 global $CFG;
5498 if ($result = parent::search($query)) {
5499 return $result;
5502 $found = false;
5503 $portfolios = get_plugin_list('portfolio');
5504 foreach ($portfolios as $p => $dir) {
5505 if (strpos($p, $query) !== false) {
5506 $found = true;
5507 break;
5510 if (!$found) {
5511 foreach (portfolio_instances(false, false) as $instance) {
5512 $title = $instance->get('name');
5513 if (strpos(textlib::strtolower($title), $query) !== false) {
5514 $found = true;
5515 break;
5520 if ($found) {
5521 $result = new stdClass();
5522 $result->page = $this;
5523 $result->settings = array();
5524 return array($this->name => $result);
5525 } else {
5526 return array();
5532 class admin_page_managerepositories extends admin_externalpage {
5534 * Calls parent::__construct with specific arguments
5536 public function __construct() {
5537 global $CFG;
5538 parent::__construct('managerepositories', get_string('manage',
5539 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
5543 * Searches page for the specified string.
5544 * @param string $query The string to search for
5545 * @return bool True if it is found on this page
5547 public function search($query) {
5548 global $CFG;
5549 if ($result = parent::search($query)) {
5550 return $result;
5553 $found = false;
5554 $repositories= get_plugin_list('repository');
5555 foreach ($repositories as $p => $dir) {
5556 if (strpos($p, $query) !== false) {
5557 $found = true;
5558 break;
5561 if (!$found) {
5562 foreach (repository::get_types() as $instance) {
5563 $title = $instance->get_typename();
5564 if (strpos(textlib::strtolower($title), $query) !== false) {
5565 $found = true;
5566 break;
5571 if ($found) {
5572 $result = new stdClass();
5573 $result->page = $this;
5574 $result->settings = array();
5575 return array($this->name => $result);
5576 } else {
5577 return array();
5584 * Special class for authentication administration.
5586 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5588 class admin_setting_manageauths extends admin_setting {
5590 * Calls parent::__construct with specific arguments
5592 public function __construct() {
5593 $this->nosave = true;
5594 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
5598 * Always returns true
5600 * @return true
5602 public function get_setting() {
5603 return true;
5607 * Always returns true
5609 * @return true
5611 public function get_defaultsetting() {
5612 return true;
5616 * Always returns '' and doesn't write anything
5618 * @return string Always returns ''
5620 public function write_setting($data) {
5621 // do not write any setting
5622 return '';
5626 * Search to find if Query is related to auth plugin
5628 * @param string $query The string to search for
5629 * @return bool true for related false for not
5631 public function is_related($query) {
5632 if (parent::is_related($query)) {
5633 return true;
5636 $authsavailable = get_plugin_list('auth');
5637 foreach ($authsavailable as $auth => $dir) {
5638 if (strpos($auth, $query) !== false) {
5639 return true;
5641 $authplugin = get_auth_plugin($auth);
5642 $authtitle = $authplugin->get_title();
5643 if (strpos(textlib::strtolower($authtitle), $query) !== false) {
5644 return true;
5647 return false;
5651 * Return XHTML to display control
5653 * @param mixed $data Unused
5654 * @param string $query
5655 * @return string highlight
5657 public function output_html($data, $query='') {
5658 global $CFG, $OUTPUT;
5661 // display strings
5662 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
5663 'settings', 'edit', 'name', 'enable', 'disable',
5664 'up', 'down', 'none'));
5665 $txt->updown = "$txt->up/$txt->down";
5667 $authsavailable = get_plugin_list('auth');
5668 get_enabled_auth_plugins(true); // fix the list of enabled auths
5669 if (empty($CFG->auth)) {
5670 $authsenabled = array();
5671 } else {
5672 $authsenabled = explode(',', $CFG->auth);
5675 // construct the display array, with enabled auth plugins at the top, in order
5676 $displayauths = array();
5677 $registrationauths = array();
5678 $registrationauths[''] = $txt->disable;
5679 foreach ($authsenabled as $auth) {
5680 $authplugin = get_auth_plugin($auth);
5681 /// Get the auth title (from core or own auth lang files)
5682 $authtitle = $authplugin->get_title();
5683 /// Apply titles
5684 $displayauths[$auth] = $authtitle;
5685 if ($authplugin->can_signup()) {
5686 $registrationauths[$auth] = $authtitle;
5690 foreach ($authsavailable as $auth => $dir) {
5691 if (array_key_exists($auth, $displayauths)) {
5692 continue; //already in the list
5694 $authplugin = get_auth_plugin($auth);
5695 /// Get the auth title (from core or own auth lang files)
5696 $authtitle = $authplugin->get_title();
5697 /// Apply titles
5698 $displayauths[$auth] = $authtitle;
5699 if ($authplugin->can_signup()) {
5700 $registrationauths[$auth] = $authtitle;
5704 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
5705 $return .= $OUTPUT->box_start('generalbox authsui');
5707 $table = new html_table();
5708 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings);
5709 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign');
5710 $table->data = array();
5711 $table->attributes['class'] = 'admintable generaltable';
5712 $table->id = 'manageauthtable';
5714 //add always enabled plugins first
5715 $displayname = "<span>".$displayauths['manual']."</span>";
5716 $settings = "<a href=\"auth_config.php?auth=manual\">{$txt->settings}</a>";
5717 //$settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
5718 $table->data[] = array($displayname, '', '', $settings);
5719 $displayname = "<span>".$displayauths['nologin']."</span>";
5720 $settings = "<a href=\"auth_config.php?auth=nologin\">{$txt->settings}</a>";
5721 $table->data[] = array($displayname, '', '', $settings);
5724 // iterate through auth plugins and add to the display table
5725 $updowncount = 1;
5726 $authcount = count($authsenabled);
5727 $url = "auth.php?sesskey=" . sesskey();
5728 foreach ($displayauths as $auth => $name) {
5729 if ($auth == 'manual' or $auth == 'nologin') {
5730 continue;
5732 // hide/show link
5733 if (in_array($auth, $authsenabled)) {
5734 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
5735 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5736 // $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\"><input type=\"checkbox\" checked /></a>";
5737 $enabled = true;
5738 $displayname = "<span>$name</span>";
5740 else {
5741 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
5742 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5743 // $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\"><input type=\"checkbox\" /></a>";
5744 $enabled = false;
5745 $displayname = "<span class=\"dimmed_text\">$name</span>";
5748 // up/down link (only if auth is enabled)
5749 $updown = '';
5750 if ($enabled) {
5751 if ($updowncount > 1) {
5752 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
5753 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5755 else {
5756 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5758 if ($updowncount < $authcount) {
5759 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
5760 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5762 else {
5763 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5765 ++ $updowncount;
5768 // settings link
5769 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
5770 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
5771 } else {
5772 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
5775 // add a row to the table
5776 $table->data[] =array($displayname, $hideshow, $updown, $settings);
5778 $return .= html_writer::table($table);
5779 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
5780 $return .= $OUTPUT->box_end();
5781 return highlight($query, $return);
5787 * Special class for authentication administration.
5789 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5791 class admin_setting_manageeditors extends admin_setting {
5793 * Calls parent::__construct with specific arguments
5795 public function __construct() {
5796 $this->nosave = true;
5797 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
5801 * Always returns true, does nothing
5803 * @return true
5805 public function get_setting() {
5806 return true;
5810 * Always returns true, does nothing
5812 * @return true
5814 public function get_defaultsetting() {
5815 return true;
5819 * Always returns '', does not write anything
5821 * @return string Always returns ''
5823 public function write_setting($data) {
5824 // do not write any setting
5825 return '';
5829 * Checks if $query is one of the available editors
5831 * @param string $query The string to search for
5832 * @return bool Returns true if found, false if not
5834 public function is_related($query) {
5835 if (parent::is_related($query)) {
5836 return true;
5839 $editors_available = editors_get_available();
5840 foreach ($editors_available as $editor=>$editorstr) {
5841 if (strpos($editor, $query) !== false) {
5842 return true;
5844 if (strpos(textlib::strtolower($editorstr), $query) !== false) {
5845 return true;
5848 return false;
5852 * Builds the XHTML to display the control
5854 * @param string $data Unused
5855 * @param string $query
5856 * @return string
5858 public function output_html($data, $query='') {
5859 global $CFG, $OUTPUT;
5861 // display strings
5862 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
5863 'up', 'down', 'none'));
5864 $struninstall = get_string('uninstallplugin', 'admin');
5866 $txt->updown = "$txt->up/$txt->down";
5868 $editors_available = editors_get_available();
5869 $active_editors = explode(',', $CFG->texteditors);
5871 $active_editors = array_reverse($active_editors);
5872 foreach ($active_editors as $key=>$editor) {
5873 if (empty($editors_available[$editor])) {
5874 unset($active_editors[$key]);
5875 } else {
5876 $name = $editors_available[$editor];
5877 unset($editors_available[$editor]);
5878 $editors_available[$editor] = $name;
5881 if (empty($active_editors)) {
5882 //$active_editors = array('textarea');
5884 $editors_available = array_reverse($editors_available, true);
5885 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
5886 $return .= $OUTPUT->box_start('generalbox editorsui');
5888 $table = new html_table();
5889 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
5890 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign');
5891 $table->id = 'editormanagement';
5892 $table->attributes['class'] = 'admintable generaltable';
5893 $table->data = array();
5895 // iterate through auth plugins and add to the display table
5896 $updowncount = 1;
5897 $editorcount = count($active_editors);
5898 $url = "editors.php?sesskey=" . sesskey();
5899 foreach ($editors_available as $editor => $name) {
5900 // hide/show link
5901 if (in_array($editor, $active_editors)) {
5902 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
5903 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5904 // $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\"><input type=\"checkbox\" checked /></a>";
5905 $enabled = true;
5906 $displayname = "<span>$name</span>";
5908 else {
5909 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
5910 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5911 // $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\"><input type=\"checkbox\" /></a>";
5912 $enabled = false;
5913 $displayname = "<span class=\"dimmed_text\">$name</span>";
5916 // up/down link (only if auth is enabled)
5917 $updown = '';
5918 if ($enabled) {
5919 if ($updowncount > 1) {
5920 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
5921 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5923 else {
5924 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5926 if ($updowncount < $editorcount) {
5927 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
5928 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5930 else {
5931 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5933 ++ $updowncount;
5936 // settings link
5937 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
5938 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
5939 $settings = "<a href='$eurl'>{$txt->settings}</a>";
5940 } else {
5941 $settings = '';
5944 if ($editor === 'textarea') {
5945 $uninstall = '';
5946 } else {
5947 $uurl = new moodle_url('/admin/editors.php', array('action'=>'uninstall', 'editor'=>$editor, 'sesskey'=>sesskey()));
5948 $uninstall = html_writer::link($uurl, $struninstall);
5951 // add a row to the table
5952 $table->data[] =array($displayname, $hideshow, $updown, $settings, $uninstall);
5954 $return .= html_writer::table($table);
5955 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
5956 $return .= $OUTPUT->box_end();
5957 return highlight($query, $return);
5963 * Special class for license administration.
5965 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5967 class admin_setting_managelicenses extends admin_setting {
5969 * Calls parent::__construct with specific arguments
5971 public function __construct() {
5972 $this->nosave = true;
5973 parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', '');
5977 * Always returns true, does nothing
5979 * @return true
5981 public function get_setting() {
5982 return true;
5986 * Always returns true, does nothing
5988 * @return true
5990 public function get_defaultsetting() {
5991 return true;
5995 * Always returns '', does not write anything
5997 * @return string Always returns ''
5999 public function write_setting($data) {
6000 // do not write any setting
6001 return '';
6005 * Builds the XHTML to display the control
6007 * @param string $data Unused
6008 * @param string $query
6009 * @return string
6011 public function output_html($data, $query='') {
6012 global $CFG, $OUTPUT;
6013 require_once($CFG->libdir . '/licenselib.php');
6014 $url = "licenses.php?sesskey=" . sesskey();
6016 // display strings
6017 $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none'));
6018 $licenses = license_manager::get_licenses();
6020 $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true);
6022 $return .= $OUTPUT->box_start('generalbox editorsui');
6024 $table = new html_table();
6025 $table->head = array($txt->name, $txt->enable);
6026 $table->colclasses = array('leftalign', 'centeralign');
6027 $table->id = 'availablelicenses';
6028 $table->attributes['class'] = 'admintable generaltable';
6029 $table->data = array();
6031 foreach ($licenses as $value) {
6032 $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank'));
6034 if ($value->enabled == 1) {
6035 $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
6036 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/hide'), 'class'=>'iconsmall', 'alt'=>'disable')));
6037 } else {
6038 $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
6039 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/show'), 'class'=>'iconsmall', 'alt'=>'enable')));
6042 if ($value->shortname == $CFG->sitedefaultlicense) {
6043 $displayname .= ' '.html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/locked'), 'class'=>'iconsmall', 'alt'=>get_string('default'), 'title'=>get_string('default')));
6044 $hideshow = '';
6047 $enabled = true;
6049 $table->data[] =array($displayname, $hideshow);
6051 $return .= html_writer::table($table);
6052 $return .= $OUTPUT->box_end();
6053 return highlight($query, $return);
6058 * Course formats manager. Allows to enable/disable formats and jump to settings
6060 class admin_setting_manageformats extends admin_setting {
6063 * Calls parent::__construct with specific arguments
6065 public function __construct() {
6066 $this->nosave = true;
6067 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
6071 * Always returns true
6073 * @return true
6075 public function get_setting() {
6076 return true;
6080 * Always returns true
6082 * @return true
6084 public function get_defaultsetting() {
6085 return true;
6089 * Always returns '' and doesn't write anything
6091 * @param mixed $data string or array, must not be NULL
6092 * @return string Always returns ''
6094 public function write_setting($data) {
6095 // do not write any setting
6096 return '';
6100 * Search to find if Query is related to format plugin
6102 * @param string $query The string to search for
6103 * @return bool true for related false for not
6105 public function is_related($query) {
6106 if (parent::is_related($query)) {
6107 return true;
6109 $formats = plugin_manager::instance()->get_plugins_of_type('format');
6110 foreach ($formats as $format) {
6111 if (strpos($format->component, $query) !== false ||
6112 strpos(textlib::strtolower($format->displayname), $query) !== false) {
6113 return true;
6116 return false;
6120 * Return XHTML to display control
6122 * @param mixed $data Unused
6123 * @param string $query
6124 * @return string highlight
6126 public function output_html($data, $query='') {
6127 global $CFG, $OUTPUT;
6128 $return = '';
6129 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
6130 $return .= $OUTPUT->box_start('generalbox formatsui');
6132 $formats = plugin_manager::instance()->get_plugins_of_type('format');
6134 // display strings
6135 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default', 'delete'));
6136 $txt->updown = "$txt->up/$txt->down";
6138 $table = new html_table();
6139 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->delete, $txt->settings);
6140 $table->align = array('left', 'center', 'center', 'center', 'center');
6141 $table->width = '90%';
6142 $table->attributes['class'] = 'manageformattable generaltable';
6143 $table->data = array();
6145 $cnt = 0;
6146 $defaultformat = get_config('moodlecourse', 'format');
6147 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
6148 foreach ($formats as $format) {
6149 $url = new moodle_url('/admin/courseformats.php',
6150 array('sesskey' => sesskey(), 'format' => $format->name));
6151 $isdefault = '';
6152 if ($format->is_enabled()) {
6153 $strformatname = html_writer::tag('span', $format->displayname);
6154 if ($defaultformat === $format->name) {
6155 $hideshow = $txt->default;
6156 } else {
6157 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
6158 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
6160 } else {
6161 $strformatname = html_writer::tag('span', $format->displayname, array('class' => 'dimmed_text'));
6162 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
6163 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
6165 $updown = '';
6166 if ($cnt) {
6167 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
6168 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
6169 } else {
6170 $updown .= $spacer;
6172 if ($cnt < count($formats) - 1) {
6173 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
6174 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
6175 } else {
6176 $updown .= $spacer;
6178 $cnt++;
6179 $settings = '';
6180 if ($format->get_settings_url()) {
6181 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
6183 $uninstall = '';
6184 if ($defaultformat !== $format->name) {
6185 $uninstall = html_writer::link($format->get_uninstall_url(), $txt->delete);
6187 $table->data[] =array($strformatname, $hideshow, $updown, $uninstall, $settings);
6189 $return .= html_writer::table($table);
6190 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
6191 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
6192 $return .= $OUTPUT->box_end();
6193 return highlight($query, $return);
6198 * Special class for filter administration.
6200 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6202 class admin_page_managefilters extends admin_externalpage {
6204 * Calls parent::__construct with specific arguments
6206 public function __construct() {
6207 global $CFG;
6208 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
6212 * Searches all installed filters for specified filter
6214 * @param string $query The filter(string) to search for
6215 * @param string $query
6217 public function search($query) {
6218 global $CFG;
6219 if ($result = parent::search($query)) {
6220 return $result;
6223 $found = false;
6224 $filternames = filter_get_all_installed();
6225 foreach ($filternames as $path => $strfiltername) {
6226 if (strpos(textlib::strtolower($strfiltername), $query) !== false) {
6227 $found = true;
6228 break;
6230 if (strpos($path, $query) !== false) {
6231 $found = true;
6232 break;
6236 if ($found) {
6237 $result = new stdClass;
6238 $result->page = $this;
6239 $result->settings = array();
6240 return array($this->name => $result);
6241 } else {
6242 return array();
6249 * Initialise admin page - this function does require login and permission
6250 * checks specified in page definition.
6252 * This function must be called on each admin page before other code.
6254 * @global moodle_page $PAGE
6256 * @param string $section name of page
6257 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
6258 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
6259 * added to the turn blocks editing on/off form, so this page reloads correctly.
6260 * @param string $actualurl if the actual page being viewed is not the normal one for this
6261 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
6262 * @param array $options Additional options that can be specified for page setup.
6263 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
6265 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
6266 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
6268 $PAGE->set_context(null); // hack - set context to something, by default to system context
6270 $site = get_site();
6271 require_login();
6273 if (!empty($options['pagelayout'])) {
6274 // A specific page layout has been requested.
6275 $PAGE->set_pagelayout($options['pagelayout']);
6276 } else if ($section === 'upgradesettings') {
6277 $PAGE->set_pagelayout('maintenance');
6278 } else {
6279 $PAGE->set_pagelayout('admin');
6282 $adminroot = admin_get_root(false, false); // settings not required for external pages
6283 $extpage = $adminroot->locate($section, true);
6285 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
6286 // The requested section isn't in the admin tree
6287 // It could be because the user has inadequate capapbilities or because the section doesn't exist
6288 if (!has_capability('moodle/site:config', context_system::instance())) {
6289 // The requested section could depend on a different capability
6290 // but most likely the user has inadequate capabilities
6291 print_error('accessdenied', 'admin');
6292 } else {
6293 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
6297 // this eliminates our need to authenticate on the actual pages
6298 if (!$extpage->check_access()) {
6299 print_error('accessdenied', 'admin');
6300 die;
6303 // $PAGE->set_extra_button($extrabutton); TODO
6305 if (!$actualurl) {
6306 $actualurl = $extpage->url;
6309 $PAGE->set_url($actualurl, $extraurlparams);
6310 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
6311 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
6314 if (empty($SITE->fullname) || empty($SITE->shortname)) {
6315 // During initial install.
6316 $strinstallation = get_string('installation', 'install');
6317 $strsettings = get_string('settings');
6318 $PAGE->navbar->add($strsettings);
6319 $PAGE->set_title($strinstallation);
6320 $PAGE->set_heading($strinstallation);
6321 $PAGE->set_cacheable(false);
6322 return;
6325 // Locate the current item on the navigation and make it active when found.
6326 $path = $extpage->path;
6327 $node = $PAGE->settingsnav;
6328 while ($node && count($path) > 0) {
6329 $node = $node->get(array_pop($path));
6331 if ($node) {
6332 $node->make_active();
6335 // Normal case.
6336 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
6337 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
6338 $USER->editing = $adminediting;
6341 $visiblepathtosection = array_reverse($extpage->visiblepath);
6343 if ($PAGE->user_allowed_editing()) {
6344 if ($PAGE->user_is_editing()) {
6345 $caption = get_string('blockseditoff');
6346 $url = new moodle_url($PAGE->url, array('adminedit'=>'0'));
6347 } else {
6348 $caption = get_string('blocksediton');
6349 $url = new moodle_url($PAGE->url, array('adminedit'=>'1'));
6351 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
6354 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
6355 $PAGE->set_heading($SITE->fullname);
6357 // prevent caching in nav block
6358 $PAGE->navigation->clear_cache();
6362 * Returns the reference to admin tree root
6364 * @return object admin_root object
6366 function admin_get_root($reload=false, $requirefulltree=true) {
6367 global $CFG, $DB, $OUTPUT;
6369 static $ADMIN = NULL;
6371 if (is_null($ADMIN)) {
6372 // create the admin tree!
6373 $ADMIN = new admin_root($requirefulltree);
6376 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
6377 $ADMIN->purge_children($requirefulltree);
6380 if (!$ADMIN->loaded) {
6381 // we process this file first to create categories first and in correct order
6382 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
6384 // now we process all other files in admin/settings to build the admin tree
6385 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
6386 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
6387 continue;
6389 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
6390 // plugins are loaded last - they may insert pages anywhere
6391 continue;
6393 require($file);
6395 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
6397 $ADMIN->loaded = true;
6400 return $ADMIN;
6403 /// settings utility functions
6406 * This function applies default settings.
6408 * @param object $node, NULL means complete tree, null by default
6409 * @param bool $unconditional if true overrides all values with defaults, null buy default
6411 function admin_apply_default_settings($node=NULL, $unconditional=true) {
6412 global $CFG;
6414 if (is_null($node)) {
6415 $node = admin_get_root(true, true);
6418 if ($node instanceof admin_category) {
6419 $entries = array_keys($node->children);
6420 foreach ($entries as $entry) {
6421 admin_apply_default_settings($node->children[$entry], $unconditional);
6424 } else if ($node instanceof admin_settingpage) {
6425 foreach ($node->settings as $setting) {
6426 if (!$unconditional and !is_null($setting->get_setting())) {
6427 //do not override existing defaults
6428 continue;
6430 $defaultsetting = $setting->get_defaultsetting();
6431 if (is_null($defaultsetting)) {
6432 // no value yet - default maybe applied after admin user creation or in upgradesettings
6433 continue;
6435 $setting->write_setting($defaultsetting);
6436 $setting->write_setting_flags(null);
6442 * Store changed settings, this function updates the errors variable in $ADMIN
6444 * @param object $formdata from form
6445 * @return int number of changed settings
6447 function admin_write_settings($formdata) {
6448 global $CFG, $SITE, $DB;
6450 $olddbsessions = !empty($CFG->dbsessions);
6451 $formdata = (array)$formdata;
6453 $data = array();
6454 foreach ($formdata as $fullname=>$value) {
6455 if (strpos($fullname, 's_') !== 0) {
6456 continue; // not a config value
6458 $data[$fullname] = $value;
6461 $adminroot = admin_get_root();
6462 $settings = admin_find_write_settings($adminroot, $data);
6464 $count = 0;
6465 foreach ($settings as $fullname=>$setting) {
6466 /** @var $setting admin_setting */
6467 $original = $setting->get_setting();
6468 $error = $setting->write_setting($data[$fullname]);
6469 if ($error !== '') {
6470 $adminroot->errors[$fullname] = new stdClass();
6471 $adminroot->errors[$fullname]->data = $data[$fullname];
6472 $adminroot->errors[$fullname]->id = $setting->get_id();
6473 $adminroot->errors[$fullname]->error = $error;
6474 } else {
6475 $setting->write_setting_flags($data);
6477 if ($setting->post_write_settings($original)) {
6478 $count++;
6482 if ($olddbsessions != !empty($CFG->dbsessions)) {
6483 require_logout();
6486 // Now update $SITE - just update the fields, in case other people have a
6487 // a reference to it (e.g. $PAGE, $COURSE).
6488 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
6489 foreach (get_object_vars($newsite) as $field => $value) {
6490 $SITE->$field = $value;
6493 // now reload all settings - some of them might depend on the changed
6494 admin_get_root(true);
6495 return $count;
6499 * Internal recursive function - finds all settings from submitted form
6501 * @param object $node Instance of admin_category, or admin_settingpage
6502 * @param array $data
6503 * @return array
6505 function admin_find_write_settings($node, $data) {
6506 $return = array();
6508 if (empty($data)) {
6509 return $return;
6512 if ($node instanceof admin_category) {
6513 $entries = array_keys($node->children);
6514 foreach ($entries as $entry) {
6515 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
6518 } else if ($node instanceof admin_settingpage) {
6519 foreach ($node->settings as $setting) {
6520 $fullname = $setting->get_full_name();
6521 if (array_key_exists($fullname, $data)) {
6522 $return[$fullname] = $setting;
6528 return $return;
6532 * Internal function - prints the search results
6534 * @param string $query String to search for
6535 * @return string empty or XHTML
6537 function admin_search_settings_html($query) {
6538 global $CFG, $OUTPUT;
6540 if (textlib::strlen($query) < 2) {
6541 return '';
6543 $query = textlib::strtolower($query);
6545 $adminroot = admin_get_root();
6546 $findings = $adminroot->search($query);
6547 $return = '';
6548 $savebutton = false;
6550 foreach ($findings as $found) {
6551 $page = $found->page;
6552 $settings = $found->settings;
6553 if ($page->is_hidden()) {
6554 // hidden pages are not displayed in search results
6555 continue;
6557 if ($page instanceof admin_externalpage) {
6558 $return .= $OUTPUT->heading(get_string('searchresults','admin').' - <a href="'.$page->url.'">'.highlight($query, $page->visiblename).'</a>', 2, 'main');
6559 } else if ($page instanceof admin_settingpage) {
6560 $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');
6561 } else {
6562 continue;
6564 if (!empty($settings)) {
6565 $return .= '<fieldset class="adminsettings">'."\n";
6566 foreach ($settings as $setting) {
6567 if (empty($setting->nosave)) {
6568 $savebutton = true;
6570 $return .= '<div class="clearer"><!-- --></div>'."\n";
6571 $fullname = $setting->get_full_name();
6572 if (array_key_exists($fullname, $adminroot->errors)) {
6573 $data = $adminroot->errors[$fullname]->data;
6574 } else {
6575 $data = $setting->get_setting();
6576 // do not use defaults if settings not available - upgradesettings handles the defaults!
6578 $return .= $setting->output_html($data, $query);
6580 $return .= '</fieldset>';
6584 if ($savebutton) {
6585 $return .= '<div class="form-buttons"><input class="form-submit" type="submit" value="'.get_string('savechanges','admin').'" /></div>';
6588 return $return;
6592 * Internal function - returns arrays of html pages with uninitialised settings
6594 * @param object $node Instance of admin_category or admin_settingpage
6595 * @return array
6597 function admin_output_new_settings_by_page($node) {
6598 global $OUTPUT;
6599 $return = array();
6601 if ($node instanceof admin_category) {
6602 $entries = array_keys($node->children);
6603 foreach ($entries as $entry) {
6604 $return += admin_output_new_settings_by_page($node->children[$entry]);
6607 } else if ($node instanceof admin_settingpage) {
6608 $newsettings = array();
6609 foreach ($node->settings as $setting) {
6610 if (is_null($setting->get_setting())) {
6611 $newsettings[] = $setting;
6614 if (count($newsettings) > 0) {
6615 $adminroot = admin_get_root();
6616 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
6617 $page .= '<fieldset class="adminsettings">'."\n";
6618 foreach ($newsettings as $setting) {
6619 $fullname = $setting->get_full_name();
6620 if (array_key_exists($fullname, $adminroot->errors)) {
6621 $data = $adminroot->errors[$fullname]->data;
6622 } else {
6623 $data = $setting->get_setting();
6624 if (is_null($data)) {
6625 $data = $setting->get_defaultsetting();
6628 $page .= '<div class="clearer"><!-- --></div>'."\n";
6629 $page .= $setting->output_html($data);
6631 $page .= '</fieldset>';
6632 $return[$node->name] = $page;
6636 return $return;
6640 * Format admin settings
6642 * @param object $setting
6643 * @param string $title label element
6644 * @param string $form form fragment, html code - not highlighted automatically
6645 * @param string $description
6646 * @param bool $label link label to id, true by default
6647 * @param string $warning warning text
6648 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
6649 * @param string $query search query to be highlighted
6650 * @return string XHTML
6652 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
6653 global $CFG;
6655 $name = empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name";
6656 $fullname = $setting->get_full_name();
6658 // sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate
6659 if ($label) {
6660 $labelfor = 'for = "'.$setting->get_id().'"';
6661 } else {
6662 $labelfor = '';
6664 $form .= $setting->output_setting_flags();
6666 $override = '';
6667 if (empty($setting->plugin)) {
6668 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
6669 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6671 } else {
6672 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
6673 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6677 if ($warning !== '') {
6678 $warning = '<div class="form-warning">'.$warning.'</div>';
6681 $defaults = array();
6682 if (!is_null($defaultinfo)) {
6683 if ($defaultinfo === '') {
6684 $defaultinfo = get_string('emptysettingvalue', 'admin');
6686 $defaults[] = $defaultinfo;
6689 $setting->get_setting_flag_defaults($defaults);
6691 if (!empty($defaults)) {
6692 $defaultinfo = implode(', ', $defaults);
6693 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
6694 $defaultinfo = '<div class="form-defaultinfo">'.get_string('defaultsettinginfo', 'admin', $defaultinfo).'</div>';
6698 $str = '
6699 <div class="form-item clearfix" id="admin-'.$setting->name.'">
6700 <div class="form-label">
6701 <label '.$labelfor.'>'.highlightfast($query, $title).$override.$warning.'</label>
6702 <span class="form-shortname">'.highlightfast($query, $name).'</span>
6703 </div>
6704 <div class="form-setting">'.$form.$defaultinfo.'</div>
6705 <div class="form-description">'.highlight($query, markdown_to_html($description)).'</div>
6706 </div>';
6708 $adminroot = admin_get_root();
6709 if (array_key_exists($fullname, $adminroot->errors)) {
6710 $str = '<fieldset class="error"><legend>'.$adminroot->errors[$fullname]->error.'</legend>'.$str.'</fieldset>';
6713 return $str;
6717 * Based on find_new_settings{@link ()} in upgradesettings.php
6718 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
6720 * @param object $node Instance of admin_category, or admin_settingpage
6721 * @return boolean true if any settings haven't been initialised, false if they all have
6723 function any_new_admin_settings($node) {
6725 if ($node instanceof admin_category) {
6726 $entries = array_keys($node->children);
6727 foreach ($entries as $entry) {
6728 if (any_new_admin_settings($node->children[$entry])) {
6729 return true;
6733 } else if ($node instanceof admin_settingpage) {
6734 foreach ($node->settings as $setting) {
6735 if ($setting->get_setting() === NULL) {
6736 return true;
6741 return false;
6745 * Moved from admin/replace.php so that we can use this in cron
6747 * @param string $search string to look for
6748 * @param string $replace string to replace
6749 * @return bool success or fail
6751 function db_replace($search, $replace) {
6752 global $DB, $CFG, $OUTPUT;
6754 // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
6755 $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log',
6756 'filter_config', 'sessions', 'events_queue', 'repository_instance_config',
6757 'block_instances', '');
6759 // Turn off time limits, sometimes upgrades can be slow.
6760 @set_time_limit(0);
6762 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
6763 return false;
6765 foreach ($tables as $table) {
6767 if (in_array($table, $skiptables)) { // Don't process these
6768 continue;
6771 if ($columns = $DB->get_columns($table)) {
6772 $DB->set_debug(true);
6773 foreach ($columns as $column => $data) {
6774 if (in_array($data->meta_type, array('C', 'X'))) { // Text stuff only
6775 //TODO: this should be definitively moved to DML driver to do the actual replace, this is not going to work for MSSQL and Oracle...
6776 $DB->execute("UPDATE {".$table."} SET $column = REPLACE($column, ?, ?)", array($search, $replace));
6779 $DB->set_debug(false);
6783 // delete modinfo caches
6784 rebuild_course_cache(0, true);
6786 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
6787 $blocks = get_plugin_list('block');
6788 foreach ($blocks as $blockname=>$fullblock) {
6789 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
6790 continue;
6793 if (!is_readable($fullblock.'/lib.php')) {
6794 continue;
6797 $function = 'block_'.$blockname.'_global_db_replace';
6798 include_once($fullblock.'/lib.php');
6799 if (!function_exists($function)) {
6800 continue;
6803 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
6804 $function($search, $replace);
6805 echo $OUTPUT->notification("...finished", 'notifysuccess');
6808 return true;
6812 * Manage repository settings
6814 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6816 class admin_setting_managerepository extends admin_setting {
6817 /** @var string */
6818 private $baseurl;
6821 * calls parent::__construct with specific arguments
6823 public function __construct() {
6824 global $CFG;
6825 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
6826 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
6830 * Always returns true, does nothing
6832 * @return true
6834 public function get_setting() {
6835 return true;
6839 * Always returns true does nothing
6841 * @return true
6843 public function get_defaultsetting() {
6844 return true;
6848 * Always returns s_managerepository
6850 * @return string Always return 's_managerepository'
6852 public function get_full_name() {
6853 return 's_managerepository';
6857 * Always returns '' doesn't do anything
6859 public function write_setting($data) {
6860 $url = $this->baseurl . '&amp;new=' . $data;
6861 return '';
6862 // TODO
6863 // Should not use redirect and exit here
6864 // Find a better way to do this.
6865 // redirect($url);
6866 // exit;
6870 * Searches repository plugins for one that matches $query
6872 * @param string $query The string to search for
6873 * @return bool true if found, false if not
6875 public function is_related($query) {
6876 if (parent::is_related($query)) {
6877 return true;
6880 $repositories= get_plugin_list('repository');
6881 foreach ($repositories as $p => $dir) {
6882 if (strpos($p, $query) !== false) {
6883 return true;
6886 foreach (repository::get_types() as $instance) {
6887 $title = $instance->get_typename();
6888 if (strpos(textlib::strtolower($title), $query) !== false) {
6889 return true;
6892 return false;
6896 * Helper function that generates a moodle_url object
6897 * relevant to the repository
6900 function repository_action_url($repository) {
6901 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
6905 * Builds XHTML to display the control
6907 * @param string $data Unused
6908 * @param string $query
6909 * @return string XHTML
6911 public function output_html($data, $query='') {
6912 global $CFG, $USER, $OUTPUT;
6914 // Get strings that are used
6915 $strshow = get_string('on', 'repository');
6916 $strhide = get_string('off', 'repository');
6917 $strdelete = get_string('disabled', 'repository');
6919 $actionchoicesforexisting = array(
6920 'show' => $strshow,
6921 'hide' => $strhide,
6922 'delete' => $strdelete
6925 $actionchoicesfornew = array(
6926 'newon' => $strshow,
6927 'newoff' => $strhide,
6928 'delete' => $strdelete
6931 $return = '';
6932 $return .= $OUTPUT->box_start('generalbox');
6934 // Set strings that are used multiple times
6935 $settingsstr = get_string('settings');
6936 $disablestr = get_string('disable');
6938 // Table to list plug-ins
6939 $table = new html_table();
6940 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
6941 $table->align = array('left', 'center', 'center', 'center', 'center');
6942 $table->data = array();
6944 // Get list of used plug-ins
6945 $repositorytypes = repository::get_types();
6946 if (!empty($repositorytypes)) {
6947 // Array to store plugins being used
6948 $alreadyplugins = array();
6949 $totalrepositorytypes = count($repositorytypes);
6950 $updowncount = 1;
6951 foreach ($repositorytypes as $i) {
6952 $settings = '';
6953 $typename = $i->get_typename();
6954 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
6955 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
6956 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
6958 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
6959 // Calculate number of instances in order to display them for the Moodle administrator
6960 if (!empty($instanceoptionnames)) {
6961 $params = array();
6962 $params['context'] = array(get_system_context());
6963 $params['onlyvisible'] = false;
6964 $params['type'] = $typename;
6965 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
6966 // site instances
6967 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
6968 $params['context'] = array();
6969 $instances = repository::static_function($typename, 'get_instances', $params);
6970 $courseinstances = array();
6971 $userinstances = array();
6973 foreach ($instances as $instance) {
6974 $repocontext = context::instance_by_id($instance->instance->contextid);
6975 if ($repocontext->contextlevel == CONTEXT_COURSE) {
6976 $courseinstances[] = $instance;
6977 } else if ($repocontext->contextlevel == CONTEXT_USER) {
6978 $userinstances[] = $instance;
6981 // course instances
6982 $instancenumber = count($courseinstances);
6983 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
6985 // user private instances
6986 $instancenumber = count($userinstances);
6987 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
6988 } else {
6989 $admininstancenumbertext = "";
6990 $courseinstancenumbertext = "";
6991 $userinstancenumbertext = "";
6994 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
6996 $settings .= $OUTPUT->container_start('mdl-left');
6997 $settings .= '<br/>';
6998 $settings .= $admininstancenumbertext;
6999 $settings .= '<br/>';
7000 $settings .= $courseinstancenumbertext;
7001 $settings .= '<br/>';
7002 $settings .= $userinstancenumbertext;
7003 $settings .= $OUTPUT->container_end();
7005 // Get the current visibility
7006 if ($i->get_visible()) {
7007 $currentaction = 'show';
7008 } else {
7009 $currentaction = 'hide';
7012 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
7014 // Display up/down link
7015 $updown = '';
7016 // Should be done with CSS instead.
7017 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
7019 if ($updowncount > 1) {
7020 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
7021 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
7023 else {
7024 $updown .= $spacer;
7026 if ($updowncount < $totalrepositorytypes) {
7027 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
7028 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
7030 else {
7031 $updown .= $spacer;
7034 $updowncount++;
7036 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
7038 if (!in_array($typename, $alreadyplugins)) {
7039 $alreadyplugins[] = $typename;
7044 // Get all the plugins that exist on disk
7045 $plugins = get_plugin_list('repository');
7046 if (!empty($plugins)) {
7047 foreach ($plugins as $plugin => $dir) {
7048 // Check that it has not already been listed
7049 if (!in_array($plugin, $alreadyplugins)) {
7050 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
7051 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
7056 $return .= html_writer::table($table);
7057 $return .= $OUTPUT->box_end();
7058 return highlight($query, $return);
7063 * Special checkbox for enable mobile web service
7064 * If enable then we store the service id of the mobile service into config table
7065 * If disable then we unstore the service id from the config table
7067 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
7069 /** @var boolean True means that the capability 'webservice/xmlrpc:use' is set for authenticated user role */
7070 private $xmlrpcuse;
7071 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
7072 private $restuse;
7075 * Return true if Authenticated user role has the capability 'webservice/xmlrpc:use' and 'webservice/rest:use', otherwise false.
7077 * @return boolean
7079 private function is_protocol_cap_allowed() {
7080 global $DB, $CFG;
7082 // We keep xmlrpc enabled for backward compatibility.
7083 // If the $this->xmlrpcuse variable is not set, it needs to be set.
7084 if (empty($this->xmlrpcuse) and $this->xmlrpcuse!==false) {
7085 $params = array();
7086 $params['permission'] = CAP_ALLOW;
7087 $params['roleid'] = $CFG->defaultuserroleid;
7088 $params['capability'] = 'webservice/xmlrpc:use';
7089 $this->xmlrpcuse = $DB->record_exists('role_capabilities', $params);
7092 // If the $this->restuse variable is not set, it needs to be set.
7093 if (empty($this->restuse) and $this->restuse!==false) {
7094 $params = array();
7095 $params['permission'] = CAP_ALLOW;
7096 $params['roleid'] = $CFG->defaultuserroleid;
7097 $params['capability'] = 'webservice/rest:use';
7098 $this->restuse = $DB->record_exists('role_capabilities', $params);
7101 return ($this->xmlrpcuse && $this->restuse);
7105 * Set the 'webservice/xmlrpc:use'/'webservice/rest:use' to the Authenticated user role (allow or not)
7106 * @param type $status true to allow, false to not set
7108 private function set_protocol_cap($status) {
7109 global $CFG;
7110 if ($status and !$this->is_protocol_cap_allowed()) {
7111 //need to allow the cap
7112 $permission = CAP_ALLOW;
7113 $assign = true;
7114 } else if (!$status and $this->is_protocol_cap_allowed()){
7115 //need to disallow the cap
7116 $permission = CAP_INHERIT;
7117 $assign = true;
7119 if (!empty($assign)) {
7120 $systemcontext = get_system_context();
7121 assign_capability('webservice/xmlrpc:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7122 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7127 * Builds XHTML to display the control.
7128 * The main purpose of this overloading is to display a warning when https
7129 * is not supported by the server
7130 * @param string $data Unused
7131 * @param string $query
7132 * @return string XHTML
7134 public function output_html($data, $query='') {
7135 global $CFG, $OUTPUT;
7136 $html = parent::output_html($data, $query);
7138 if ((string)$data === $this->yes) {
7139 require_once($CFG->dirroot . "/lib/filelib.php");
7140 $curl = new curl();
7141 $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); //force https url
7142 $curl->head($httpswwwroot . "/login/index.php");
7143 $info = $curl->get_info();
7144 if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
7145 $html .= $OUTPUT->notification(get_string('nohttpsformobilewarning', 'admin'));
7149 return $html;
7153 * Retrieves the current setting using the objects name
7155 * @return string
7157 public function get_setting() {
7158 global $CFG;
7160 // For install cli script, $CFG->defaultuserroleid is not set so return 0
7161 // Or if web services aren't enabled this can't be,
7162 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
7163 return 0;
7166 require_once($CFG->dirroot . '/webservice/lib.php');
7167 $webservicemanager = new webservice();
7168 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7169 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
7170 return $this->config_read($this->name); //same as returning 1
7171 } else {
7172 return 0;
7177 * Save the selected setting
7179 * @param string $data The selected site
7180 * @return string empty string or error message
7182 public function write_setting($data) {
7183 global $DB, $CFG;
7185 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
7186 if (empty($CFG->defaultuserroleid)) {
7187 return '';
7190 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
7192 require_once($CFG->dirroot . '/webservice/lib.php');
7193 $webservicemanager = new webservice();
7195 $updateprotocol = false;
7196 if ((string)$data === $this->yes) {
7197 //code run when enable mobile web service
7198 //enable web service systeme if necessary
7199 set_config('enablewebservices', true);
7201 //enable mobile service
7202 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7203 $mobileservice->enabled = 1;
7204 $webservicemanager->update_external_service($mobileservice);
7206 //enable xml-rpc server
7207 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7209 if (!in_array('xmlrpc', $activeprotocols)) {
7210 $activeprotocols[] = 'xmlrpc';
7211 $updateprotocol = true;
7214 if (!in_array('rest', $activeprotocols)) {
7215 $activeprotocols[] = 'rest';
7216 $updateprotocol = true;
7219 if ($updateprotocol) {
7220 set_config('webserviceprotocols', implode(',', $activeprotocols));
7223 //allow xml-rpc:use capability for authenticated user
7224 $this->set_protocol_cap(true);
7226 } else {
7227 //disable web service system if no other services are enabled
7228 $otherenabledservices = $DB->get_records_select('external_services',
7229 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
7230 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
7231 if (empty($otherenabledservices)) {
7232 set_config('enablewebservices', false);
7234 //also disable xml-rpc server
7235 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7236 $protocolkey = array_search('xmlrpc', $activeprotocols);
7237 if ($protocolkey !== false) {
7238 unset($activeprotocols[$protocolkey]);
7239 $updateprotocol = true;
7242 $protocolkey = array_search('rest', $activeprotocols);
7243 if ($protocolkey !== false) {
7244 unset($activeprotocols[$protocolkey]);
7245 $updateprotocol = true;
7248 if ($updateprotocol) {
7249 set_config('webserviceprotocols', implode(',', $activeprotocols));
7252 //disallow xml-rpc:use capability for authenticated user
7253 $this->set_protocol_cap(false);
7256 //disable the mobile service
7257 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7258 $mobileservice->enabled = 0;
7259 $webservicemanager->update_external_service($mobileservice);
7262 return (parent::write_setting($data));
7267 * Special class for management of external services
7269 * @author Petr Skoda (skodak)
7271 class admin_setting_manageexternalservices extends admin_setting {
7273 * Calls parent::__construct with specific arguments
7275 public function __construct() {
7276 $this->nosave = true;
7277 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
7281 * Always returns true, does nothing
7283 * @return true
7285 public function get_setting() {
7286 return true;
7290 * Always returns true, does nothing
7292 * @return true
7294 public function get_defaultsetting() {
7295 return true;
7299 * Always returns '', does not write anything
7301 * @return string Always returns ''
7303 public function write_setting($data) {
7304 // do not write any setting
7305 return '';
7309 * Checks if $query is one of the available external services
7311 * @param string $query The string to search for
7312 * @return bool Returns true if found, false if not
7314 public function is_related($query) {
7315 global $DB;
7317 if (parent::is_related($query)) {
7318 return true;
7321 $services = $DB->get_records('external_services', array(), 'id, name');
7322 foreach ($services as $service) {
7323 if (strpos(textlib::strtolower($service->name), $query) !== false) {
7324 return true;
7327 return false;
7331 * Builds the XHTML to display the control
7333 * @param string $data Unused
7334 * @param string $query
7335 * @return string
7337 public function output_html($data, $query='') {
7338 global $CFG, $OUTPUT, $DB;
7340 // display strings
7341 $stradministration = get_string('administration');
7342 $stredit = get_string('edit');
7343 $strservice = get_string('externalservice', 'webservice');
7344 $strdelete = get_string('delete');
7345 $strplugin = get_string('plugin', 'admin');
7346 $stradd = get_string('add');
7347 $strfunctions = get_string('functions', 'webservice');
7348 $strusers = get_string('users');
7349 $strserviceusers = get_string('serviceusers', 'webservice');
7351 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
7352 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
7353 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
7355 // built in services
7356 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
7357 $return = "";
7358 if (!empty($services)) {
7359 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
7363 $table = new html_table();
7364 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
7365 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7366 $table->id = 'builtinservices';
7367 $table->attributes['class'] = 'admintable externalservices generaltable';
7368 $table->data = array();
7370 // iterate through auth plugins and add to the display table
7371 foreach ($services as $service) {
7372 $name = $service->name;
7374 // hide/show link
7375 if ($service->enabled) {
7376 $displayname = "<span>$name</span>";
7377 } else {
7378 $displayname = "<span class=\"dimmed_text\">$name</span>";
7381 $plugin = $service->component;
7383 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7385 if ($service->restrictedusers) {
7386 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7387 } else {
7388 $users = get_string('allusers', 'webservice');
7391 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7393 // add a row to the table
7394 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
7396 $return .= html_writer::table($table);
7399 // Custom services
7400 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
7401 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
7403 $table = new html_table();
7404 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
7405 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7406 $table->id = 'customservices';
7407 $table->attributes['class'] = 'admintable externalservices generaltable';
7408 $table->data = array();
7410 // iterate through auth plugins and add to the display table
7411 foreach ($services as $service) {
7412 $name = $service->name;
7414 // hide/show link
7415 if ($service->enabled) {
7416 $displayname = "<span>$name</span>";
7417 } else {
7418 $displayname = "<span class=\"dimmed_text\">$name</span>";
7421 // delete link
7422 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
7424 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7426 if ($service->restrictedusers) {
7427 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7428 } else {
7429 $users = get_string('allusers', 'webservice');
7432 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7434 // add a row to the table
7435 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
7437 // add new custom service option
7438 $return .= html_writer::table($table);
7440 $return .= '<br />';
7441 // add a token to the table
7442 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
7444 return highlight($query, $return);
7449 * Special class for overview of external services
7451 * @author Jerome Mouneyrac
7453 class admin_setting_webservicesoverview extends admin_setting {
7456 * Calls parent::__construct with specific arguments
7458 public function __construct() {
7459 $this->nosave = true;
7460 parent::__construct('webservicesoverviewui',
7461 get_string('webservicesoverview', 'webservice'), '', '');
7465 * Always returns true, does nothing
7467 * @return true
7469 public function get_setting() {
7470 return true;
7474 * Always returns true, does nothing
7476 * @return true
7478 public function get_defaultsetting() {
7479 return true;
7483 * Always returns '', does not write anything
7485 * @return string Always returns ''
7487 public function write_setting($data) {
7488 // do not write any setting
7489 return '';
7493 * Builds the XHTML to display the control
7495 * @param string $data Unused
7496 * @param string $query
7497 * @return string
7499 public function output_html($data, $query='') {
7500 global $CFG, $OUTPUT;
7502 $return = "";
7503 $brtag = html_writer::empty_tag('br');
7505 // Enable mobile web service
7506 $enablemobile = new admin_setting_enablemobileservice('enablemobilewebservice',
7507 get_string('enablemobilewebservice', 'admin'),
7508 get_string('configenablemobilewebservice',
7509 'admin', ''), 0); //we don't want to display it but to know the ws mobile status
7510 $manageserviceurl = new moodle_url("/admin/settings.php?section=externalservices");
7511 $wsmobileparam = new stdClass();
7512 $wsmobileparam->enablemobileservice = get_string('enablemobilewebservice', 'admin');
7513 $wsmobileparam->manageservicelink = html_writer::link($manageserviceurl,
7514 get_string('externalservices', 'webservice'));
7515 $mobilestatus = $enablemobile->get_setting()?get_string('mobilewsenabled', 'webservice'):get_string('mobilewsdisabled', 'webservice');
7516 $wsmobileparam->wsmobilestatus = html_writer::tag('strong', $mobilestatus);
7517 $return .= $OUTPUT->heading(get_string('enablemobilewebservice', 'admin'), 3, 'main');
7518 $return .= $brtag . get_string('enablemobilewsoverview', 'webservice', $wsmobileparam)
7519 . $brtag . $brtag;
7521 /// One system controlling Moodle with Token
7522 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
7523 $table = new html_table();
7524 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7525 get_string('description'));
7526 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7527 $table->id = 'onesystemcontrol';
7528 $table->attributes['class'] = 'admintable wsoverview generaltable';
7529 $table->data = array();
7531 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
7532 . $brtag . $brtag;
7534 /// 1. Enable Web Services
7535 $row = array();
7536 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7537 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7538 array('href' => $url));
7539 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7540 if ($CFG->enablewebservices) {
7541 $status = get_string('yes');
7543 $row[1] = $status;
7544 $row[2] = get_string('enablewsdescription', 'webservice');
7545 $table->data[] = $row;
7547 /// 2. Enable protocols
7548 $row = array();
7549 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7550 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7551 array('href' => $url));
7552 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7553 //retrieve activated protocol
7554 $active_protocols = empty($CFG->webserviceprotocols) ?
7555 array() : explode(',', $CFG->webserviceprotocols);
7556 if (!empty($active_protocols)) {
7557 $status = "";
7558 foreach ($active_protocols as $protocol) {
7559 $status .= $protocol . $brtag;
7562 $row[1] = $status;
7563 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7564 $table->data[] = $row;
7566 /// 3. Create user account
7567 $row = array();
7568 $url = new moodle_url("/user/editadvanced.php?id=-1");
7569 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
7570 array('href' => $url));
7571 $row[1] = "";
7572 $row[2] = get_string('createuserdescription', 'webservice');
7573 $table->data[] = $row;
7575 /// 4. Add capability to users
7576 $row = array();
7577 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7578 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
7579 array('href' => $url));
7580 $row[1] = "";
7581 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
7582 $table->data[] = $row;
7584 /// 5. Select a web service
7585 $row = array();
7586 $url = new moodle_url("/admin/settings.php?section=externalservices");
7587 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7588 array('href' => $url));
7589 $row[1] = "";
7590 $row[2] = get_string('createservicedescription', 'webservice');
7591 $table->data[] = $row;
7593 /// 6. Add functions
7594 $row = array();
7595 $url = new moodle_url("/admin/settings.php?section=externalservices");
7596 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7597 array('href' => $url));
7598 $row[1] = "";
7599 $row[2] = get_string('addfunctionsdescription', 'webservice');
7600 $table->data[] = $row;
7602 /// 7. Add the specific user
7603 $row = array();
7604 $url = new moodle_url("/admin/settings.php?section=externalservices");
7605 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
7606 array('href' => $url));
7607 $row[1] = "";
7608 $row[2] = get_string('selectspecificuserdescription', 'webservice');
7609 $table->data[] = $row;
7611 /// 8. Create token for the specific user
7612 $row = array();
7613 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
7614 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
7615 array('href' => $url));
7616 $row[1] = "";
7617 $row[2] = get_string('createtokenforuserdescription', 'webservice');
7618 $table->data[] = $row;
7620 /// 9. Enable the documentation
7621 $row = array();
7622 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
7623 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
7624 array('href' => $url));
7625 $status = '<span class="warning">' . get_string('no') . '</span>';
7626 if ($CFG->enablewsdocumentation) {
7627 $status = get_string('yes');
7629 $row[1] = $status;
7630 $row[2] = get_string('enabledocumentationdescription', 'webservice');
7631 $table->data[] = $row;
7633 /// 10. Test the service
7634 $row = array();
7635 $url = new moodle_url("/admin/webservice/testclient.php");
7636 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7637 array('href' => $url));
7638 $row[1] = "";
7639 $row[2] = get_string('testwithtestclientdescription', 'webservice');
7640 $table->data[] = $row;
7642 $return .= html_writer::table($table);
7644 /// Users as clients with token
7645 $return .= $brtag . $brtag . $brtag;
7646 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
7647 $table = new html_table();
7648 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7649 get_string('description'));
7650 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7651 $table->id = 'userasclients';
7652 $table->attributes['class'] = 'admintable wsoverview generaltable';
7653 $table->data = array();
7655 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
7656 $brtag . $brtag;
7658 /// 1. Enable Web Services
7659 $row = array();
7660 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7661 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7662 array('href' => $url));
7663 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7664 if ($CFG->enablewebservices) {
7665 $status = get_string('yes');
7667 $row[1] = $status;
7668 $row[2] = get_string('enablewsdescription', 'webservice');
7669 $table->data[] = $row;
7671 /// 2. Enable protocols
7672 $row = array();
7673 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7674 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7675 array('href' => $url));
7676 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7677 //retrieve activated protocol
7678 $active_protocols = empty($CFG->webserviceprotocols) ?
7679 array() : explode(',', $CFG->webserviceprotocols);
7680 if (!empty($active_protocols)) {
7681 $status = "";
7682 foreach ($active_protocols as $protocol) {
7683 $status .= $protocol . $brtag;
7686 $row[1] = $status;
7687 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7688 $table->data[] = $row;
7691 /// 3. Select a web service
7692 $row = array();
7693 $url = new moodle_url("/admin/settings.php?section=externalservices");
7694 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7695 array('href' => $url));
7696 $row[1] = "";
7697 $row[2] = get_string('createserviceforusersdescription', 'webservice');
7698 $table->data[] = $row;
7700 /// 4. Add functions
7701 $row = array();
7702 $url = new moodle_url("/admin/settings.php?section=externalservices");
7703 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7704 array('href' => $url));
7705 $row[1] = "";
7706 $row[2] = get_string('addfunctionsdescription', 'webservice');
7707 $table->data[] = $row;
7709 /// 5. Add capability to users
7710 $row = array();
7711 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7712 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
7713 array('href' => $url));
7714 $row[1] = "";
7715 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
7716 $table->data[] = $row;
7718 /// 6. Test the service
7719 $row = array();
7720 $url = new moodle_url("/admin/webservice/testclient.php");
7721 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7722 array('href' => $url));
7723 $row[1] = "";
7724 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
7725 $table->data[] = $row;
7727 $return .= html_writer::table($table);
7729 return highlight($query, $return);
7736 * Special class for web service protocol administration.
7738 * @author Petr Skoda (skodak)
7740 class admin_setting_managewebserviceprotocols extends admin_setting {
7743 * Calls parent::__construct with specific arguments
7745 public function __construct() {
7746 $this->nosave = true;
7747 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
7751 * Always returns true, does nothing
7753 * @return true
7755 public function get_setting() {
7756 return true;
7760 * Always returns true, does nothing
7762 * @return true
7764 public function get_defaultsetting() {
7765 return true;
7769 * Always returns '', does not write anything
7771 * @return string Always returns ''
7773 public function write_setting($data) {
7774 // do not write any setting
7775 return '';
7779 * Checks if $query is one of the available webservices
7781 * @param string $query The string to search for
7782 * @return bool Returns true if found, false if not
7784 public function is_related($query) {
7785 if (parent::is_related($query)) {
7786 return true;
7789 $protocols = get_plugin_list('webservice');
7790 foreach ($protocols as $protocol=>$location) {
7791 if (strpos($protocol, $query) !== false) {
7792 return true;
7794 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
7795 if (strpos(textlib::strtolower($protocolstr), $query) !== false) {
7796 return true;
7799 return false;
7803 * Builds the XHTML to display the control
7805 * @param string $data Unused
7806 * @param string $query
7807 * @return string
7809 public function output_html($data, $query='') {
7810 global $CFG, $OUTPUT;
7812 // display strings
7813 $stradministration = get_string('administration');
7814 $strsettings = get_string('settings');
7815 $stredit = get_string('edit');
7816 $strprotocol = get_string('protocol', 'webservice');
7817 $strenable = get_string('enable');
7818 $strdisable = get_string('disable');
7819 $strversion = get_string('version');
7821 $protocols_available = get_plugin_list('webservice');
7822 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7823 ksort($protocols_available);
7825 foreach ($active_protocols as $key=>$protocol) {
7826 if (empty($protocols_available[$protocol])) {
7827 unset($active_protocols[$key]);
7831 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
7832 $return .= $OUTPUT->box_start('generalbox webservicesui');
7834 $table = new html_table();
7835 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
7836 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7837 $table->id = 'webserviceprotocols';
7838 $table->attributes['class'] = 'admintable generaltable';
7839 $table->data = array();
7841 // iterate through auth plugins and add to the display table
7842 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
7843 foreach ($protocols_available as $protocol => $location) {
7844 $name = get_string('pluginname', 'webservice_'.$protocol);
7846 $plugin = new stdClass();
7847 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
7848 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
7850 $version = isset($plugin->version) ? $plugin->version : '';
7852 // hide/show link
7853 if (in_array($protocol, $active_protocols)) {
7854 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
7855 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
7856 $displayname = "<span>$name</span>";
7857 } else {
7858 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
7859 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
7860 $displayname = "<span class=\"dimmed_text\">$name</span>";
7863 // settings link
7864 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
7865 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
7866 } else {
7867 $settings = '';
7870 // add a row to the table
7871 $table->data[] = array($displayname, $version, $hideshow, $settings);
7873 $return .= html_writer::table($table);
7874 $return .= get_string('configwebserviceplugins', 'webservice');
7875 $return .= $OUTPUT->box_end();
7877 return highlight($query, $return);
7883 * Special class for web service token administration.
7885 * @author Jerome Mouneyrac
7887 class admin_setting_managewebservicetokens extends admin_setting {
7890 * Calls parent::__construct with specific arguments
7892 public function __construct() {
7893 $this->nosave = true;
7894 parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
7898 * Always returns true, does nothing
7900 * @return true
7902 public function get_setting() {
7903 return true;
7907 * Always returns true, does nothing
7909 * @return true
7911 public function get_defaultsetting() {
7912 return true;
7916 * Always returns '', does not write anything
7918 * @return string Always returns ''
7920 public function write_setting($data) {
7921 // do not write any setting
7922 return '';
7926 * Builds the XHTML to display the control
7928 * @param string $data Unused
7929 * @param string $query
7930 * @return string
7932 public function output_html($data, $query='') {
7933 global $CFG, $OUTPUT, $DB, $USER;
7935 // display strings
7936 $stroperation = get_string('operation', 'webservice');
7937 $strtoken = get_string('token', 'webservice');
7938 $strservice = get_string('service', 'webservice');
7939 $struser = get_string('user');
7940 $strcontext = get_string('context', 'webservice');
7941 $strvaliduntil = get_string('validuntil', 'webservice');
7942 $striprestriction = get_string('iprestriction', 'webservice');
7944 $return = $OUTPUT->box_start('generalbox webservicestokenui');
7946 $table = new html_table();
7947 $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation);
7948 $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'centeralign');
7949 $table->id = 'webservicetokens';
7950 $table->attributes['class'] = 'admintable generaltable';
7951 $table->data = array();
7953 $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
7955 //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN
7957 //here retrieve token list (including linked users firstname/lastname and linked services name)
7958 $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid
7959 FROM {external_tokens} t, {user} u, {external_services} s
7960 WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id";
7961 $tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT));
7962 if (!empty($tokens)) {
7963 foreach ($tokens as $token) {
7964 //TODO: retrieve context
7966 $delete = "<a href=\"".$tokenpageurl."&amp;action=delete&amp;tokenid=".$token->id."\">";
7967 $delete .= get_string('delete')."</a>";
7969 $validuntil = '';
7970 if (!empty($token->validuntil)) {
7971 $validuntil = userdate($token->validuntil, get_string('strftimedatetime', 'langconfig'));
7974 $iprestriction = '';
7975 if (!empty($token->iprestriction)) {
7976 $iprestriction = $token->iprestriction;
7979 $userprofilurl = new moodle_url('/user/profile.php?id='.$token->userid);
7980 $useratag = html_writer::start_tag('a', array('href' => $userprofilurl));
7981 $useratag .= $token->firstname." ".$token->lastname;
7982 $useratag .= html_writer::end_tag('a');
7984 //check user missing capabilities
7985 require_once($CFG->dirroot . '/webservice/lib.php');
7986 $webservicemanager = new webservice();
7987 $usermissingcaps = $webservicemanager->get_missing_capabilities_by_users(
7988 array(array('id' => $token->userid)), $token->serviceid);
7990 if (!is_siteadmin($token->userid) and
7991 array_key_exists($token->userid, $usermissingcaps)) {
7992 $missingcapabilities = implode(', ',
7993 $usermissingcaps[$token->userid]);
7994 if (!empty($missingcapabilities)) {
7995 $useratag .= html_writer::tag('div',
7996 get_string('usermissingcaps', 'webservice',
7997 $missingcapabilities)
7998 . '&nbsp;' . $OUTPUT->help_icon('missingcaps', 'webservice'),
7999 array('class' => 'missingcaps'));
8003 $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete);
8006 $return .= html_writer::table($table);
8007 } else {
8008 $return .= get_string('notoken', 'webservice');
8011 $return .= $OUTPUT->box_end();
8012 // add a token to the table
8013 $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
8014 $return .= get_string('add')."</a>";
8016 return highlight($query, $return);
8022 * Colour picker
8024 * @copyright 2010 Sam Hemelryk
8025 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8027 class admin_setting_configcolourpicker extends admin_setting {
8030 * Information for previewing the colour
8032 * @var array|null
8034 protected $previewconfig = null;
8038 * @param string $name
8039 * @param string $visiblename
8040 * @param string $description
8041 * @param string $defaultsetting
8042 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
8044 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig=null) {
8045 $this->previewconfig = $previewconfig;
8046 parent::__construct($name, $visiblename, $description, $defaultsetting);
8050 * Return the setting
8052 * @return mixed returns config if successful else null
8054 public function get_setting() {
8055 return $this->config_read($this->name);
8059 * Saves the setting
8061 * @param string $data
8062 * @return bool
8064 public function write_setting($data) {
8065 $data = $this->validate($data);
8066 if ($data === false) {
8067 return get_string('validateerror', 'admin');
8069 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
8073 * Validates the colour that was entered by the user
8075 * @param string $data
8076 * @return string|false
8078 protected function validate($data) {
8080 * List of valid HTML colour names
8082 * @var array
8084 $colornames = array(
8085 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
8086 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
8087 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
8088 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
8089 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
8090 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
8091 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
8092 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
8093 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
8094 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
8095 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
8096 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
8097 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
8098 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
8099 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
8100 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
8101 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
8102 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
8103 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
8104 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
8105 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
8106 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
8107 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
8108 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
8109 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
8110 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
8111 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
8112 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
8113 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
8114 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
8115 'whitesmoke', 'yellow', 'yellowgreen'
8118 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
8119 if (strpos($data, '#')!==0) {
8120 $data = '#'.$data;
8122 return $data;
8123 } else if (in_array(strtolower($data), $colornames)) {
8124 return $data;
8125 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
8126 return $data;
8127 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
8128 return $data;
8129 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
8130 return $data;
8131 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
8132 return $data;
8133 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
8134 return $data;
8135 } else if (empty($data)) {
8136 return $this->defaultsetting;
8137 } else {
8138 return false;
8143 * Generates the HTML for the setting
8145 * @global moodle_page $PAGE
8146 * @global core_renderer $OUTPUT
8147 * @param string $data
8148 * @param string $query
8150 public function output_html($data, $query = '') {
8151 global $PAGE, $OUTPUT;
8152 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
8153 $content = html_writer::start_tag('div', array('class'=>'form-colourpicker defaultsnext'));
8154 $content .= html_writer::tag('div', $OUTPUT->pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', array('class'=>'loadingicon')), array('class'=>'admin_colourpicker clearfix'));
8155 $content .= html_writer::empty_tag('input', array('type'=>'text','id'=>$this->get_id(), 'name'=>$this->get_full_name(), 'value'=>$data, 'size'=>'12'));
8156 if (!empty($this->previewconfig)) {
8157 $content .= html_writer::empty_tag('input', array('type'=>'button','id'=>$this->get_id().'_preview', 'value'=>get_string('preview'), 'class'=>'admin_colourpicker_preview'));
8159 $content .= html_writer::end_tag('div');
8160 return format_admin_setting($this, $this->visiblename, $content, $this->description, false, '', $this->get_defaultsetting(), $query);
8166 * Class used for uploading of one file into file storage,
8167 * the file name is stored in config table.
8169 * Please note you need to implement your own '_pluginfile' callback function,
8170 * this setting only stores the file, it does not deal with file serving.
8172 * @copyright 2013 Petr Skoda {@link http://skodak.org}
8173 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8175 class admin_setting_configstoredfile extends admin_setting {
8176 /** @var array file area options - should be one file only */
8177 protected $options;
8178 /** @var string name of the file area */
8179 protected $filearea;
8180 /** @var int intemid */
8181 protected $itemid;
8182 /** @var string used for detection of changes */
8183 protected $oldhashes;
8186 * Create new stored file setting.
8188 * @param string $name low level setting name
8189 * @param string $visiblename human readable setting name
8190 * @param string $description description of setting
8191 * @param mixed $filearea file area for file storage
8192 * @param int $itemid itemid for file storage
8193 * @param array $options file area options
8195 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
8196 parent::__construct($name, $visiblename, $description, '');
8197 $this->filearea = $filearea;
8198 $this->itemid = $itemid;
8199 $this->options = (array)$options;
8203 * Applies defaults and returns all options.
8204 * @return array
8206 protected function get_options() {
8207 global $CFG;
8209 require_once("$CFG->libdir/filelib.php");
8210 require_once("$CFG->dirroot/repository/lib.php");
8211 $defaults = array(
8212 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
8213 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
8214 'context' => context_system::instance());
8215 foreach($this->options as $k => $v) {
8216 $defaults[$k] = $v;
8219 return $defaults;
8222 public function get_setting() {
8223 return $this->config_read($this->name);
8226 public function write_setting($data) {
8227 global $USER;
8229 // Let's not deal with validation here, this is for admins only.
8230 $current = $this->get_setting();
8231 if (empty($data)) {
8232 // Most probably applying default settings.
8233 if ($current === null) {
8234 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
8236 return '';
8237 } else if (!is_number($data)) {
8238 // Draft item id is expected here!
8239 return get_string('errorsetting', 'admin');
8242 $options = $this->get_options();
8243 $fs = get_file_storage();
8244 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8246 $this->oldhashes = null;
8247 if ($current) {
8248 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8249 if ($file = $fs->get_file_by_hash($hash)) {
8250 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
8252 unset($file);
8255 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
8256 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
8257 $usercontext = context_user::instance($USER->id);
8258 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.')) {
8259 return get_string('errorsetting', 'admin');
8263 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8264 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
8266 $filepath = '';
8267 if ($files) {
8268 /** @var stored_file $file */
8269 $file = reset($files);
8270 $filepath = $file->get_filepath().$file->get_filename();
8273 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
8276 public function post_write_settings($original) {
8277 $options = $this->get_options();
8278 $fs = get_file_storage();
8279 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8281 $current = $this->get_setting();
8282 $newhashes = null;
8283 if ($current) {
8284 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8285 if ($file = $fs->get_file_by_hash($hash)) {
8286 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
8288 unset($file);
8291 if ($this->oldhashes === $newhashes) {
8292 $this->oldhashes = null;
8293 return false;
8295 $this->oldhashes = null;
8297 $callbackfunction = $this->updatedcallback;
8298 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
8299 $callbackfunction($this->get_full_name());
8301 return true;
8304 public function output_html($data, $query = '') {
8305 global $PAGE, $CFG;
8307 $options = $this->get_options();
8308 $id = $this->get_id();
8309 $elname = $this->get_full_name();
8310 $draftitemid = file_get_submitted_draft_itemid($elname);
8311 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8312 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8314 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
8315 require_once("$CFG->dirroot/lib/form/filemanager.php");
8317 $fmoptions = new stdClass();
8318 $fmoptions->mainfile = $options['mainfile'];
8319 $fmoptions->maxbytes = $options['maxbytes'];
8320 $fmoptions->maxfiles = $options['maxfiles'];
8321 $fmoptions->client_id = uniqid();
8322 $fmoptions->itemid = $draftitemid;
8323 $fmoptions->subdirs = $options['subdirs'];
8324 $fmoptions->target = $id;
8325 $fmoptions->accepted_types = $options['accepted_types'];
8326 $fmoptions->return_types = $options['return_types'];
8327 $fmoptions->context = $options['context'];
8328 $fmoptions->areamaxbytes = $options['areamaxbytes'];
8330 $fm = new form_filemanager($fmoptions);
8331 $output = $PAGE->get_renderer('core', 'files');
8332 $html = $output->render($fm);
8334 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
8335 $html .= '<input value="" id="'.$id.'" type="hidden" />';
8337 return format_admin_setting($this, $this->visiblename,
8338 '<div class="form-filemanager">'.$html.'</div>', $this->description, true, '', '', $query);
8344 * Administration interface for user specified regular expressions for device detection.
8346 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8348 class admin_setting_devicedetectregex extends admin_setting {
8351 * Calls parent::__construct with specific args
8353 * @param string $name
8354 * @param string $visiblename
8355 * @param string $description
8356 * @param mixed $defaultsetting
8358 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
8359 global $CFG;
8360 parent::__construct($name, $visiblename, $description, $defaultsetting);
8364 * Return the current setting(s)
8366 * @return array Current settings array
8368 public function get_setting() {
8369 global $CFG;
8371 $config = $this->config_read($this->name);
8372 if (is_null($config)) {
8373 return null;
8376 return $this->prepare_form_data($config);
8380 * Save selected settings
8382 * @param array $data Array of settings to save
8383 * @return bool
8385 public function write_setting($data) {
8386 if (empty($data)) {
8387 $data = array();
8390 if ($this->config_write($this->name, $this->process_form_data($data))) {
8391 return ''; // success
8392 } else {
8393 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
8398 * Return XHTML field(s) for regexes
8400 * @param array $data Array of options to set in HTML
8401 * @return string XHTML string for the fields and wrapping div(s)
8403 public function output_html($data, $query='') {
8404 global $OUTPUT;
8406 $out = html_writer::start_tag('table', array('border' => 1, 'class' => 'generaltable'));
8407 $out .= html_writer::start_tag('thead');
8408 $out .= html_writer::start_tag('tr');
8409 $out .= html_writer::tag('th', get_string('devicedetectregexexpression', 'admin'));
8410 $out .= html_writer::tag('th', get_string('devicedetectregexvalue', 'admin'));
8411 $out .= html_writer::end_tag('tr');
8412 $out .= html_writer::end_tag('thead');
8413 $out .= html_writer::start_tag('tbody');
8415 if (empty($data)) {
8416 $looplimit = 1;
8417 } else {
8418 $looplimit = (count($data)/2)+1;
8421 for ($i=0; $i<$looplimit; $i++) {
8422 $out .= html_writer::start_tag('tr');
8424 $expressionname = 'expression'.$i;
8426 if (!empty($data[$expressionname])){
8427 $expression = $data[$expressionname];
8428 } else {
8429 $expression = '';
8432 $out .= html_writer::tag('td',
8433 html_writer::empty_tag('input',
8434 array(
8435 'type' => 'text',
8436 'class' => 'form-text',
8437 'name' => $this->get_full_name().'[expression'.$i.']',
8438 'value' => $expression,
8440 ), array('class' => 'c'.$i)
8443 $valuename = 'value'.$i;
8445 if (!empty($data[$valuename])){
8446 $value = $data[$valuename];
8447 } else {
8448 $value= '';
8451 $out .= html_writer::tag('td',
8452 html_writer::empty_tag('input',
8453 array(
8454 'type' => 'text',
8455 'class' => 'form-text',
8456 'name' => $this->get_full_name().'[value'.$i.']',
8457 'value' => $value,
8459 ), array('class' => 'c'.$i)
8462 $out .= html_writer::end_tag('tr');
8465 $out .= html_writer::end_tag('tbody');
8466 $out .= html_writer::end_tag('table');
8468 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', null, $query);
8472 * Converts the string of regexes
8474 * @see self::process_form_data()
8475 * @param $regexes string of regexes
8476 * @return array of form fields and their values
8478 protected function prepare_form_data($regexes) {
8480 $regexes = json_decode($regexes);
8482 $form = array();
8484 $i = 0;
8486 foreach ($regexes as $value => $regex) {
8487 $expressionname = 'expression'.$i;
8488 $valuename = 'value'.$i;
8490 $form[$expressionname] = $regex;
8491 $form[$valuename] = $value;
8492 $i++;
8495 return $form;
8499 * Converts the data from admin settings form into a string of regexes
8501 * @see self::prepare_form_data()
8502 * @param array $data array of admin form fields and values
8503 * @return false|string of regexes
8505 protected function process_form_data(array $form) {
8507 $count = count($form); // number of form field values
8509 if ($count % 2) {
8510 // we must get five fields per expression
8511 return false;
8514 $regexes = array();
8515 for ($i = 0; $i < $count / 2; $i++) {
8516 $expressionname = "expression".$i;
8517 $valuename = "value".$i;
8519 $expression = trim($form['expression'.$i]);
8520 $value = trim($form['value'.$i]);
8522 if (empty($expression)){
8523 continue;
8526 $regexes[$value] = $expression;
8529 $regexes = json_encode($regexes);
8531 return $regexes;
8536 * Multiselect for current modules
8538 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8540 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
8541 private $excludesystem;
8544 * Calls parent::__construct - note array $choices is not required
8546 * @param string $name setting name
8547 * @param string $visiblename localised setting name
8548 * @param string $description setting description
8549 * @param array $defaultsetting a plain array of default module ids
8550 * @param bool $excludesystem If true, excludes modules with 'system' archetype
8552 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
8553 $excludesystem = true) {
8554 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
8555 $this->excludesystem = $excludesystem;
8559 * Loads an array of current module choices
8561 * @return bool always return true
8563 public function load_choices() {
8564 if (is_array($this->choices)) {
8565 return true;
8567 $this->choices = array();
8569 global $CFG, $DB;
8570 $records = $DB->get_records('modules', array('visible'=>1), 'name');
8571 foreach ($records as $record) {
8572 // Exclude modules if the code doesn't exist
8573 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
8574 // Also exclude system modules (if specified)
8575 if (!($this->excludesystem &&
8576 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
8577 MOD_ARCHETYPE_SYSTEM)) {
8578 $this->choices[$record->id] = $record->name;
8582 return true;