Merge branch 'MDL-40255_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE
[moodle.git] / lib / adminlib.php
blobaf6bab71f73cdbd81c29c5c62db46b4482974f86
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;
1511 * Constructor
1512 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1513 * or 'myplugin/mysetting' for ones in config_plugins.
1514 * @param string $visiblename localised name
1515 * @param string $description localised long description
1516 * @param mixed $defaultsetting string or array depending on implementation
1518 public function __construct($name, $visiblename, $description, $defaultsetting) {
1519 $this->parse_setting_name($name);
1520 $this->visiblename = $visiblename;
1521 $this->description = $description;
1522 $this->defaultsetting = $defaultsetting;
1526 * Set up $this->name and potentially $this->plugin
1528 * Set up $this->name and possibly $this->plugin based on whether $name looks
1529 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1530 * on the names, that is, output a developer debug warning if the name
1531 * contains anything other than [a-zA-Z0-9_]+.
1533 * @param string $name the setting name passed in to the constructor.
1535 private function parse_setting_name($name) {
1536 $bits = explode('/', $name);
1537 if (count($bits) > 2) {
1538 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1540 $this->name = array_pop($bits);
1541 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1542 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1544 if (!empty($bits)) {
1545 $this->plugin = array_pop($bits);
1546 if ($this->plugin === 'moodle') {
1547 $this->plugin = null;
1548 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1549 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1555 * Returns the fullname prefixed by the plugin
1556 * @return string
1558 public function get_full_name() {
1559 return 's_'.$this->plugin.'_'.$this->name;
1563 * Returns the ID string based on plugin and name
1564 * @return string
1566 public function get_id() {
1567 return 'id_s_'.$this->plugin.'_'.$this->name;
1571 * @param bool $affectsmodinfo If true, changes to this setting will
1572 * cause the course cache to be rebuilt
1574 public function set_affects_modinfo($affectsmodinfo) {
1575 $this->affectsmodinfo = $affectsmodinfo;
1579 * Returns the config if possible
1581 * @return mixed returns config if successful else null
1583 public function config_read($name) {
1584 global $CFG;
1585 if (!empty($this->plugin)) {
1586 $value = get_config($this->plugin, $name);
1587 return $value === false ? NULL : $value;
1589 } else {
1590 if (isset($CFG->$name)) {
1591 return $CFG->$name;
1592 } else {
1593 return NULL;
1599 * Used to set a config pair and log change
1601 * @param string $name
1602 * @param mixed $value Gets converted to string if not null
1603 * @return bool Write setting to config table
1605 public function config_write($name, $value) {
1606 global $DB, $USER, $CFG;
1608 if ($this->nosave) {
1609 return true;
1612 // make sure it is a real change
1613 $oldvalue = get_config($this->plugin, $name);
1614 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1615 $value = is_null($value) ? null : (string)$value;
1617 if ($oldvalue === $value) {
1618 return true;
1621 // store change
1622 set_config($name, $value, $this->plugin);
1624 // Some admin settings affect course modinfo
1625 if ($this->affectsmodinfo) {
1626 // Clear course cache for all courses
1627 rebuild_course_cache(0, true);
1630 // log change
1631 $log = new stdClass();
1632 $log->userid = during_initial_install() ? 0 :$USER->id; // 0 as user id during install
1633 $log->timemodified = time();
1634 $log->plugin = $this->plugin;
1635 $log->name = $name;
1636 $log->value = $value;
1637 $log->oldvalue = $oldvalue;
1638 $DB->insert_record('config_log', $log);
1640 return true; // BC only
1644 * Returns current value of this setting
1645 * @return mixed array or string depending on instance, NULL means not set yet
1647 public abstract function get_setting();
1650 * Returns default setting if exists
1651 * @return mixed array or string depending on instance; NULL means no default, user must supply
1653 public function get_defaultsetting() {
1654 $adminroot = admin_get_root(false, false);
1655 if (!empty($adminroot->custom_defaults)) {
1656 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1657 if (isset($adminroot->custom_defaults[$plugin])) {
1658 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1659 return $adminroot->custom_defaults[$plugin][$this->name];
1663 return $this->defaultsetting;
1667 * Store new setting
1669 * @param mixed $data string or array, must not be NULL
1670 * @return string empty string if ok, string error message otherwise
1672 public abstract function write_setting($data);
1675 * Return part of form with setting
1676 * This function should always be overwritten
1678 * @param mixed $data array or string depending on setting
1679 * @param string $query
1680 * @return string
1682 public function output_html($data, $query='') {
1683 // should be overridden
1684 return;
1688 * Function called if setting updated - cleanup, cache reset, etc.
1689 * @param string $functionname Sets the function name
1690 * @return void
1692 public function set_updatedcallback($functionname) {
1693 $this->updatedcallback = $functionname;
1697 * Execute postupdatecallback if necessary.
1698 * @param mixed $original original value before write_setting()
1699 * @return bool true if changed, false if not.
1701 public function post_write_settings($original) {
1702 // Comparison must work for arrays too.
1703 if (serialize($original) === serialize($this->get_setting())) {
1704 return false;
1707 $callbackfunction = $this->updatedcallback;
1708 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
1709 $callbackfunction($this->get_full_name());
1711 return true;
1715 * Is setting related to query text - used when searching
1716 * @param string $query
1717 * @return bool
1719 public function is_related($query) {
1720 if (strpos(strtolower($this->name), $query) !== false) {
1721 return true;
1723 if (strpos(textlib::strtolower($this->visiblename), $query) !== false) {
1724 return true;
1726 if (strpos(textlib::strtolower($this->description), $query) !== false) {
1727 return true;
1729 $current = $this->get_setting();
1730 if (!is_null($current)) {
1731 if (is_string($current)) {
1732 if (strpos(textlib::strtolower($current), $query) !== false) {
1733 return true;
1737 $default = $this->get_defaultsetting();
1738 if (!is_null($default)) {
1739 if (is_string($default)) {
1740 if (strpos(textlib::strtolower($default), $query) !== false) {
1741 return true;
1745 return false;
1751 * No setting - just heading and text.
1753 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1755 class admin_setting_heading extends admin_setting {
1758 * not a setting, just text
1759 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1760 * @param string $heading heading
1761 * @param string $information text in box
1763 public function __construct($name, $heading, $information) {
1764 $this->nosave = true;
1765 parent::__construct($name, $heading, $information, '');
1769 * Always returns true
1770 * @return bool Always returns true
1772 public function get_setting() {
1773 return true;
1777 * Always returns true
1778 * @return bool Always returns true
1780 public function get_defaultsetting() {
1781 return true;
1785 * Never write settings
1786 * @return string Always returns an empty string
1788 public function write_setting($data) {
1789 // do not write any setting
1790 return '';
1794 * Returns an HTML string
1795 * @return string Returns an HTML string
1797 public function output_html($data, $query='') {
1798 global $OUTPUT;
1799 $return = '';
1800 if ($this->visiblename != '') {
1801 $return .= $OUTPUT->heading($this->visiblename, 3, 'main');
1803 if ($this->description != '') {
1804 $return .= $OUTPUT->box(highlight($query, markdown_to_html($this->description)), 'generalbox formsettingheading');
1806 return $return;
1812 * The most flexibly setting, user is typing text
1814 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1816 class admin_setting_configtext extends admin_setting {
1818 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
1819 public $paramtype;
1820 /** @var int default field size */
1821 public $size;
1824 * Config text constructor
1826 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1827 * @param string $visiblename localised
1828 * @param string $description long localised info
1829 * @param string $defaultsetting
1830 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
1831 * @param int $size default field size
1833 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
1834 $this->paramtype = $paramtype;
1835 if (!is_null($size)) {
1836 $this->size = $size;
1837 } else {
1838 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
1840 parent::__construct($name, $visiblename, $description, $defaultsetting);
1844 * Return the setting
1846 * @return mixed returns config if successful else null
1848 public function get_setting() {
1849 return $this->config_read($this->name);
1852 public function write_setting($data) {
1853 if ($this->paramtype === PARAM_INT and $data === '') {
1854 // do not complain if '' used instead of 0
1855 $data = 0;
1857 // $data is a string
1858 $validated = $this->validate($data);
1859 if ($validated !== true) {
1860 return $validated;
1862 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
1866 * Validate data before storage
1867 * @param string data
1868 * @return mixed true if ok string if error found
1870 public function validate($data) {
1871 // allow paramtype to be a custom regex if it is the form of /pattern/
1872 if (preg_match('#^/.*/$#', $this->paramtype)) {
1873 if (preg_match($this->paramtype, $data)) {
1874 return true;
1875 } else {
1876 return get_string('validateerror', 'admin');
1879 } else if ($this->paramtype === PARAM_RAW) {
1880 return true;
1882 } else {
1883 $cleaned = clean_param($data, $this->paramtype);
1884 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
1885 return true;
1886 } else {
1887 return get_string('validateerror', 'admin');
1893 * Return an XHTML string for the setting
1894 * @return string Returns an XHTML string
1896 public function output_html($data, $query='') {
1897 $default = $this->get_defaultsetting();
1899 return format_admin_setting($this, $this->visiblename,
1900 '<div class="form-text defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" /></div>',
1901 $this->description, true, '', $default, $query);
1907 * General text area without html editor.
1909 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1911 class admin_setting_configtextarea extends admin_setting_configtext {
1912 private $rows;
1913 private $cols;
1916 * @param string $name
1917 * @param string $visiblename
1918 * @param string $description
1919 * @param mixed $defaultsetting string or array
1920 * @param mixed $paramtype
1921 * @param string $cols The number of columns to make the editor
1922 * @param string $rows The number of rows to make the editor
1924 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
1925 $this->rows = $rows;
1926 $this->cols = $cols;
1927 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
1931 * Returns an XHTML string for the editor
1933 * @param string $data
1934 * @param string $query
1935 * @return string XHTML string for the editor
1937 public function output_html($data, $query='') {
1938 $default = $this->get_defaultsetting();
1940 $defaultinfo = $default;
1941 if (!is_null($default) and $default !== '') {
1942 $defaultinfo = "\n".$default;
1945 return format_admin_setting($this, $this->visiblename,
1946 '<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>',
1947 $this->description, true, '', $defaultinfo, $query);
1953 * General text area with html editor.
1955 class admin_setting_confightmleditor extends admin_setting_configtext {
1956 private $rows;
1957 private $cols;
1960 * @param string $name
1961 * @param string $visiblename
1962 * @param string $description
1963 * @param mixed $defaultsetting string or array
1964 * @param mixed $paramtype
1966 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
1967 $this->rows = $rows;
1968 $this->cols = $cols;
1969 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
1970 editors_head_setup();
1974 * Returns an XHTML string for the editor
1976 * @param string $data
1977 * @param string $query
1978 * @return string XHTML string for the editor
1980 public function output_html($data, $query='') {
1981 $default = $this->get_defaultsetting();
1983 $defaultinfo = $default;
1984 if (!is_null($default) and $default !== '') {
1985 $defaultinfo = "\n".$default;
1988 $editor = editors_get_preferred_editor(FORMAT_HTML);
1989 $editor->use_editor($this->get_id(), array('noclean'=>true));
1991 return format_admin_setting($this, $this->visiblename,
1992 '<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>',
1993 $this->description, true, '', $defaultinfo, $query);
1999 * Password field, allows unmasking of password
2001 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2003 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2005 * Constructor
2006 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2007 * @param string $visiblename localised
2008 * @param string $description long localised info
2009 * @param string $defaultsetting default password
2011 public function __construct($name, $visiblename, $description, $defaultsetting) {
2012 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2016 * Returns XHTML for the field
2017 * Writes Javascript into the HTML below right before the last div
2019 * @todo Make javascript available through newer methods if possible
2020 * @param string $data Value for the field
2021 * @param string $query Passed as final argument for format_admin_setting
2022 * @return string XHTML field
2024 public function output_html($data, $query='') {
2025 $id = $this->get_id();
2026 $unmask = get_string('unmaskpassword', 'form');
2027 $unmaskjs = '<script type="text/javascript">
2028 //<![CDATA[
2029 var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
2031 document.getElementById("'.$id.'").setAttribute("autocomplete", "off");
2033 var unmaskdiv = document.getElementById("'.$id.'unmaskdiv");
2035 var unmaskchb = document.createElement("input");
2036 unmaskchb.setAttribute("type", "checkbox");
2037 unmaskchb.setAttribute("id", "'.$id.'unmask");
2038 unmaskchb.onchange = function() {unmaskPassword("'.$id.'");};
2039 unmaskdiv.appendChild(unmaskchb);
2041 var unmasklbl = document.createElement("label");
2042 unmasklbl.innerHTML = "'.addslashes_js($unmask).'";
2043 if (is_ie) {
2044 unmasklbl.setAttribute("htmlFor", "'.$id.'unmask");
2045 } else {
2046 unmasklbl.setAttribute("for", "'.$id.'unmask");
2048 unmaskdiv.appendChild(unmasklbl);
2050 if (is_ie) {
2051 // ugly hack to work around the famous onchange IE bug
2052 unmaskchb.onclick = function() {this.blur();};
2053 unmaskdiv.onclick = function() {this.blur();};
2055 //]]>
2056 </script>';
2057 return format_admin_setting($this, $this->visiblename,
2058 '<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>',
2059 $this->description, true, '', NULL, $query);
2065 * Path to directory
2067 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2069 class admin_setting_configfile extends admin_setting_configtext {
2071 * Constructor
2072 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2073 * @param string $visiblename localised
2074 * @param string $description long localised info
2075 * @param string $defaultdirectory default directory location
2077 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2078 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2082 * Returns XHTML for the field
2084 * Returns XHTML for the field and also checks whether the file
2085 * specified in $data exists using file_exists()
2087 * @param string $data File name and path to use in value attr
2088 * @param string $query
2089 * @return string XHTML field
2091 public function output_html($data, $query='') {
2092 $default = $this->get_defaultsetting();
2094 if ($data) {
2095 if (file_exists($data)) {
2096 $executable = '<span class="pathok">&#x2714;</span>';
2097 } else {
2098 $executable = '<span class="patherror">&#x2718;</span>';
2100 } else {
2101 $executable = '';
2104 return format_admin_setting($this, $this->visiblename,
2105 '<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>',
2106 $this->description, true, '', $default, $query);
2109 * checks if execpatch has been disabled in config.php
2111 public function write_setting($data) {
2112 global $CFG;
2113 if (!empty($CFG->preventexecpath)) {
2114 return '';
2116 return parent::write_setting($data);
2122 * Path to executable file
2124 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2126 class admin_setting_configexecutable extends admin_setting_configfile {
2129 * Returns an XHTML field
2131 * @param string $data This is the value for the field
2132 * @param string $query
2133 * @return string XHTML field
2135 public function output_html($data, $query='') {
2136 global $CFG;
2137 $default = $this->get_defaultsetting();
2139 if ($data) {
2140 if (file_exists($data) and is_executable($data)) {
2141 $executable = '<span class="pathok">&#x2714;</span>';
2142 } else {
2143 $executable = '<span class="patherror">&#x2718;</span>';
2145 } else {
2146 $executable = '';
2148 if (!empty($CFG->preventexecpath)) {
2149 $this->visiblename .= '<div class="form-overridden">'.get_string('execpathnotallowed', 'admin').'</div>';
2152 return format_admin_setting($this, $this->visiblename,
2153 '<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>',
2154 $this->description, true, '', $default, $query);
2160 * Path to directory
2162 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2164 class admin_setting_configdirectory extends admin_setting_configfile {
2167 * Returns an XHTML field
2169 * @param string $data This is the value for the field
2170 * @param string $query
2171 * @return string XHTML
2173 public function output_html($data, $query='') {
2174 $default = $this->get_defaultsetting();
2176 if ($data) {
2177 if (file_exists($data) and is_dir($data)) {
2178 $executable = '<span class="pathok">&#x2714;</span>';
2179 } else {
2180 $executable = '<span class="patherror">&#x2718;</span>';
2182 } else {
2183 $executable = '';
2186 return format_admin_setting($this, $this->visiblename,
2187 '<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>',
2188 $this->description, true, '', $default, $query);
2194 * Checkbox
2196 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2198 class admin_setting_configcheckbox extends admin_setting {
2199 /** @var string Value used when checked */
2200 public $yes;
2201 /** @var string Value used when not checked */
2202 public $no;
2205 * Constructor
2206 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2207 * @param string $visiblename localised
2208 * @param string $description long localised info
2209 * @param string $defaultsetting
2210 * @param string $yes value used when checked
2211 * @param string $no value used when not checked
2213 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2214 parent::__construct($name, $visiblename, $description, $defaultsetting);
2215 $this->yes = (string)$yes;
2216 $this->no = (string)$no;
2220 * Retrieves the current setting using the objects name
2222 * @return string
2224 public function get_setting() {
2225 return $this->config_read($this->name);
2229 * Sets the value for the setting
2231 * Sets the value for the setting to either the yes or no values
2232 * of the object by comparing $data to yes
2234 * @param mixed $data Gets converted to str for comparison against yes value
2235 * @return string empty string or error
2237 public function write_setting($data) {
2238 if ((string)$data === $this->yes) { // convert to strings before comparison
2239 $data = $this->yes;
2240 } else {
2241 $data = $this->no;
2243 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2247 * Returns an XHTML checkbox field
2249 * @param string $data If $data matches yes then checkbox is checked
2250 * @param string $query
2251 * @return string XHTML field
2253 public function output_html($data, $query='') {
2254 $default = $this->get_defaultsetting();
2256 if (!is_null($default)) {
2257 if ((string)$default === $this->yes) {
2258 $defaultinfo = get_string('checkboxyes', 'admin');
2259 } else {
2260 $defaultinfo = get_string('checkboxno', 'admin');
2262 } else {
2263 $defaultinfo = NULL;
2266 if ((string)$data === $this->yes) { // convert to strings before comparison
2267 $checked = 'checked="checked"';
2268 } else {
2269 $checked = '';
2272 return format_admin_setting($this, $this->visiblename,
2273 '<div class="form-checkbox defaultsnext" ><input type="hidden" name="'.$this->get_full_name().'" value="'.s($this->no).'" /> '
2274 .'<input type="checkbox" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($this->yes).'" '.$checked.' /></div>',
2275 $this->description, true, '', $defaultinfo, $query);
2281 * Multiple checkboxes, each represents different value, stored in csv format
2283 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2285 class admin_setting_configmulticheckbox extends admin_setting {
2286 /** @var array Array of choices value=>label */
2287 public $choices;
2290 * Constructor: uses parent::__construct
2292 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2293 * @param string $visiblename localised
2294 * @param string $description long localised info
2295 * @param array $defaultsetting array of selected
2296 * @param array $choices array of $value=>$label for each checkbox
2298 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2299 $this->choices = $choices;
2300 parent::__construct($name, $visiblename, $description, $defaultsetting);
2304 * This public function may be used in ancestors for lazy loading of choices
2306 * @todo Check if this function is still required content commented out only returns true
2307 * @return bool true if loaded, false if error
2309 public function load_choices() {
2311 if (is_array($this->choices)) {
2312 return true;
2314 .... load choices here
2316 return true;
2320 * Is setting related to query text - used when searching
2322 * @param string $query
2323 * @return bool true on related, false on not or failure
2325 public function is_related($query) {
2326 if (!$this->load_choices() or empty($this->choices)) {
2327 return false;
2329 if (parent::is_related($query)) {
2330 return true;
2333 foreach ($this->choices as $desc) {
2334 if (strpos(textlib::strtolower($desc), $query) !== false) {
2335 return true;
2338 return false;
2342 * Returns the current setting if it is set
2344 * @return mixed null if null, else an array
2346 public function get_setting() {
2347 $result = $this->config_read($this->name);
2349 if (is_null($result)) {
2350 return NULL;
2352 if ($result === '') {
2353 return array();
2355 $enabled = explode(',', $result);
2356 $setting = array();
2357 foreach ($enabled as $option) {
2358 $setting[$option] = 1;
2360 return $setting;
2364 * Saves the setting(s) provided in $data
2366 * @param array $data An array of data, if not array returns empty str
2367 * @return mixed empty string on useless data or bool true=success, false=failed
2369 public function write_setting($data) {
2370 if (!is_array($data)) {
2371 return ''; // ignore it
2373 if (!$this->load_choices() or empty($this->choices)) {
2374 return '';
2376 unset($data['xxxxx']);
2377 $result = array();
2378 foreach ($data as $key => $value) {
2379 if ($value and array_key_exists($key, $this->choices)) {
2380 $result[] = $key;
2383 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
2387 * Returns XHTML field(s) as required by choices
2389 * Relies on data being an array should data ever be another valid vartype with
2390 * acceptable value this may cause a warning/error
2391 * if (!is_array($data)) would fix the problem
2393 * @todo Add vartype handling to ensure $data is an array
2395 * @param array $data An array of checked values
2396 * @param string $query
2397 * @return string XHTML field
2399 public function output_html($data, $query='') {
2400 if (!$this->load_choices() or empty($this->choices)) {
2401 return '';
2403 $default = $this->get_defaultsetting();
2404 if (is_null($default)) {
2405 $default = array();
2407 if (is_null($data)) {
2408 $data = array();
2410 $options = array();
2411 $defaults = array();
2412 foreach ($this->choices as $key=>$description) {
2413 if (!empty($data[$key])) {
2414 $checked = 'checked="checked"';
2415 } else {
2416 $checked = '';
2418 if (!empty($default[$key])) {
2419 $defaults[] = $description;
2422 $options[] = '<input type="checkbox" id="'.$this->get_id().'_'.$key.'" name="'.$this->get_full_name().'['.$key.']" value="1" '.$checked.' />'
2423 .'<label for="'.$this->get_id().'_'.$key.'">'.highlightfast($query, $description).'</label>';
2426 if (is_null($default)) {
2427 $defaultinfo = NULL;
2428 } else if (!empty($defaults)) {
2429 $defaultinfo = implode(', ', $defaults);
2430 } else {
2431 $defaultinfo = get_string('none');
2434 $return = '<div class="form-multicheckbox">';
2435 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2436 if ($options) {
2437 $return .= '<ul>';
2438 foreach ($options as $option) {
2439 $return .= '<li>'.$option.'</li>';
2441 $return .= '</ul>';
2443 $return .= '</div>';
2445 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
2452 * Multiple checkboxes 2, value stored as string 00101011
2454 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2456 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
2459 * Returns the setting if set
2461 * @return mixed null if not set, else an array of set settings
2463 public function get_setting() {
2464 $result = $this->config_read($this->name);
2465 if (is_null($result)) {
2466 return NULL;
2468 if (!$this->load_choices()) {
2469 return NULL;
2471 $result = str_pad($result, count($this->choices), '0');
2472 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
2473 $setting = array();
2474 foreach ($this->choices as $key=>$unused) {
2475 $value = array_shift($result);
2476 if ($value) {
2477 $setting[$key] = 1;
2480 return $setting;
2484 * Save setting(s) provided in $data param
2486 * @param array $data An array of settings to save
2487 * @return mixed empty string for bad data or bool true=>success, false=>error
2489 public function write_setting($data) {
2490 if (!is_array($data)) {
2491 return ''; // ignore it
2493 if (!$this->load_choices() or empty($this->choices)) {
2494 return '';
2496 $result = '';
2497 foreach ($this->choices as $key=>$unused) {
2498 if (!empty($data[$key])) {
2499 $result .= '1';
2500 } else {
2501 $result .= '0';
2504 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
2510 * Select one value from list
2512 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2514 class admin_setting_configselect extends admin_setting {
2515 /** @var array Array of choices value=>label */
2516 public $choices;
2519 * Constructor
2520 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2521 * @param string $visiblename localised
2522 * @param string $description long localised info
2523 * @param string|int $defaultsetting
2524 * @param array $choices array of $value=>$label for each selection
2526 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2527 $this->choices = $choices;
2528 parent::__construct($name, $visiblename, $description, $defaultsetting);
2532 * This function may be used in ancestors for lazy loading of choices
2534 * Override this method if loading of choices is expensive, such
2535 * as when it requires multiple db requests.
2537 * @return bool true if loaded, false if error
2539 public function load_choices() {
2541 if (is_array($this->choices)) {
2542 return true;
2544 .... load choices here
2546 return true;
2550 * Check if this is $query is related to a choice
2552 * @param string $query
2553 * @return bool true if related, false if not
2555 public function is_related($query) {
2556 if (parent::is_related($query)) {
2557 return true;
2559 if (!$this->load_choices()) {
2560 return false;
2562 foreach ($this->choices as $key=>$value) {
2563 if (strpos(textlib::strtolower($key), $query) !== false) {
2564 return true;
2566 if (strpos(textlib::strtolower($value), $query) !== false) {
2567 return true;
2570 return false;
2574 * Return the setting
2576 * @return mixed returns config if successful else null
2578 public function get_setting() {
2579 return $this->config_read($this->name);
2583 * Save a setting
2585 * @param string $data
2586 * @return string empty of error string
2588 public function write_setting($data) {
2589 if (!$this->load_choices() or empty($this->choices)) {
2590 return '';
2592 if (!array_key_exists($data, $this->choices)) {
2593 return ''; // ignore it
2596 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2600 * Returns XHTML select field
2602 * Ensure the options are loaded, and generate the XHTML for the select
2603 * element and any warning message. Separating this out from output_html
2604 * makes it easier to subclass this class.
2606 * @param string $data the option to show as selected.
2607 * @param string $current the currently selected option in the database, null if none.
2608 * @param string $default the default selected option.
2609 * @return array the HTML for the select element, and a warning message.
2611 public function output_select_html($data, $current, $default, $extraname = '') {
2612 if (!$this->load_choices() or empty($this->choices)) {
2613 return array('', '');
2616 $warning = '';
2617 if (is_null($current)) {
2618 // first run
2619 } else if (empty($current) and (array_key_exists('', $this->choices) or array_key_exists(0, $this->choices))) {
2620 // no warning
2621 } else if (!array_key_exists($current, $this->choices)) {
2622 $warning = get_string('warningcurrentsetting', 'admin', s($current));
2623 if (!is_null($default) and $data == $current) {
2624 $data = $default; // use default instead of first value when showing the form
2628 $selecthtml = '<select id="'.$this->get_id().'" name="'.$this->get_full_name().$extraname.'">';
2629 foreach ($this->choices as $key => $value) {
2630 // the string cast is needed because key may be integer - 0 is equal to most strings!
2631 $selecthtml .= '<option value="'.$key.'"'.((string)$key==$data ? ' selected="selected"' : '').'>'.$value.'</option>';
2633 $selecthtml .= '</select>';
2634 return array($selecthtml, $warning);
2638 * Returns XHTML select field and wrapping div(s)
2640 * @see output_select_html()
2642 * @param string $data the option to show as selected
2643 * @param string $query
2644 * @return string XHTML field and wrapping div
2646 public function output_html($data, $query='') {
2647 $default = $this->get_defaultsetting();
2648 $current = $this->get_setting();
2650 list($selecthtml, $warning) = $this->output_select_html($data, $current, $default);
2651 if (!$selecthtml) {
2652 return '';
2655 if (!is_null($default) and array_key_exists($default, $this->choices)) {
2656 $defaultinfo = $this->choices[$default];
2657 } else {
2658 $defaultinfo = NULL;
2661 $return = '<div class="form-select defaultsnext">' . $selecthtml . '</div>';
2663 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
2669 * Select multiple items from list
2671 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2673 class admin_setting_configmultiselect extends admin_setting_configselect {
2675 * Constructor
2676 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2677 * @param string $visiblename localised
2678 * @param string $description long localised info
2679 * @param array $defaultsetting array of selected items
2680 * @param array $choices array of $value=>$label for each list item
2682 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2683 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
2687 * Returns the select setting(s)
2689 * @return mixed null or array. Null if no settings else array of setting(s)
2691 public function get_setting() {
2692 $result = $this->config_read($this->name);
2693 if (is_null($result)) {
2694 return NULL;
2696 if ($result === '') {
2697 return array();
2699 return explode(',', $result);
2703 * Saves setting(s) provided through $data
2705 * Potential bug in the works should anyone call with this function
2706 * using a vartype that is not an array
2708 * @param array $data
2710 public function write_setting($data) {
2711 if (!is_array($data)) {
2712 return ''; //ignore it
2714 if (!$this->load_choices() or empty($this->choices)) {
2715 return '';
2718 unset($data['xxxxx']);
2720 $save = array();
2721 foreach ($data as $value) {
2722 if (!array_key_exists($value, $this->choices)) {
2723 continue; // ignore it
2725 $save[] = $value;
2728 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
2732 * Is setting related to query text - used when searching
2734 * @param string $query
2735 * @return bool true if related, false if not
2737 public function is_related($query) {
2738 if (!$this->load_choices() or empty($this->choices)) {
2739 return false;
2741 if (parent::is_related($query)) {
2742 return true;
2745 foreach ($this->choices as $desc) {
2746 if (strpos(textlib::strtolower($desc), $query) !== false) {
2747 return true;
2750 return false;
2754 * Returns XHTML multi-select field
2756 * @todo Add vartype handling to ensure $data is an array
2757 * @param array $data Array of values to select by default
2758 * @param string $query
2759 * @return string XHTML multi-select field
2761 public function output_html($data, $query='') {
2762 if (!$this->load_choices() or empty($this->choices)) {
2763 return '';
2765 $choices = $this->choices;
2766 $default = $this->get_defaultsetting();
2767 if (is_null($default)) {
2768 $default = array();
2770 if (is_null($data)) {
2771 $data = array();
2774 $defaults = array();
2775 $size = min(10, count($this->choices));
2776 $return = '<div class="form-select"><input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2777 $return .= '<select id="'.$this->get_id().'" name="'.$this->get_full_name().'[]" size="'.$size.'" multiple="multiple">';
2778 foreach ($this->choices as $key => $description) {
2779 if (in_array($key, $data)) {
2780 $selected = 'selected="selected"';
2781 } else {
2782 $selected = '';
2784 if (in_array($key, $default)) {
2785 $defaults[] = $description;
2788 $return .= '<option value="'.s($key).'" '.$selected.'>'.$description.'</option>';
2791 if (is_null($default)) {
2792 $defaultinfo = NULL;
2793 } if (!empty($defaults)) {
2794 $defaultinfo = implode(', ', $defaults);
2795 } else {
2796 $defaultinfo = get_string('none');
2799 $return .= '</select></div>';
2800 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
2805 * Time selector
2807 * This is a liiitle bit messy. we're using two selects, but we're returning
2808 * them as an array named after $name (so we only use $name2 internally for the setting)
2810 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2812 class admin_setting_configtime extends admin_setting {
2813 /** @var string Used for setting second select (minutes) */
2814 public $name2;
2817 * Constructor
2818 * @param string $hoursname setting for hours
2819 * @param string $minutesname setting for hours
2820 * @param string $visiblename localised
2821 * @param string $description long localised info
2822 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
2824 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
2825 $this->name2 = $minutesname;
2826 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
2830 * Get the selected time
2832 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
2834 public function get_setting() {
2835 $result1 = $this->config_read($this->name);
2836 $result2 = $this->config_read($this->name2);
2837 if (is_null($result1) or is_null($result2)) {
2838 return NULL;
2841 return array('h' => $result1, 'm' => $result2);
2845 * Store the time (hours and minutes)
2847 * @param array $data Must be form 'h'=>xx, 'm'=>xx
2848 * @return bool true if success, false if not
2850 public function write_setting($data) {
2851 if (!is_array($data)) {
2852 return '';
2855 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
2856 return ($result ? '' : get_string('errorsetting', 'admin'));
2860 * Returns XHTML time select fields
2862 * @param array $data Must be form 'h'=>xx, 'm'=>xx
2863 * @param string $query
2864 * @return string XHTML time select fields and wrapping div(s)
2866 public function output_html($data, $query='') {
2867 $default = $this->get_defaultsetting();
2869 if (is_array($default)) {
2870 $defaultinfo = $default['h'].':'.$default['m'];
2871 } else {
2872 $defaultinfo = NULL;
2875 $return = '<div class="form-time defaultsnext">'.
2876 '<select id="'.$this->get_id().'h" name="'.$this->get_full_name().'[h]">';
2877 for ($i = 0; $i < 24; $i++) {
2878 $return .= '<option value="'.$i.'"'.($i == $data['h'] ? ' selected="selected"' : '').'>'.$i.'</option>';
2880 $return .= '</select>:<select id="'.$this->get_id().'m" name="'.$this->get_full_name().'[m]">';
2881 for ($i = 0; $i < 60; $i += 5) {
2882 $return .= '<option value="'.$i.'"'.($i == $data['m'] ? ' selected="selected"' : '').'>'.$i.'</option>';
2884 $return .= '</select></div>';
2885 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
2892 * Seconds duration setting.
2894 * @copyright 2012 Petr Skoda (http://skodak.org)
2895 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2897 class admin_setting_configduration extends admin_setting {
2899 /** @var int default duration unit */
2900 protected $defaultunit;
2903 * Constructor
2904 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2905 * or 'myplugin/mysetting' for ones in config_plugins.
2906 * @param string $visiblename localised name
2907 * @param string $description localised long description
2908 * @param mixed $defaultsetting string or array depending on implementation
2909 * @param int $defaultunit - day, week, etc. (in seconds)
2911 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
2912 if (is_number($defaultsetting)) {
2913 $defaultsetting = self::parse_seconds($defaultsetting);
2915 $units = self::get_units();
2916 if (isset($units[$defaultunit])) {
2917 $this->defaultunit = $defaultunit;
2918 } else {
2919 $this->defaultunit = 86400;
2921 parent::__construct($name, $visiblename, $description, $defaultsetting);
2925 * Returns selectable units.
2926 * @static
2927 * @return array
2929 protected static function get_units() {
2930 return array(
2931 604800 => get_string('weeks'),
2932 86400 => get_string('days'),
2933 3600 => get_string('hours'),
2934 60 => get_string('minutes'),
2935 1 => get_string('seconds'),
2940 * Converts seconds to some more user friendly string.
2941 * @static
2942 * @param int $seconds
2943 * @return string
2945 protected static function get_duration_text($seconds) {
2946 if (empty($seconds)) {
2947 return get_string('none');
2949 $data = self::parse_seconds($seconds);
2950 switch ($data['u']) {
2951 case (60*60*24*7):
2952 return get_string('numweeks', '', $data['v']);
2953 case (60*60*24):
2954 return get_string('numdays', '', $data['v']);
2955 case (60*60):
2956 return get_string('numhours', '', $data['v']);
2957 case (60):
2958 return get_string('numminutes', '', $data['v']);
2959 default:
2960 return get_string('numseconds', '', $data['v']*$data['u']);
2965 * Finds suitable units for given duration.
2966 * @static
2967 * @param int $seconds
2968 * @return array
2970 protected static function parse_seconds($seconds) {
2971 foreach (self::get_units() as $unit => $unused) {
2972 if ($seconds % $unit === 0) {
2973 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
2976 return array('v'=>(int)$seconds, 'u'=>1);
2980 * Get the selected duration as array.
2982 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
2984 public function get_setting() {
2985 $seconds = $this->config_read($this->name);
2986 if (is_null($seconds)) {
2987 return null;
2990 return self::parse_seconds($seconds);
2994 * Store the duration as seconds.
2996 * @param array $data Must be form 'h'=>xx, 'm'=>xx
2997 * @return bool true if success, false if not
2999 public function write_setting($data) {
3000 if (!is_array($data)) {
3001 return '';
3004 $seconds = (int)($data['v']*$data['u']);
3005 if ($seconds < 0) {
3006 return get_string('errorsetting', 'admin');
3009 $result = $this->config_write($this->name, $seconds);
3010 return ($result ? '' : get_string('errorsetting', 'admin'));
3014 * Returns duration text+select fields.
3016 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3017 * @param string $query
3018 * @return string duration text+select fields and wrapping div(s)
3020 public function output_html($data, $query='') {
3021 $default = $this->get_defaultsetting();
3023 if (is_number($default)) {
3024 $defaultinfo = self::get_duration_text($default);
3025 } else if (is_array($default)) {
3026 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3027 } else {
3028 $defaultinfo = null;
3031 $units = self::get_units();
3033 $return = '<div class="form-duration defaultsnext">';
3034 $return .= '<input type="text" size="5" id="'.$this->get_id().'v" name="'.$this->get_full_name().'[v]" value="'.s($data['v']).'" />';
3035 $return .= '<select id="'.$this->get_id().'u" name="'.$this->get_full_name().'[u]">';
3036 foreach ($units as $val => $text) {
3037 $selected = '';
3038 if ($data['v'] == 0) {
3039 if ($val == $this->defaultunit) {
3040 $selected = ' selected="selected"';
3042 } else if ($val == $data['u']) {
3043 $selected = ' selected="selected"';
3045 $return .= '<option value="'.$val.'"'.$selected.'>'.$text.'</option>';
3047 $return .= '</select></div>';
3048 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
3054 * Used to validate a textarea used for ip addresses
3056 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3058 class admin_setting_configiplist extends admin_setting_configtextarea {
3061 * Validate the contents of the textarea as IP addresses
3063 * Used to validate a new line separated list of IP addresses collected from
3064 * a textarea control
3066 * @param string $data A list of IP Addresses separated by new lines
3067 * @return mixed bool true for success or string:error on failure
3069 public function validate($data) {
3070 if(!empty($data)) {
3071 $ips = explode("\n", $data);
3072 } else {
3073 return true;
3075 $result = true;
3076 foreach($ips as $ip) {
3077 $ip = trim($ip);
3078 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3079 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3080 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3081 $result = true;
3082 } else {
3083 $result = false;
3084 break;
3087 if($result) {
3088 return true;
3089 } else {
3090 return get_string('validateerror', 'admin');
3097 * An admin setting for selecting one or more users who have a capability
3098 * in the system context
3100 * An admin setting for selecting one or more users, who have a particular capability
3101 * in the system context. Warning, make sure the list will never be too long. There is
3102 * no paging or searching of this list.
3104 * To correctly get a list of users from this config setting, you need to call the
3105 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
3107 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3109 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
3110 /** @var string The capabilities name */
3111 protected $capability;
3112 /** @var int include admin users too */
3113 protected $includeadmins;
3116 * Constructor.
3118 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3119 * @param string $visiblename localised name
3120 * @param string $description localised long description
3121 * @param array $defaultsetting array of usernames
3122 * @param string $capability string capability name.
3123 * @param bool $includeadmins include administrators
3125 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
3126 $this->capability = $capability;
3127 $this->includeadmins = $includeadmins;
3128 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
3132 * Load all of the uses who have the capability into choice array
3134 * @return bool Always returns true
3136 function load_choices() {
3137 if (is_array($this->choices)) {
3138 return true;
3140 list($sort, $sortparams) = users_order_by_sql('u');
3141 if (!empty($sortparams)) {
3142 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
3143 'This is unexpected, and a problem because there is no way to pass these ' .
3144 'parameters to get_users_by_capability. See MDL-34657.');
3146 $users = get_users_by_capability(context_system::instance(),
3147 $this->capability, 'u.id,u.username,u.firstname,u.lastname', $sort);
3148 $this->choices = array(
3149 '$@NONE@$' => get_string('nobody'),
3150 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
3152 if ($this->includeadmins) {
3153 $admins = get_admins();
3154 foreach ($admins as $user) {
3155 $this->choices[$user->id] = fullname($user);
3158 if (is_array($users)) {
3159 foreach ($users as $user) {
3160 $this->choices[$user->id] = fullname($user);
3163 return true;
3167 * Returns the default setting for class
3169 * @return mixed Array, or string. Empty string if no default
3171 public function get_defaultsetting() {
3172 $this->load_choices();
3173 $defaultsetting = parent::get_defaultsetting();
3174 if (empty($defaultsetting)) {
3175 return array('$@NONE@$');
3176 } else if (array_key_exists($defaultsetting, $this->choices)) {
3177 return $defaultsetting;
3178 } else {
3179 return '';
3184 * Returns the current setting
3186 * @return mixed array or string
3188 public function get_setting() {
3189 $result = parent::get_setting();
3190 if ($result === null) {
3191 // this is necessary for settings upgrade
3192 return null;
3194 if (empty($result)) {
3195 $result = array('$@NONE@$');
3197 return $result;
3201 * Save the chosen setting provided as $data
3203 * @param array $data
3204 * @return mixed string or array
3206 public function write_setting($data) {
3207 // If all is selected, remove any explicit options.
3208 if (in_array('$@ALL@$', $data)) {
3209 $data = array('$@ALL@$');
3211 // None never needs to be written to the DB.
3212 if (in_array('$@NONE@$', $data)) {
3213 unset($data[array_search('$@NONE@$', $data)]);
3215 return parent::write_setting($data);
3221 * Special checkbox for calendar - resets SESSION vars.
3223 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3225 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
3227 * Calls the parent::__construct with default values
3229 * name => calendar_adminseesall
3230 * visiblename => get_string('adminseesall', 'admin')
3231 * description => get_string('helpadminseesall', 'admin')
3232 * defaultsetting => 0
3234 public function __construct() {
3235 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
3236 get_string('helpadminseesall', 'admin'), '0');
3240 * Stores the setting passed in $data
3242 * @param mixed gets converted to string for comparison
3243 * @return string empty string or error message
3245 public function write_setting($data) {
3246 global $SESSION;
3247 return parent::write_setting($data);
3252 * Special select for settings that are altered in setup.php and can not be altered on the fly
3254 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3256 class admin_setting_special_selectsetup extends admin_setting_configselect {
3258 * Reads the setting directly from the database
3260 * @return mixed
3262 public function get_setting() {
3263 // read directly from db!
3264 return get_config(NULL, $this->name);
3268 * Save the setting passed in $data
3270 * @param string $data The setting to save
3271 * @return string empty or error message
3273 public function write_setting($data) {
3274 global $CFG;
3275 // do not change active CFG setting!
3276 $current = $CFG->{$this->name};
3277 $result = parent::write_setting($data);
3278 $CFG->{$this->name} = $current;
3279 return $result;
3285 * Special select for frontpage - stores data in course table
3287 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3289 class admin_setting_sitesetselect extends admin_setting_configselect {
3291 * Returns the site name for the selected site
3293 * @see get_site()
3294 * @return string The site name of the selected site
3296 public function get_setting() {
3297 $site = course_get_format(get_site())->get_course();
3298 return $site->{$this->name};
3302 * Updates the database and save the setting
3304 * @param string data
3305 * @return string empty or error message
3307 public function write_setting($data) {
3308 global $DB, $SITE;
3309 if (!in_array($data, array_keys($this->choices))) {
3310 return get_string('errorsetting', 'admin');
3312 $record = new stdClass();
3313 $record->id = SITEID;
3314 $temp = $this->name;
3315 $record->$temp = $data;
3316 $record->timemodified = time();
3317 // update $SITE
3318 $SITE->{$this->name} = $data;
3319 course_get_format($SITE)->update_course_format_options($record);
3320 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
3326 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
3327 * block to hidden.
3329 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3331 class admin_setting_bloglevel extends admin_setting_configselect {
3333 * Updates the database and save the setting
3335 * @param string data
3336 * @return string empty or error message
3338 public function write_setting($data) {
3339 global $DB, $CFG;
3340 if ($data == 0) {
3341 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
3342 foreach ($blogblocks as $block) {
3343 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
3345 } else {
3346 // reenable all blocks only when switching from disabled blogs
3347 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
3348 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
3349 foreach ($blogblocks as $block) {
3350 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
3354 return parent::write_setting($data);
3360 * Special select - lists on the frontpage - hacky
3362 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3364 class admin_setting_courselist_frontpage extends admin_setting {
3365 /** @var array Array of choices value=>label */
3366 public $choices;
3369 * Construct override, requires one param
3371 * @param bool $loggedin Is the user logged in
3373 public function __construct($loggedin) {
3374 global $CFG;
3375 require_once($CFG->dirroot.'/course/lib.php');
3376 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
3377 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
3378 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
3379 $defaults = array(FRONTPAGEALLCOURSELIST);
3380 parent::__construct($name, $visiblename, $description, $defaults);
3384 * Loads the choices available
3386 * @return bool always returns true
3388 public function load_choices() {
3389 if (is_array($this->choices)) {
3390 return true;
3392 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
3393 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
3394 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
3395 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
3396 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
3397 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
3398 'none' => get_string('none'));
3399 if ($this->name === 'frontpage') {
3400 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
3402 return true;
3406 * Returns the selected settings
3408 * @param mixed array or setting or null
3410 public function get_setting() {
3411 $result = $this->config_read($this->name);
3412 if (is_null($result)) {
3413 return NULL;
3415 if ($result === '') {
3416 return array();
3418 return explode(',', $result);
3422 * Save the selected options
3424 * @param array $data
3425 * @return mixed empty string (data is not an array) or bool true=success false=failure
3427 public function write_setting($data) {
3428 if (!is_array($data)) {
3429 return '';
3431 $this->load_choices();
3432 $save = array();
3433 foreach($data as $datum) {
3434 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
3435 continue;
3437 $save[$datum] = $datum; // no duplicates
3439 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3443 * Return XHTML select field and wrapping div
3445 * @todo Add vartype handling to make sure $data is an array
3446 * @param array $data Array of elements to select by default
3447 * @return string XHTML select field and wrapping div
3449 public function output_html($data, $query='') {
3450 $this->load_choices();
3451 $currentsetting = array();
3452 foreach ($data as $key) {
3453 if ($key != 'none' and array_key_exists($key, $this->choices)) {
3454 $currentsetting[] = $key; // already selected first
3458 $return = '<div class="form-group">';
3459 for ($i = 0; $i < count($this->choices) - 1; $i++) {
3460 if (!array_key_exists($i, $currentsetting)) {
3461 $currentsetting[$i] = 'none'; //none
3463 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
3464 foreach ($this->choices as $key => $value) {
3465 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
3467 $return .= '</select>';
3468 if ($i !== count($this->choices) - 2) {
3469 $return .= '<br />';
3472 $return .= '</div>';
3474 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3480 * Special checkbox for frontpage - stores data in course table
3482 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3484 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
3486 * Returns the current sites name
3488 * @return string
3490 public function get_setting() {
3491 $site = course_get_format(get_site())->get_course();
3492 return $site->{$this->name};
3496 * Save the selected setting
3498 * @param string $data The selected site
3499 * @return string empty string or error message
3501 public function write_setting($data) {
3502 global $DB, $SITE;
3503 $record = new stdClass();
3504 $record->id = $SITE->id;
3505 $record->{$this->name} = ($data == '1' ? 1 : 0);
3506 $record->timemodified = time();
3507 // update $SITE
3508 $SITE->{$this->name} = $data;
3509 course_get_format($SITE)->update_course_format_options($record);
3510 $DB->update_record('course', $record);
3511 // There is something wrong in cache updates somewhere, let's reset everything.
3512 format_base::reset_course_cache();
3513 return '';
3518 * Special text for frontpage - stores data in course table.
3519 * Empty string means not set here. Manual setting is required.
3521 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3523 class admin_setting_sitesettext extends admin_setting_configtext {
3525 * Return the current setting
3527 * @return mixed string or null
3529 public function get_setting() {
3530 $site = course_get_format(get_site())->get_course();
3531 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
3535 * Validate the selected data
3537 * @param string $data The selected value to validate
3538 * @return mixed true or message string
3540 public function validate($data) {
3541 $cleaned = clean_param($data, PARAM_TEXT);
3542 if ($cleaned === '') {
3543 return get_string('required');
3545 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
3546 return true;
3547 } else {
3548 return get_string('validateerror', 'admin');
3553 * Save the selected setting
3555 * @param string $data The selected value
3556 * @return string empty or error message
3558 public function write_setting($data) {
3559 global $DB, $SITE;
3560 $data = trim($data);
3561 $validated = $this->validate($data);
3562 if ($validated !== true) {
3563 return $validated;
3566 $record = new stdClass();
3567 $record->id = $SITE->id;
3568 $record->{$this->name} = $data;
3569 $record->timemodified = time();
3570 // update $SITE
3571 $SITE->{$this->name} = $data;
3572 course_get_format($SITE)->update_course_format_options($record);
3573 $DB->update_record('course', $record);
3574 // There is something wrong in cache updates somewhere, let's reset everything.
3575 format_base::reset_course_cache();
3576 return '';
3582 * Special text editor for site description.
3584 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3586 class admin_setting_special_frontpagedesc extends admin_setting {
3588 * Calls parent::__construct with specific arguments
3590 public function __construct() {
3591 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
3592 editors_head_setup();
3596 * Return the current setting
3597 * @return string The current setting
3599 public function get_setting() {
3600 $site = course_get_format(get_site())->get_course();
3601 return $site->{$this->name};
3605 * Save the new setting
3607 * @param string $data The new value to save
3608 * @return string empty or error message
3610 public function write_setting($data) {
3611 global $DB, $SITE;
3612 $record = new stdClass();
3613 $record->id = $SITE->id;
3614 $record->{$this->name} = $data;
3615 $record->timemodified = time();
3616 $SITE->{$this->name} = $data;
3617 course_get_format($SITE)->update_course_format_options($record);
3618 $DB->update_record('course', $record);
3619 // There is something wrong in cache updates somewhere, let's reset everything.
3620 format_base::reset_course_cache();
3621 return '';
3625 * Returns XHTML for the field plus wrapping div
3627 * @param string $data The current value
3628 * @param string $query
3629 * @return string The XHTML output
3631 public function output_html($data, $query='') {
3632 global $CFG;
3634 $CFG->adminusehtmleditor = can_use_html_editor();
3635 $return = '<div class="form-htmlarea">'.print_textarea($CFG->adminusehtmleditor, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
3637 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
3643 * Administration interface for emoticon_manager settings.
3645 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3647 class admin_setting_emoticons extends admin_setting {
3650 * Calls parent::__construct with specific args
3652 public function __construct() {
3653 global $CFG;
3655 $manager = get_emoticon_manager();
3656 $defaults = $this->prepare_form_data($manager->default_emoticons());
3657 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
3661 * Return the current setting(s)
3663 * @return array Current settings array
3665 public function get_setting() {
3666 global $CFG;
3668 $manager = get_emoticon_manager();
3670 $config = $this->config_read($this->name);
3671 if (is_null($config)) {
3672 return null;
3675 $config = $manager->decode_stored_config($config);
3676 if (is_null($config)) {
3677 return null;
3680 return $this->prepare_form_data($config);
3684 * Save selected settings
3686 * @param array $data Array of settings to save
3687 * @return bool
3689 public function write_setting($data) {
3691 $manager = get_emoticon_manager();
3692 $emoticons = $this->process_form_data($data);
3694 if ($emoticons === false) {
3695 return false;
3698 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
3699 return ''; // success
3700 } else {
3701 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
3706 * Return XHTML field(s) for options
3708 * @param array $data Array of options to set in HTML
3709 * @return string XHTML string for the fields and wrapping div(s)
3711 public function output_html($data, $query='') {
3712 global $OUTPUT;
3714 $out = html_writer::start_tag('table', array('id' => 'emoticonsetting', 'class' => 'admintable generaltable'));
3715 $out .= html_writer::start_tag('thead');
3716 $out .= html_writer::start_tag('tr');
3717 $out .= html_writer::tag('th', get_string('emoticontext', 'admin'));
3718 $out .= html_writer::tag('th', get_string('emoticonimagename', 'admin'));
3719 $out .= html_writer::tag('th', get_string('emoticoncomponent', 'admin'));
3720 $out .= html_writer::tag('th', get_string('emoticonalt', 'admin'), array('colspan' => 2));
3721 $out .= html_writer::tag('th', '');
3722 $out .= html_writer::end_tag('tr');
3723 $out .= html_writer::end_tag('thead');
3724 $out .= html_writer::start_tag('tbody');
3725 $i = 0;
3726 foreach($data as $field => $value) {
3727 switch ($i) {
3728 case 0:
3729 $out .= html_writer::start_tag('tr');
3730 $current_text = $value;
3731 $current_filename = '';
3732 $current_imagecomponent = '';
3733 $current_altidentifier = '';
3734 $current_altcomponent = '';
3735 case 1:
3736 $current_filename = $value;
3737 case 2:
3738 $current_imagecomponent = $value;
3739 case 3:
3740 $current_altidentifier = $value;
3741 case 4:
3742 $current_altcomponent = $value;
3745 $out .= html_writer::tag('td',
3746 html_writer::empty_tag('input',
3747 array(
3748 'type' => 'text',
3749 'class' => 'form-text',
3750 'name' => $this->get_full_name().'['.$field.']',
3751 'value' => $value,
3753 ), array('class' => 'c'.$i)
3756 if ($i == 4) {
3757 if (get_string_manager()->string_exists($current_altidentifier, $current_altcomponent)) {
3758 $alt = get_string($current_altidentifier, $current_altcomponent);
3759 } else {
3760 $alt = $current_text;
3762 if ($current_filename) {
3763 $out .= html_writer::tag('td', $OUTPUT->render(new pix_emoticon($current_filename, $alt, $current_imagecomponent)));
3764 } else {
3765 $out .= html_writer::tag('td', '');
3767 $out .= html_writer::end_tag('tr');
3768 $i = 0;
3769 } else {
3770 $i++;
3774 $out .= html_writer::end_tag('tbody');
3775 $out .= html_writer::end_tag('table');
3776 $out = html_writer::tag('div', $out, array('class' => 'form-group'));
3777 $out .= html_writer::tag('div', html_writer::link(new moodle_url('/admin/resetemoticons.php'), get_string('emoticonsreset', 'admin')));
3779 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', NULL, $query);
3783 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
3785 * @see self::process_form_data()
3786 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
3787 * @return array of form fields and their values
3789 protected function prepare_form_data(array $emoticons) {
3791 $form = array();
3792 $i = 0;
3793 foreach ($emoticons as $emoticon) {
3794 $form['text'.$i] = $emoticon->text;
3795 $form['imagename'.$i] = $emoticon->imagename;
3796 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
3797 $form['altidentifier'.$i] = $emoticon->altidentifier;
3798 $form['altcomponent'.$i] = $emoticon->altcomponent;
3799 $i++;
3801 // add one more blank field set for new object
3802 $form['text'.$i] = '';
3803 $form['imagename'.$i] = '';
3804 $form['imagecomponent'.$i] = '';
3805 $form['altidentifier'.$i] = '';
3806 $form['altcomponent'.$i] = '';
3808 return $form;
3812 * Converts the data from admin settings form into an array of emoticon objects
3814 * @see self::prepare_form_data()
3815 * @param array $data array of admin form fields and values
3816 * @return false|array of emoticon objects
3818 protected function process_form_data(array $form) {
3820 $count = count($form); // number of form field values
3822 if ($count % 5) {
3823 // we must get five fields per emoticon object
3824 return false;
3827 $emoticons = array();
3828 for ($i = 0; $i < $count / 5; $i++) {
3829 $emoticon = new stdClass();
3830 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
3831 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
3832 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
3833 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
3834 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
3836 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
3837 // prevent from breaking http://url.addresses by accident
3838 $emoticon->text = '';
3841 if (strlen($emoticon->text) < 2) {
3842 // do not allow single character emoticons
3843 $emoticon->text = '';
3846 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
3847 // emoticon text must contain some non-alphanumeric character to prevent
3848 // breaking HTML tags
3849 $emoticon->text = '';
3852 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
3853 $emoticons[] = $emoticon;
3856 return $emoticons;
3862 * Special setting for limiting of the list of available languages.
3864 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3866 class admin_setting_langlist extends admin_setting_configtext {
3868 * Calls parent::__construct with specific arguments
3870 public function __construct() {
3871 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
3875 * Save the new setting
3877 * @param string $data The new setting
3878 * @return bool
3880 public function write_setting($data) {
3881 $return = parent::write_setting($data);
3882 get_string_manager()->reset_caches();
3883 return $return;
3889 * Selection of one of the recognised countries using the list
3890 * returned by {@link get_list_of_countries()}.
3892 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3894 class admin_settings_country_select extends admin_setting_configselect {
3895 protected $includeall;
3896 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
3897 $this->includeall = $includeall;
3898 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
3902 * Lazy-load the available choices for the select box
3904 public function load_choices() {
3905 global $CFG;
3906 if (is_array($this->choices)) {
3907 return true;
3909 $this->choices = array_merge(
3910 array('0' => get_string('choosedots')),
3911 get_string_manager()->get_list_of_countries($this->includeall));
3912 return true;
3918 * admin_setting_configselect for the default number of sections in a course,
3919 * simply so we can lazy-load the choices.
3921 * @copyright 2011 The Open University
3922 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3924 class admin_settings_num_course_sections extends admin_setting_configselect {
3925 public function __construct($name, $visiblename, $description, $defaultsetting) {
3926 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
3929 /** Lazy-load the available choices for the select box */
3930 public function load_choices() {
3931 $max = get_config('moodlecourse', 'maxsections');
3932 if (!isset($max) || !is_numeric($max)) {
3933 $max = 52;
3935 for ($i = 0; $i <= $max; $i++) {
3936 $this->choices[$i] = "$i";
3938 return true;
3944 * Course category selection
3946 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3948 class admin_settings_coursecat_select extends admin_setting_configselect {
3950 * Calls parent::__construct with specific arguments
3952 public function __construct($name, $visiblename, $description, $defaultsetting) {
3953 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
3957 * Load the available choices for the select box
3959 * @return bool
3961 public function load_choices() {
3962 global $CFG;
3963 require_once($CFG->dirroot.'/course/lib.php');
3964 if (is_array($this->choices)) {
3965 return true;
3967 $this->choices = make_categories_options();
3968 return true;
3974 * Special control for selecting days to backup
3976 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3978 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
3980 * Calls parent::__construct with specific arguments
3982 public function __construct() {
3983 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
3984 $this->plugin = 'backup';
3988 * Load the available choices for the select box
3990 * @return bool Always returns true
3992 public function load_choices() {
3993 if (is_array($this->choices)) {
3994 return true;
3996 $this->choices = array();
3997 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
3998 foreach ($days as $day) {
3999 $this->choices[$day] = get_string($day, 'calendar');
4001 return true;
4007 * Special debug setting
4009 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4011 class admin_setting_special_debug extends admin_setting_configselect {
4013 * Calls parent::__construct with specific arguments
4015 public function __construct() {
4016 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
4020 * Load the available choices for the select box
4022 * @return bool
4024 public function load_choices() {
4025 if (is_array($this->choices)) {
4026 return true;
4028 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
4029 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
4030 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
4031 DEBUG_ALL => get_string('debugall', 'admin'),
4032 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
4033 return true;
4039 * Special admin control
4041 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4043 class admin_setting_special_calendar_weekend extends admin_setting {
4045 * Calls parent::__construct with specific arguments
4047 public function __construct() {
4048 $name = 'calendar_weekend';
4049 $visiblename = get_string('calendar_weekend', 'admin');
4050 $description = get_string('helpweekenddays', 'admin');
4051 $default = array ('0', '6'); // Saturdays and Sundays
4052 parent::__construct($name, $visiblename, $description, $default);
4056 * Gets the current settings as an array
4058 * @return mixed Null if none, else array of settings
4060 public function get_setting() {
4061 $result = $this->config_read($this->name);
4062 if (is_null($result)) {
4063 return NULL;
4065 if ($result === '') {
4066 return array();
4068 $settings = array();
4069 for ($i=0; $i<7; $i++) {
4070 if ($result & (1 << $i)) {
4071 $settings[] = $i;
4074 return $settings;
4078 * Save the new settings
4080 * @param array $data Array of new settings
4081 * @return bool
4083 public function write_setting($data) {
4084 if (!is_array($data)) {
4085 return '';
4087 unset($data['xxxxx']);
4088 $result = 0;
4089 foreach($data as $index) {
4090 $result |= 1 << $index;
4092 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
4096 * Return XHTML to display the control
4098 * @param array $data array of selected days
4099 * @param string $query
4100 * @return string XHTML for display (field + wrapping div(s)
4102 public function output_html($data, $query='') {
4103 // The order matters very much because of the implied numeric keys
4104 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
4105 $return = '<table><thead><tr>';
4106 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
4107 foreach($days as $index => $day) {
4108 $return .= '<td><label for="'.$this->get_id().$index.'">'.get_string($day, 'calendar').'</label></td>';
4110 $return .= '</tr></thead><tbody><tr>';
4111 foreach($days as $index => $day) {
4112 $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>';
4114 $return .= '</tr></tbody></table>';
4116 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
4123 * Admin setting that allows a user to pick a behaviour.
4125 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4127 class admin_setting_question_behaviour extends admin_setting_configselect {
4129 * @param string $name name of config variable
4130 * @param string $visiblename display name
4131 * @param string $description description
4132 * @param string $default default.
4134 public function __construct($name, $visiblename, $description, $default) {
4135 parent::__construct($name, $visiblename, $description, $default, NULL);
4139 * Load list of behaviours as choices
4140 * @return bool true => success, false => error.
4142 public function load_choices() {
4143 global $CFG;
4144 require_once($CFG->dirroot . '/question/engine/lib.php');
4145 $this->choices = question_engine::get_behaviour_options('');
4146 return true;
4152 * Admin setting that allows a user to pick appropriate roles for something.
4154 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4156 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
4157 /** @var array Array of capabilities which identify roles */
4158 private $types;
4161 * @param string $name Name of config variable
4162 * @param string $visiblename Display name
4163 * @param string $description Description
4164 * @param array $types Array of archetypes which identify
4165 * roles that will be enabled by default.
4167 public function __construct($name, $visiblename, $description, $types) {
4168 parent::__construct($name, $visiblename, $description, NULL, NULL);
4169 $this->types = $types;
4173 * Load roles as choices
4175 * @return bool true=>success, false=>error
4177 public function load_choices() {
4178 global $CFG, $DB;
4179 if (during_initial_install()) {
4180 return false;
4182 if (is_array($this->choices)) {
4183 return true;
4185 if ($roles = get_all_roles()) {
4186 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
4187 return true;
4188 } else {
4189 return false;
4194 * Return the default setting for this control
4196 * @return array Array of default settings
4198 public function get_defaultsetting() {
4199 global $CFG;
4201 if (during_initial_install()) {
4202 return null;
4204 $result = array();
4205 foreach($this->types as $archetype) {
4206 if ($caproles = get_archetype_roles($archetype)) {
4207 foreach ($caproles as $caprole) {
4208 $result[$caprole->id] = 1;
4212 return $result;
4218 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
4220 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4222 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
4224 * Constructor
4225 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4226 * @param string $visiblename localised
4227 * @param string $description long localised info
4228 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
4229 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
4230 * @param int $size default field size
4232 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
4233 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
4237 * Loads the current setting and returns array
4239 * @return array Returns array value=>xx, __construct=>xx
4241 public function get_setting() {
4242 $value = parent::get_setting();
4243 $adv = $this->config_read($this->name.'_adv');
4244 if (is_null($value) or is_null($adv)) {
4245 return NULL;
4247 return array('value' => $value, 'adv' => $adv);
4251 * Saves the new settings passed in $data
4253 * @todo Add vartype handling to ensure $data is an array
4254 * @param array $data
4255 * @return mixed string or Array
4257 public function write_setting($data) {
4258 $error = parent::write_setting($data['value']);
4259 if (!$error) {
4260 $value = empty($data['adv']) ? 0 : 1;
4261 $this->config_write($this->name.'_adv', $value);
4263 return $error;
4267 * Return XHTML for the control
4269 * @param array $data Default data array
4270 * @param string $query
4271 * @return string XHTML to display control
4273 public function output_html($data, $query='') {
4274 $default = $this->get_defaultsetting();
4275 $defaultinfo = array();
4276 if (isset($default['value'])) {
4277 if ($default['value'] === '') {
4278 $defaultinfo[] = "''";
4279 } else {
4280 $defaultinfo[] = $default['value'];
4283 if (!empty($default['adv'])) {
4284 $defaultinfo[] = get_string('advanced');
4286 $defaultinfo = implode(', ', $defaultinfo);
4288 $adv = !empty($data['adv']);
4289 $return = '<div class="form-text defaultsnext">' .
4290 '<input type="text" size="' . $this->size . '" id="' . $this->get_id() .
4291 '" name="' . $this->get_full_name() . '[value]" value="' . s($data['value']) . '" />' .
4292 ' <input type="checkbox" class="form-checkbox" id="' .
4293 $this->get_id() . '_adv" name="' . $this->get_full_name() .
4294 '[adv]" value="1" ' . ($adv ? 'checked="checked"' : '') . ' />' .
4295 ' <label for="' . $this->get_id() . '_adv">' .
4296 get_string('advanced') . '</label></div>';
4298 return format_admin_setting($this, $this->visiblename, $return,
4299 $this->description, true, '', $defaultinfo, $query);
4305 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
4307 * @copyright 2009 Petr Skoda (http://skodak.org)
4308 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4310 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
4313 * Constructor
4314 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4315 * @param string $visiblename localised
4316 * @param string $description long localised info
4317 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
4318 * @param string $yes value used when checked
4319 * @param string $no value used when not checked
4321 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4322 parent::__construct($name, $visiblename, $description, $defaultsetting, $yes, $no);
4326 * Loads the current setting and returns array
4328 * @return array Returns array value=>xx, adv=>xx
4330 public function get_setting() {
4331 $value = parent::get_setting();
4332 $adv = $this->config_read($this->name.'_adv');
4333 if (is_null($value) or is_null($adv)) {
4334 return NULL;
4336 return array('value' => $value, 'adv' => $adv);
4340 * Sets the value for the setting
4342 * Sets the value for the setting to either the yes or no values
4343 * of the object by comparing $data to yes
4345 * @param mixed $data Gets converted to str for comparison against yes value
4346 * @return string empty string or error
4348 public function write_setting($data) {
4349 $error = parent::write_setting($data['value']);
4350 if (!$error) {
4351 $value = empty($data['adv']) ? 0 : 1;
4352 $this->config_write($this->name.'_adv', $value);
4354 return $error;
4358 * Returns an XHTML checkbox field and with extra advanced cehckbox
4360 * @param string $data If $data matches yes then checkbox is checked
4361 * @param string $query
4362 * @return string XHTML field
4364 public function output_html($data, $query='') {
4365 $defaults = $this->get_defaultsetting();
4366 $defaultinfo = array();
4367 if (!is_null($defaults)) {
4368 if ((string)$defaults['value'] === $this->yes) {
4369 $defaultinfo[] = get_string('checkboxyes', 'admin');
4370 } else {
4371 $defaultinfo[] = get_string('checkboxno', 'admin');
4373 if (!empty($defaults['adv'])) {
4374 $defaultinfo[] = get_string('advanced');
4377 $defaultinfo = implode(', ', $defaultinfo);
4379 if ((string)$data['value'] === $this->yes) { // convert to strings before comparison
4380 $checked = 'checked="checked"';
4381 } else {
4382 $checked = '';
4384 if (!empty($data['adv'])) {
4385 $advanced = 'checked="checked"';
4386 } else {
4387 $advanced = '';
4390 $fullname = $this->get_full_name();
4391 $novalue = s($this->no);
4392 $yesvalue = s($this->yes);
4393 $id = $this->get_id();
4394 $stradvanced = get_string('advanced');
4395 $return = <<<EOT
4396 <div class="form-checkbox defaultsnext" >
4397 <input type="hidden" name="{$fullname}[value]" value="$novalue" />
4398 <input type="checkbox" id="$id" name="{$fullname}[value]" value="$yesvalue" $checked />
4399 <input type="checkbox" class="form-checkbox" id="{$id}_adv" name="{$fullname}[adv]" value="1" $advanced />
4400 <label for="{$id}_adv">$stradvanced</label>
4401 </div>
4402 EOT;
4403 return format_admin_setting($this, $this->visiblename, $return, $this->description,
4404 true, '', $defaultinfo, $query);
4410 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
4412 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
4414 * @copyright 2010 Sam Hemelryk
4415 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4417 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
4419 * Constructor
4420 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4421 * @param string $visiblename localised
4422 * @param string $description long localised info
4423 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
4424 * @param string $yes value used when checked
4425 * @param string $no value used when not checked
4427 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
4428 parent::__construct($name, $visiblename, $description, $defaultsetting, $yes, $no);
4432 * Loads the current setting and returns array
4434 * @return array Returns array value=>xx, adv=>xx
4436 public function get_setting() {
4437 $value = parent::get_setting();
4438 $locked = $this->config_read($this->name.'_locked');
4439 if (is_null($value) or is_null($locked)) {
4440 return NULL;
4442 return array('value' => $value, 'locked' => $locked);
4446 * Sets the value for the setting
4448 * Sets the value for the setting to either the yes or no values
4449 * of the object by comparing $data to yes
4451 * @param mixed $data Gets converted to str for comparison against yes value
4452 * @return string empty string or error
4454 public function write_setting($data) {
4455 $error = parent::write_setting($data['value']);
4456 if (!$error) {
4457 $value = empty($data['locked']) ? 0 : 1;
4458 $this->config_write($this->name.'_locked', $value);
4460 return $error;
4464 * Returns an XHTML checkbox field and with extra locked checkbox
4466 * @param string $data If $data matches yes then checkbox is checked
4467 * @param string $query
4468 * @return string XHTML field
4470 public function output_html($data, $query='') {
4471 $defaults = $this->get_defaultsetting();
4472 $defaultinfo = array();
4473 if (!is_null($defaults)) {
4474 if ((string)$defaults['value'] === $this->yes) {
4475 $defaultinfo[] = get_string('checkboxyes', 'admin');
4476 } else {
4477 $defaultinfo[] = get_string('checkboxno', 'admin');
4479 if (!empty($defaults['locked'])) {
4480 $defaultinfo[] = get_string('locked', 'admin');
4483 $defaultinfo = implode(', ', $defaultinfo);
4485 $fullname = $this->get_full_name();
4486 $novalue = s($this->no);
4487 $yesvalue = s($this->yes);
4488 $id = $this->get_id();
4490 $checkboxparams = array('type'=>'checkbox', 'id'=>$id,'name'=>$fullname.'[value]', 'value'=>$yesvalue);
4491 if ((string)$data['value'] === $this->yes) { // convert to strings before comparison
4492 $checkboxparams['checked'] = 'checked';
4495 $lockcheckboxparams = array('type'=>'checkbox', 'id'=>$id.'_locked','name'=>$fullname.'[locked]', 'value'=>1, 'class'=>'form-checkbox locked-checkbox');
4496 if (!empty($data['locked'])) { // convert to strings before comparison
4497 $lockcheckboxparams['checked'] = 'checked';
4500 $return = html_writer::start_tag('div', array('class'=>'form-checkbox defaultsnext'));
4501 $return .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>$fullname.'[value]', 'value'=>$novalue));
4502 $return .= html_writer::empty_tag('input', $checkboxparams);
4503 $return .= html_writer::empty_tag('input', $lockcheckboxparams);
4504 $return .= html_writer::tag('label', get_string('locked', 'admin'), array('for'=>$id.'_locked'));
4505 $return .= html_writer::end_tag('div');
4506 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
4512 * Dropdown menu 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_configselect_with_advanced extends admin_setting_configselect {
4518 * Calls parent::__construct with specific arguments
4520 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4521 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
4525 * Loads the current setting and returns array
4527 * @return array Returns array value=>xx, adv=>xx
4529 public function get_setting() {
4530 $value = parent::get_setting();
4531 $adv = $this->config_read($this->name.'_adv');
4532 if (is_null($value) or is_null($adv)) {
4533 return NULL;
4535 return array('value' => $value, 'adv' => $adv);
4539 * Saves the new settings passed in $data
4541 * @todo Add vartype handling to ensure $data is an array
4542 * @param array $data
4543 * @return mixed string or Array
4545 public function write_setting($data) {
4546 $error = parent::write_setting($data['value']);
4547 if (!$error) {
4548 $value = empty($data['adv']) ? 0 : 1;
4549 $this->config_write($this->name.'_adv', $value);
4551 return $error;
4555 * Return XHTML for the control
4557 * @param array $data Default data array
4558 * @param string $query
4559 * @return string XHTML to display control
4561 public function output_html($data, $query='') {
4562 $default = $this->get_defaultsetting();
4563 $current = $this->get_setting();
4565 list($selecthtml, $warning) = $this->output_select_html($data['value'],
4566 $current['value'], $default['value'], '[value]');
4567 if (!$selecthtml) {
4568 return '';
4571 if (!is_null($default) and array_key_exists($default['value'], $this->choices)) {
4572 $defaultinfo = array();
4573 if (isset($this->choices[$default['value']])) {
4574 $defaultinfo[] = $this->choices[$default['value']];
4576 if (!empty($default['adv'])) {
4577 $defaultinfo[] = get_string('advanced');
4579 $defaultinfo = implode(', ', $defaultinfo);
4580 } else {
4581 $defaultinfo = '';
4584 $adv = !empty($data['adv']);
4585 $return = '<div class="form-select defaultsnext">' . $selecthtml .
4586 ' <input type="checkbox" class="form-checkbox" id="' .
4587 $this->get_id() . '_adv" name="' . $this->get_full_name() .
4588 '[adv]" value="1" ' . ($adv ? 'checked="checked"' : '') . ' />' .
4589 ' <label for="' . $this->get_id() . '_adv">' .
4590 get_string('advanced') . '</label></div>';
4592 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
4598 * Graded roles in gradebook
4600 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4602 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
4604 * Calls parent::__construct with specific arguments
4606 public function __construct() {
4607 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
4608 get_string('configgradebookroles', 'admin'),
4609 array('student'));
4616 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4618 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
4620 * Saves the new settings passed in $data
4622 * @param string $data
4623 * @return mixed string or Array
4625 public function write_setting($data) {
4626 global $CFG, $DB;
4628 $oldvalue = $this->config_read($this->name);
4629 $return = parent::write_setting($data);
4630 $newvalue = $this->config_read($this->name);
4632 if ($oldvalue !== $newvalue) {
4633 // force full regrading
4634 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
4637 return $return;
4643 * Which roles to show on course description page
4645 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4647 class admin_setting_special_coursecontact extends admin_setting_pickroles {
4649 * Calls parent::__construct with specific arguments
4651 public function __construct() {
4652 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
4653 get_string('coursecontact_desc', 'admin'),
4654 array('editingteacher'));
4661 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4663 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
4665 * Calls parent::__construct with specific arguments
4667 function admin_setting_special_gradelimiting() {
4668 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
4669 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
4673 * Force site regrading
4675 function regrade_all() {
4676 global $CFG;
4677 require_once("$CFG->libdir/gradelib.php");
4678 grade_force_site_regrading();
4682 * Saves the new settings
4684 * @param mixed $data
4685 * @return string empty string or error message
4687 function write_setting($data) {
4688 $previous = $this->get_setting();
4690 if ($previous === null) {
4691 if ($data) {
4692 $this->regrade_all();
4694 } else {
4695 if ($data != $previous) {
4696 $this->regrade_all();
4699 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4706 * Primary grade export plugin - has state tracking.
4708 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4710 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
4712 * Calls parent::__construct with specific arguments
4714 public function __construct() {
4715 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
4716 get_string('configgradeexport', 'admin'), array(), NULL);
4720 * Load the available choices for the multicheckbox
4722 * @return bool always returns true
4724 public function load_choices() {
4725 if (is_array($this->choices)) {
4726 return true;
4728 $this->choices = array();
4730 if ($plugins = get_plugin_list('gradeexport')) {
4731 foreach($plugins as $plugin => $unused) {
4732 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
4735 return true;
4741 * Grade category settings
4743 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4745 class admin_setting_gradecat_combo extends admin_setting {
4746 /** @var array Array of choices */
4747 public $choices;
4750 * Sets choices and calls parent::__construct with passed arguments
4751 * @param string $name
4752 * @param string $visiblename
4753 * @param string $description
4754 * @param mixed $defaultsetting string or array depending on implementation
4755 * @param array $choices An array of choices for the control
4757 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
4758 $this->choices = $choices;
4759 parent::__construct($name, $visiblename, $description, $defaultsetting);
4763 * Return the current setting(s) array
4765 * @return array Array of value=>xx, forced=>xx, adv=>xx
4767 public function get_setting() {
4768 global $CFG;
4770 $value = $this->config_read($this->name);
4771 $flag = $this->config_read($this->name.'_flag');
4773 if (is_null($value) or is_null($flag)) {
4774 return NULL;
4777 $flag = (int)$flag;
4778 $forced = (boolean)(1 & $flag); // first bit
4779 $adv = (boolean)(2 & $flag); // second bit
4781 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
4785 * Save the new settings passed in $data
4787 * @todo Add vartype handling to ensure $data is array
4788 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4789 * @return string empty or error message
4791 public function write_setting($data) {
4792 global $CFG;
4794 $value = $data['value'];
4795 $forced = empty($data['forced']) ? 0 : 1;
4796 $adv = empty($data['adv']) ? 0 : 2;
4797 $flag = ($forced | $adv); //bitwise or
4799 if (!in_array($value, array_keys($this->choices))) {
4800 return 'Error setting ';
4803 $oldvalue = $this->config_read($this->name);
4804 $oldflag = (int)$this->config_read($this->name.'_flag');
4805 $oldforced = (1 & $oldflag); // first bit
4807 $result1 = $this->config_write($this->name, $value);
4808 $result2 = $this->config_write($this->name.'_flag', $flag);
4810 // force regrade if needed
4811 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
4812 require_once($CFG->libdir.'/gradelib.php');
4813 grade_category::updated_forced_settings();
4816 if ($result1 and $result2) {
4817 return '';
4818 } else {
4819 return get_string('errorsetting', 'admin');
4824 * Return XHTML to display the field and wrapping div
4826 * @todo Add vartype handling to ensure $data is array
4827 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
4828 * @param string $query
4829 * @return string XHTML to display control
4831 public function output_html($data, $query='') {
4832 $value = $data['value'];
4833 $forced = !empty($data['forced']);
4834 $adv = !empty($data['adv']);
4836 $default = $this->get_defaultsetting();
4837 if (!is_null($default)) {
4838 $defaultinfo = array();
4839 if (isset($this->choices[$default['value']])) {
4840 $defaultinfo[] = $this->choices[$default['value']];
4842 if (!empty($default['forced'])) {
4843 $defaultinfo[] = get_string('force');
4845 if (!empty($default['adv'])) {
4846 $defaultinfo[] = get_string('advanced');
4848 $defaultinfo = implode(', ', $defaultinfo);
4850 } else {
4851 $defaultinfo = NULL;
4855 $return = '<div class="form-group">';
4856 $return .= '<select class="form-select" id="'.$this->get_id().'" name="'.$this->get_full_name().'[value]">';
4857 foreach ($this->choices as $key => $val) {
4858 // the string cast is needed because key may be integer - 0 is equal to most strings!
4859 $return .= '<option value="'.$key.'"'.((string)$key==$value ? ' selected="selected"' : '').'>'.$val.'</option>';
4861 $return .= '</select>';
4862 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'force" name="'.$this->get_full_name().'[forced]" value="1" '.($forced ? 'checked="checked"' : '').' />'
4863 .'<label for="'.$this->get_id().'force">'.get_string('force').'</label>';
4864 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'adv" name="'.$this->get_full_name().'[adv]" value="1" '.($adv ? 'checked="checked"' : '').' />'
4865 .'<label for="'.$this->get_id().'adv">'.get_string('advanced').'</label>';
4866 $return .= '</div>';
4868 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
4874 * Selection of grade report in user profiles
4876 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4878 class admin_setting_grade_profilereport extends admin_setting_configselect {
4880 * Calls parent::__construct with specific arguments
4882 public function __construct() {
4883 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
4887 * Loads an array of choices for the configselect control
4889 * @return bool always return true
4891 public function load_choices() {
4892 if (is_array($this->choices)) {
4893 return true;
4895 $this->choices = array();
4897 global $CFG;
4898 require_once($CFG->libdir.'/gradelib.php');
4900 foreach (get_plugin_list('gradereport') as $plugin => $plugindir) {
4901 if (file_exists($plugindir.'/lib.php')) {
4902 require_once($plugindir.'/lib.php');
4903 $functionname = 'grade_report_'.$plugin.'_profilereport';
4904 if (function_exists($functionname)) {
4905 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
4909 return true;
4915 * Special class for register auth selection
4917 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4919 class admin_setting_special_registerauth extends admin_setting_configselect {
4921 * Calls parent::__construct with specific arguments
4923 public function __construct() {
4924 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
4928 * Returns the default option
4930 * @return string empty or default option
4932 public function get_defaultsetting() {
4933 $this->load_choices();
4934 $defaultsetting = parent::get_defaultsetting();
4935 if (array_key_exists($defaultsetting, $this->choices)) {
4936 return $defaultsetting;
4937 } else {
4938 return '';
4943 * Loads the possible choices for the array
4945 * @return bool always returns true
4947 public function load_choices() {
4948 global $CFG;
4950 if (is_array($this->choices)) {
4951 return true;
4953 $this->choices = array();
4954 $this->choices[''] = get_string('disable');
4956 $authsenabled = get_enabled_auth_plugins(true);
4958 foreach ($authsenabled as $auth) {
4959 $authplugin = get_auth_plugin($auth);
4960 if (!$authplugin->can_signup()) {
4961 continue;
4963 // Get the auth title (from core or own auth lang files)
4964 $authtitle = $authplugin->get_title();
4965 $this->choices[$auth] = $authtitle;
4967 return true;
4973 * General plugins manager
4975 class admin_page_pluginsoverview extends admin_externalpage {
4978 * Sets basic information about the external page
4980 public function __construct() {
4981 global $CFG;
4982 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
4983 "$CFG->wwwroot/$CFG->admin/plugins.php");
4988 * Module manage page
4990 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4992 class admin_page_managemods extends admin_externalpage {
4994 * Calls parent::__construct with specific arguments
4996 public function __construct() {
4997 global $CFG;
4998 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
5002 * Try to find the specified module
5004 * @param string $query The module to search for
5005 * @return array
5007 public function search($query) {
5008 global $CFG, $DB;
5009 if ($result = parent::search($query)) {
5010 return $result;
5013 $found = false;
5014 if ($modules = $DB->get_records('modules')) {
5015 foreach ($modules as $module) {
5016 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
5017 continue;
5019 if (strpos($module->name, $query) !== false) {
5020 $found = true;
5021 break;
5023 $strmodulename = get_string('modulename', $module->name);
5024 if (strpos(textlib::strtolower($strmodulename), $query) !== false) {
5025 $found = true;
5026 break;
5030 if ($found) {
5031 $result = new stdClass();
5032 $result->page = $this;
5033 $result->settings = array();
5034 return array($this->name => $result);
5035 } else {
5036 return array();
5043 * Special class for enrol plugins management.
5045 * @copyright 2010 Petr Skoda {@link http://skodak.org}
5046 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5048 class admin_setting_manageenrols extends admin_setting {
5050 * Calls parent::__construct with specific arguments
5052 public function __construct() {
5053 $this->nosave = true;
5054 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
5058 * Always returns true, does nothing
5060 * @return true
5062 public function get_setting() {
5063 return true;
5067 * Always returns true, does nothing
5069 * @return true
5071 public function get_defaultsetting() {
5072 return true;
5076 * Always returns '', does not write anything
5078 * @return string Always returns ''
5080 public function write_setting($data) {
5081 // do not write any setting
5082 return '';
5086 * Checks if $query is one of the available enrol plugins
5088 * @param string $query The string to search for
5089 * @return bool Returns true if found, false if not
5091 public function is_related($query) {
5092 if (parent::is_related($query)) {
5093 return true;
5096 $query = textlib::strtolower($query);
5097 $enrols = enrol_get_plugins(false);
5098 foreach ($enrols as $name=>$enrol) {
5099 $localised = get_string('pluginname', 'enrol_'.$name);
5100 if (strpos(textlib::strtolower($name), $query) !== false) {
5101 return true;
5103 if (strpos(textlib::strtolower($localised), $query) !== false) {
5104 return true;
5107 return false;
5111 * Builds the XHTML to display the control
5113 * @param string $data Unused
5114 * @param string $query
5115 * @return string
5117 public function output_html($data, $query='') {
5118 global $CFG, $OUTPUT, $DB, $PAGE;
5120 // Display strings.
5121 $strup = get_string('up');
5122 $strdown = get_string('down');
5123 $strsettings = get_string('settings');
5124 $strenable = get_string('enable');
5125 $strdisable = get_string('disable');
5126 $struninstall = get_string('uninstallplugin', 'admin');
5127 $strusage = get_string('enrolusage', 'enrol');
5128 $strversion = get_string('version');
5130 $pluginmanager = plugin_manager::instance();
5132 $enrols_available = enrol_get_plugins(false);
5133 $active_enrols = enrol_get_plugins(true);
5135 $allenrols = array();
5136 foreach ($active_enrols as $key=>$enrol) {
5137 $allenrols[$key] = true;
5139 foreach ($enrols_available as $key=>$enrol) {
5140 $allenrols[$key] = true;
5142 // Now find all borked plugins and at least allow then to uninstall.
5143 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
5144 foreach ($condidates as $candidate) {
5145 if (empty($allenrols[$candidate])) {
5146 $allenrols[$candidate] = true;
5150 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
5151 $return .= $OUTPUT->box_start('generalbox enrolsui');
5153 $table = new html_table();
5154 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
5155 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
5156 $table->id = 'courseenrolmentplugins';
5157 $table->attributes['class'] = 'admintable generaltable';
5158 $table->data = array();
5160 // Iterate through enrol plugins and add to the display table.
5161 $updowncount = 1;
5162 $enrolcount = count($active_enrols);
5163 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
5164 $printed = array();
5165 foreach($allenrols as $enrol => $unused) {
5166 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
5167 $version = get_config('enrol_'.$enrol, 'version');
5168 if ($version === false) {
5169 $version = '';
5172 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
5173 $name = get_string('pluginname', 'enrol_'.$enrol);
5174 } else {
5175 $name = $enrol;
5177 // Usage.
5178 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
5179 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
5180 $usage = "$ci / $cp";
5182 // Hide/show links.
5183 if (isset($active_enrols[$enrol])) {
5184 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
5185 $hideshow = "<a href=\"$aurl\">";
5186 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
5187 $enabled = true;
5188 $displayname = "<span>$name</span>";
5189 } else if (isset($enrols_available[$enrol])) {
5190 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
5191 $hideshow = "<a href=\"$aurl\">";
5192 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
5193 $enabled = false;
5194 $displayname = "<span class=\"dimmed_text\">$name</span>";
5195 } else {
5196 $hideshow = '';
5197 $enabled = false;
5198 $displayname = '<span class="notifyproblem">'.$name.'</span>';
5200 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
5201 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
5202 } else {
5203 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
5206 // Up/down link (only if enrol is enabled).
5207 $updown = '';
5208 if ($enabled) {
5209 if ($updowncount > 1) {
5210 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
5211 $updown .= "<a href=\"$aurl\">";
5212 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" class=\"iconsmall\" /></a>&nbsp;";
5213 } else {
5214 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5216 if ($updowncount < $enrolcount) {
5217 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
5218 $updown .= "<a href=\"$aurl\">";
5219 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" class=\"iconsmall\" /></a>";
5220 } else {
5221 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5223 ++$updowncount;
5226 // Add settings link.
5227 if (!$version) {
5228 $settings = '';
5229 } else if ($url = $plugininfo->get_settings_url()) {
5230 $settings = html_writer::link($url, $strsettings);
5231 } else {
5232 $settings = '';
5235 // Add uninstall info.
5236 if ($version) {
5237 $url = new moodle_url($plugininfo->get_uninstall_url(), array('return'=>'settings'));
5238 $uninstall = html_writer::link($url, $struninstall);
5239 } else {
5240 $uninstall = '';
5243 // Add a row to the table.
5244 $table->data[] = array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $uninstall);
5246 $printed[$enrol] = true;
5249 $return .= html_writer::table($table);
5250 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
5251 $return .= $OUTPUT->box_end();
5252 return highlight($query, $return);
5258 * Blocks manage page
5260 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5262 class admin_page_manageblocks extends admin_externalpage {
5264 * Calls parent::__construct with specific arguments
5266 public function __construct() {
5267 global $CFG;
5268 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
5272 * Search for a specific block
5274 * @param string $query The string to search for
5275 * @return array
5277 public function search($query) {
5278 global $CFG, $DB;
5279 if ($result = parent::search($query)) {
5280 return $result;
5283 $found = false;
5284 if ($blocks = $DB->get_records('block')) {
5285 foreach ($blocks as $block) {
5286 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
5287 continue;
5289 if (strpos($block->name, $query) !== false) {
5290 $found = true;
5291 break;
5293 $strblockname = get_string('pluginname', 'block_'.$block->name);
5294 if (strpos(textlib::strtolower($strblockname), $query) !== false) {
5295 $found = true;
5296 break;
5300 if ($found) {
5301 $result = new stdClass();
5302 $result->page = $this;
5303 $result->settings = array();
5304 return array($this->name => $result);
5305 } else {
5306 return array();
5312 * Message outputs configuration
5314 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5316 class admin_page_managemessageoutputs extends admin_externalpage {
5318 * Calls parent::__construct with specific arguments
5320 public function __construct() {
5321 global $CFG;
5322 parent::__construct('managemessageoutputs', get_string('managemessageoutputs', 'message'), new moodle_url('/admin/message.php'));
5326 * Search for a specific message processor
5328 * @param string $query The string to search for
5329 * @return array
5331 public function search($query) {
5332 global $CFG, $DB;
5333 if ($result = parent::search($query)) {
5334 return $result;
5337 $found = false;
5338 if ($processors = get_message_processors()) {
5339 foreach ($processors as $processor) {
5340 if (!$processor->available) {
5341 continue;
5343 if (strpos($processor->name, $query) !== false) {
5344 $found = true;
5345 break;
5347 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
5348 if (strpos(textlib::strtolower($strprocessorname), $query) !== false) {
5349 $found = true;
5350 break;
5354 if ($found) {
5355 $result = new stdClass();
5356 $result->page = $this;
5357 $result->settings = array();
5358 return array($this->name => $result);
5359 } else {
5360 return array();
5366 * Default message outputs configuration
5368 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5370 class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
5372 * Calls parent::__construct with specific arguments
5374 public function __construct() {
5375 global $CFG;
5376 admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
5382 * Manage question behaviours page
5384 * @copyright 2011 The Open University
5385 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5387 class admin_page_manageqbehaviours extends admin_externalpage {
5389 * Constructor
5391 public function __construct() {
5392 global $CFG;
5393 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
5394 new moodle_url('/admin/qbehaviours.php'));
5398 * Search question behaviours for the specified string
5400 * @param string $query The string to search for in question behaviours
5401 * @return array
5403 public function search($query) {
5404 global $CFG;
5405 if ($result = parent::search($query)) {
5406 return $result;
5409 $found = false;
5410 require_once($CFG->dirroot . '/question/engine/lib.php');
5411 foreach (get_plugin_list('qbehaviour') as $behaviour => $notused) {
5412 if (strpos(textlib::strtolower(question_engine::get_behaviour_name($behaviour)),
5413 $query) !== false) {
5414 $found = true;
5415 break;
5418 if ($found) {
5419 $result = new stdClass();
5420 $result->page = $this;
5421 $result->settings = array();
5422 return array($this->name => $result);
5423 } else {
5424 return array();
5431 * Question type manage page
5433 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5435 class admin_page_manageqtypes extends admin_externalpage {
5437 * Calls parent::__construct with specific arguments
5439 public function __construct() {
5440 global $CFG;
5441 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
5442 new moodle_url('/admin/qtypes.php'));
5446 * Search question types for the specified string
5448 * @param string $query The string to search for in question types
5449 * @return array
5451 public function search($query) {
5452 global $CFG;
5453 if ($result = parent::search($query)) {
5454 return $result;
5457 $found = false;
5458 require_once($CFG->dirroot . '/question/engine/bank.php');
5459 foreach (question_bank::get_all_qtypes() as $qtype) {
5460 if (strpos(textlib::strtolower($qtype->local_name()), $query) !== false) {
5461 $found = true;
5462 break;
5465 if ($found) {
5466 $result = new stdClass();
5467 $result->page = $this;
5468 $result->settings = array();
5469 return array($this->name => $result);
5470 } else {
5471 return array();
5477 class admin_page_manageportfolios extends admin_externalpage {
5479 * Calls parent::__construct with specific arguments
5481 public function __construct() {
5482 global $CFG;
5483 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
5484 "$CFG->wwwroot/$CFG->admin/portfolio.php");
5488 * Searches page for the specified string.
5489 * @param string $query The string to search for
5490 * @return bool True if it is found on this page
5492 public function search($query) {
5493 global $CFG;
5494 if ($result = parent::search($query)) {
5495 return $result;
5498 $found = false;
5499 $portfolios = get_plugin_list('portfolio');
5500 foreach ($portfolios as $p => $dir) {
5501 if (strpos($p, $query) !== false) {
5502 $found = true;
5503 break;
5506 if (!$found) {
5507 foreach (portfolio_instances(false, false) as $instance) {
5508 $title = $instance->get('name');
5509 if (strpos(textlib::strtolower($title), $query) !== false) {
5510 $found = true;
5511 break;
5516 if ($found) {
5517 $result = new stdClass();
5518 $result->page = $this;
5519 $result->settings = array();
5520 return array($this->name => $result);
5521 } else {
5522 return array();
5528 class admin_page_managerepositories extends admin_externalpage {
5530 * Calls parent::__construct with specific arguments
5532 public function __construct() {
5533 global $CFG;
5534 parent::__construct('managerepositories', get_string('manage',
5535 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
5539 * Searches page for the specified string.
5540 * @param string $query The string to search for
5541 * @return bool True if it is found on this page
5543 public function search($query) {
5544 global $CFG;
5545 if ($result = parent::search($query)) {
5546 return $result;
5549 $found = false;
5550 $repositories= get_plugin_list('repository');
5551 foreach ($repositories as $p => $dir) {
5552 if (strpos($p, $query) !== false) {
5553 $found = true;
5554 break;
5557 if (!$found) {
5558 foreach (repository::get_types() as $instance) {
5559 $title = $instance->get_typename();
5560 if (strpos(textlib::strtolower($title), $query) !== false) {
5561 $found = true;
5562 break;
5567 if ($found) {
5568 $result = new stdClass();
5569 $result->page = $this;
5570 $result->settings = array();
5571 return array($this->name => $result);
5572 } else {
5573 return array();
5580 * Special class for authentication administration.
5582 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5584 class admin_setting_manageauths extends admin_setting {
5586 * Calls parent::__construct with specific arguments
5588 public function __construct() {
5589 $this->nosave = true;
5590 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
5594 * Always returns true
5596 * @return true
5598 public function get_setting() {
5599 return true;
5603 * Always returns true
5605 * @return true
5607 public function get_defaultsetting() {
5608 return true;
5612 * Always returns '' and doesn't write anything
5614 * @return string Always returns ''
5616 public function write_setting($data) {
5617 // do not write any setting
5618 return '';
5622 * Search to find if Query is related to auth plugin
5624 * @param string $query The string to search for
5625 * @return bool true for related false for not
5627 public function is_related($query) {
5628 if (parent::is_related($query)) {
5629 return true;
5632 $authsavailable = get_plugin_list('auth');
5633 foreach ($authsavailable as $auth => $dir) {
5634 if (strpos($auth, $query) !== false) {
5635 return true;
5637 $authplugin = get_auth_plugin($auth);
5638 $authtitle = $authplugin->get_title();
5639 if (strpos(textlib::strtolower($authtitle), $query) !== false) {
5640 return true;
5643 return false;
5647 * Return XHTML to display control
5649 * @param mixed $data Unused
5650 * @param string $query
5651 * @return string highlight
5653 public function output_html($data, $query='') {
5654 global $CFG, $OUTPUT;
5657 // display strings
5658 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
5659 'settings', 'edit', 'name', 'enable', 'disable',
5660 'up', 'down', 'none'));
5661 $txt->updown = "$txt->up/$txt->down";
5663 $authsavailable = get_plugin_list('auth');
5664 get_enabled_auth_plugins(true); // fix the list of enabled auths
5665 if (empty($CFG->auth)) {
5666 $authsenabled = array();
5667 } else {
5668 $authsenabled = explode(',', $CFG->auth);
5671 // construct the display array, with enabled auth plugins at the top, in order
5672 $displayauths = array();
5673 $registrationauths = array();
5674 $registrationauths[''] = $txt->disable;
5675 foreach ($authsenabled as $auth) {
5676 $authplugin = get_auth_plugin($auth);
5677 /// Get the auth title (from core or own auth lang files)
5678 $authtitle = $authplugin->get_title();
5679 /// Apply titles
5680 $displayauths[$auth] = $authtitle;
5681 if ($authplugin->can_signup()) {
5682 $registrationauths[$auth] = $authtitle;
5686 foreach ($authsavailable as $auth => $dir) {
5687 if (array_key_exists($auth, $displayauths)) {
5688 continue; //already in the list
5690 $authplugin = get_auth_plugin($auth);
5691 /// Get the auth title (from core or own auth lang files)
5692 $authtitle = $authplugin->get_title();
5693 /// Apply titles
5694 $displayauths[$auth] = $authtitle;
5695 if ($authplugin->can_signup()) {
5696 $registrationauths[$auth] = $authtitle;
5700 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
5701 $return .= $OUTPUT->box_start('generalbox authsui');
5703 $table = new html_table();
5704 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings);
5705 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign');
5706 $table->data = array();
5707 $table->attributes['class'] = 'admintable generaltable';
5708 $table->id = 'manageauthtable';
5710 //add always enabled plugins first
5711 $displayname = "<span>".$displayauths['manual']."</span>";
5712 $settings = "<a href=\"auth_config.php?auth=manual\">{$txt->settings}</a>";
5713 //$settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
5714 $table->data[] = array($displayname, '', '', $settings);
5715 $displayname = "<span>".$displayauths['nologin']."</span>";
5716 $settings = "<a href=\"auth_config.php?auth=nologin\">{$txt->settings}</a>";
5717 $table->data[] = array($displayname, '', '', $settings);
5720 // iterate through auth plugins and add to the display table
5721 $updowncount = 1;
5722 $authcount = count($authsenabled);
5723 $url = "auth.php?sesskey=" . sesskey();
5724 foreach ($displayauths as $auth => $name) {
5725 if ($auth == 'manual' or $auth == 'nologin') {
5726 continue;
5728 // hide/show link
5729 if (in_array($auth, $authsenabled)) {
5730 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
5731 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5732 // $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\"><input type=\"checkbox\" checked /></a>";
5733 $enabled = true;
5734 $displayname = "<span>$name</span>";
5736 else {
5737 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
5738 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5739 // $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\"><input type=\"checkbox\" /></a>";
5740 $enabled = false;
5741 $displayname = "<span class=\"dimmed_text\">$name</span>";
5744 // up/down link (only if auth is enabled)
5745 $updown = '';
5746 if ($enabled) {
5747 if ($updowncount > 1) {
5748 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
5749 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5751 else {
5752 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5754 if ($updowncount < $authcount) {
5755 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
5756 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5758 else {
5759 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5761 ++ $updowncount;
5764 // settings link
5765 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
5766 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
5767 } else {
5768 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
5771 // add a row to the table
5772 $table->data[] =array($displayname, $hideshow, $updown, $settings);
5774 $return .= html_writer::table($table);
5775 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
5776 $return .= $OUTPUT->box_end();
5777 return highlight($query, $return);
5783 * Special class for authentication administration.
5785 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5787 class admin_setting_manageeditors extends admin_setting {
5789 * Calls parent::__construct with specific arguments
5791 public function __construct() {
5792 $this->nosave = true;
5793 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
5797 * Always returns true, does nothing
5799 * @return true
5801 public function get_setting() {
5802 return true;
5806 * Always returns true, does nothing
5808 * @return true
5810 public function get_defaultsetting() {
5811 return true;
5815 * Always returns '', does not write anything
5817 * @return string Always returns ''
5819 public function write_setting($data) {
5820 // do not write any setting
5821 return '';
5825 * Checks if $query is one of the available editors
5827 * @param string $query The string to search for
5828 * @return bool Returns true if found, false if not
5830 public function is_related($query) {
5831 if (parent::is_related($query)) {
5832 return true;
5835 $editors_available = editors_get_available();
5836 foreach ($editors_available as $editor=>$editorstr) {
5837 if (strpos($editor, $query) !== false) {
5838 return true;
5840 if (strpos(textlib::strtolower($editorstr), $query) !== false) {
5841 return true;
5844 return false;
5848 * Builds the XHTML to display the control
5850 * @param string $data Unused
5851 * @param string $query
5852 * @return string
5854 public function output_html($data, $query='') {
5855 global $CFG, $OUTPUT;
5857 // display strings
5858 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
5859 'up', 'down', 'none'));
5860 $struninstall = get_string('uninstallplugin', 'admin');
5862 $txt->updown = "$txt->up/$txt->down";
5864 $editors_available = editors_get_available();
5865 $active_editors = explode(',', $CFG->texteditors);
5867 $active_editors = array_reverse($active_editors);
5868 foreach ($active_editors as $key=>$editor) {
5869 if (empty($editors_available[$editor])) {
5870 unset($active_editors[$key]);
5871 } else {
5872 $name = $editors_available[$editor];
5873 unset($editors_available[$editor]);
5874 $editors_available[$editor] = $name;
5877 if (empty($active_editors)) {
5878 //$active_editors = array('textarea');
5880 $editors_available = array_reverse($editors_available, true);
5881 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
5882 $return .= $OUTPUT->box_start('generalbox editorsui');
5884 $table = new html_table();
5885 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
5886 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign');
5887 $table->id = 'editormanagement';
5888 $table->attributes['class'] = 'admintable generaltable';
5889 $table->data = array();
5891 // iterate through auth plugins and add to the display table
5892 $updowncount = 1;
5893 $editorcount = count($active_editors);
5894 $url = "editors.php?sesskey=" . sesskey();
5895 foreach ($editors_available as $editor => $name) {
5896 // hide/show link
5897 if (in_array($editor, $active_editors)) {
5898 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
5899 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"disable\" /></a>";
5900 // $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\"><input type=\"checkbox\" checked /></a>";
5901 $enabled = true;
5902 $displayname = "<span>$name</span>";
5904 else {
5905 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
5906 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"enable\" /></a>";
5907 // $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\"><input type=\"checkbox\" /></a>";
5908 $enabled = false;
5909 $displayname = "<span class=\"dimmed_text\">$name</span>";
5912 // up/down link (only if auth is enabled)
5913 $updown = '';
5914 if ($enabled) {
5915 if ($updowncount > 1) {
5916 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
5917 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
5919 else {
5920 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />&nbsp;";
5922 if ($updowncount < $editorcount) {
5923 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
5924 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
5926 else {
5927 $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"iconsmall\" alt=\"\" />";
5929 ++ $updowncount;
5932 // settings link
5933 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
5934 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
5935 $settings = "<a href='$eurl'>{$txt->settings}</a>";
5936 } else {
5937 $settings = '';
5940 if ($editor === 'textarea') {
5941 $uninstall = '';
5942 } else {
5943 $uurl = new moodle_url('/admin/editors.php', array('action'=>'uninstall', 'editor'=>$editor, 'sesskey'=>sesskey()));
5944 $uninstall = html_writer::link($uurl, $struninstall);
5947 // add a row to the table
5948 $table->data[] =array($displayname, $hideshow, $updown, $settings, $uninstall);
5950 $return .= html_writer::table($table);
5951 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
5952 $return .= $OUTPUT->box_end();
5953 return highlight($query, $return);
5959 * Special class for license administration.
5961 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5963 class admin_setting_managelicenses extends admin_setting {
5965 * Calls parent::__construct with specific arguments
5967 public function __construct() {
5968 $this->nosave = true;
5969 parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', '');
5973 * Always returns true, does nothing
5975 * @return true
5977 public function get_setting() {
5978 return true;
5982 * Always returns true, does nothing
5984 * @return true
5986 public function get_defaultsetting() {
5987 return true;
5991 * Always returns '', does not write anything
5993 * @return string Always returns ''
5995 public function write_setting($data) {
5996 // do not write any setting
5997 return '';
6001 * Builds the XHTML to display the control
6003 * @param string $data Unused
6004 * @param string $query
6005 * @return string
6007 public function output_html($data, $query='') {
6008 global $CFG, $OUTPUT;
6009 require_once($CFG->libdir . '/licenselib.php');
6010 $url = "licenses.php?sesskey=" . sesskey();
6012 // display strings
6013 $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none'));
6014 $licenses = license_manager::get_licenses();
6016 $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true);
6018 $return .= $OUTPUT->box_start('generalbox editorsui');
6020 $table = new html_table();
6021 $table->head = array($txt->name, $txt->enable);
6022 $table->colclasses = array('leftalign', 'centeralign');
6023 $table->id = 'availablelicenses';
6024 $table->attributes['class'] = 'admintable generaltable';
6025 $table->data = array();
6027 foreach ($licenses as $value) {
6028 $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank'));
6030 if ($value->enabled == 1) {
6031 $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
6032 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/hide'), 'class'=>'iconsmall', 'alt'=>'disable')));
6033 } else {
6034 $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
6035 html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/show'), 'class'=>'iconsmall', 'alt'=>'enable')));
6038 if ($value->shortname == $CFG->sitedefaultlicense) {
6039 $displayname .= ' '.html_writer::tag('img', '', array('src'=>$OUTPUT->pix_url('t/locked'), 'class'=>'iconsmall', 'alt'=>get_string('default'), 'title'=>get_string('default')));
6040 $hideshow = '';
6043 $enabled = true;
6045 $table->data[] =array($displayname, $hideshow);
6047 $return .= html_writer::table($table);
6048 $return .= $OUTPUT->box_end();
6049 return highlight($query, $return);
6054 * Course formats manager. Allows to enable/disable formats and jump to settings
6056 class admin_setting_manageformats extends admin_setting {
6059 * Calls parent::__construct with specific arguments
6061 public function __construct() {
6062 $this->nosave = true;
6063 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
6067 * Always returns true
6069 * @return true
6071 public function get_setting() {
6072 return true;
6076 * Always returns true
6078 * @return true
6080 public function get_defaultsetting() {
6081 return true;
6085 * Always returns '' and doesn't write anything
6087 * @param mixed $data string or array, must not be NULL
6088 * @return string Always returns ''
6090 public function write_setting($data) {
6091 // do not write any setting
6092 return '';
6096 * Search to find if Query is related to format plugin
6098 * @param string $query The string to search for
6099 * @return bool true for related false for not
6101 public function is_related($query) {
6102 if (parent::is_related($query)) {
6103 return true;
6105 $formats = plugin_manager::instance()->get_plugins_of_type('format');
6106 foreach ($formats as $format) {
6107 if (strpos($format->component, $query) !== false ||
6108 strpos(textlib::strtolower($format->displayname), $query) !== false) {
6109 return true;
6112 return false;
6116 * Return XHTML to display control
6118 * @param mixed $data Unused
6119 * @param string $query
6120 * @return string highlight
6122 public function output_html($data, $query='') {
6123 global $CFG, $OUTPUT;
6124 $return = '';
6125 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
6126 $return .= $OUTPUT->box_start('generalbox formatsui');
6128 $formats = plugin_manager::instance()->get_plugins_of_type('format');
6130 // display strings
6131 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default', 'delete'));
6132 $txt->updown = "$txt->up/$txt->down";
6134 $table = new html_table();
6135 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->delete, $txt->settings);
6136 $table->align = array('left', 'center', 'center', 'center', 'center');
6137 $table->width = '90%';
6138 $table->attributes['class'] = 'manageformattable generaltable';
6139 $table->data = array();
6141 $cnt = 0;
6142 $defaultformat = get_config('moodlecourse', 'format');
6143 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
6144 foreach ($formats as $format) {
6145 $url = new moodle_url('/admin/courseformats.php',
6146 array('sesskey' => sesskey(), 'format' => $format->name));
6147 $isdefault = '';
6148 if ($format->is_enabled()) {
6149 $strformatname = html_writer::tag('span', $format->displayname);
6150 if ($defaultformat === $format->name) {
6151 $hideshow = $txt->default;
6152 } else {
6153 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
6154 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
6156 } else {
6157 $strformatname = html_writer::tag('span', $format->displayname, array('class' => 'dimmed_text'));
6158 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
6159 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
6161 $updown = '';
6162 if ($cnt) {
6163 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
6164 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
6165 } else {
6166 $updown .= $spacer;
6168 if ($cnt < count($formats) - 1) {
6169 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
6170 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
6171 } else {
6172 $updown .= $spacer;
6174 $cnt++;
6175 $settings = '';
6176 if ($format->get_settings_url()) {
6177 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
6179 $uninstall = '';
6180 if ($defaultformat !== $format->name) {
6181 $uninstall = html_writer::link($format->get_uninstall_url(), $txt->delete);
6183 $table->data[] =array($strformatname, $hideshow, $updown, $uninstall, $settings);
6185 $return .= html_writer::table($table);
6186 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
6187 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
6188 $return .= $OUTPUT->box_end();
6189 return highlight($query, $return);
6194 * Special class for filter administration.
6196 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6198 class admin_page_managefilters extends admin_externalpage {
6200 * Calls parent::__construct with specific arguments
6202 public function __construct() {
6203 global $CFG;
6204 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
6208 * Searches all installed filters for specified filter
6210 * @param string $query The filter(string) to search for
6211 * @param string $query
6213 public function search($query) {
6214 global $CFG;
6215 if ($result = parent::search($query)) {
6216 return $result;
6219 $found = false;
6220 $filternames = filter_get_all_installed();
6221 foreach ($filternames as $path => $strfiltername) {
6222 if (strpos(textlib::strtolower($strfiltername), $query) !== false) {
6223 $found = true;
6224 break;
6226 if (strpos($path, $query) !== false) {
6227 $found = true;
6228 break;
6232 if ($found) {
6233 $result = new stdClass;
6234 $result->page = $this;
6235 $result->settings = array();
6236 return array($this->name => $result);
6237 } else {
6238 return array();
6245 * Initialise admin page - this function does require login and permission
6246 * checks specified in page definition.
6248 * This function must be called on each admin page before other code.
6250 * @global moodle_page $PAGE
6252 * @param string $section name of page
6253 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
6254 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
6255 * added to the turn blocks editing on/off form, so this page reloads correctly.
6256 * @param string $actualurl if the actual page being viewed is not the normal one for this
6257 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
6258 * @param array $options Additional options that can be specified for page setup.
6259 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
6261 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
6262 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
6264 $PAGE->set_context(null); // hack - set context to something, by default to system context
6266 $site = get_site();
6267 require_login();
6269 if (!empty($options['pagelayout'])) {
6270 // A specific page layout has been requested.
6271 $PAGE->set_pagelayout($options['pagelayout']);
6272 } else if ($section === 'upgradesettings') {
6273 $PAGE->set_pagelayout('maintenance');
6274 } else {
6275 $PAGE->set_pagelayout('admin');
6278 $adminroot = admin_get_root(false, false); // settings not required for external pages
6279 $extpage = $adminroot->locate($section, true);
6281 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
6282 // The requested section isn't in the admin tree
6283 // It could be because the user has inadequate capapbilities or because the section doesn't exist
6284 if (!has_capability('moodle/site:config', context_system::instance())) {
6285 // The requested section could depend on a different capability
6286 // but most likely the user has inadequate capabilities
6287 print_error('accessdenied', 'admin');
6288 } else {
6289 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
6293 // this eliminates our need to authenticate on the actual pages
6294 if (!$extpage->check_access()) {
6295 print_error('accessdenied', 'admin');
6296 die;
6299 // $PAGE->set_extra_button($extrabutton); TODO
6301 if (!$actualurl) {
6302 $actualurl = $extpage->url;
6305 $PAGE->set_url($actualurl, $extraurlparams);
6306 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
6307 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
6310 if (empty($SITE->fullname) || empty($SITE->shortname)) {
6311 // During initial install.
6312 $strinstallation = get_string('installation', 'install');
6313 $strsettings = get_string('settings');
6314 $PAGE->navbar->add($strsettings);
6315 $PAGE->set_title($strinstallation);
6316 $PAGE->set_heading($strinstallation);
6317 $PAGE->set_cacheable(false);
6318 return;
6321 // Locate the current item on the navigation and make it active when found.
6322 $path = $extpage->path;
6323 $node = $PAGE->settingsnav;
6324 while ($node && count($path) > 0) {
6325 $node = $node->get(array_pop($path));
6327 if ($node) {
6328 $node->make_active();
6331 // Normal case.
6332 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
6333 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
6334 $USER->editing = $adminediting;
6337 $visiblepathtosection = array_reverse($extpage->visiblepath);
6339 if ($PAGE->user_allowed_editing()) {
6340 if ($PAGE->user_is_editing()) {
6341 $caption = get_string('blockseditoff');
6342 $url = new moodle_url($PAGE->url, array('adminedit'=>'0'));
6343 } else {
6344 $caption = get_string('blocksediton');
6345 $url = new moodle_url($PAGE->url, array('adminedit'=>'1'));
6347 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
6350 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
6351 $PAGE->set_heading($SITE->fullname);
6353 // prevent caching in nav block
6354 $PAGE->navigation->clear_cache();
6358 * Returns the reference to admin tree root
6360 * @return object admin_root object
6362 function admin_get_root($reload=false, $requirefulltree=true) {
6363 global $CFG, $DB, $OUTPUT;
6365 static $ADMIN = NULL;
6367 if (is_null($ADMIN)) {
6368 // create the admin tree!
6369 $ADMIN = new admin_root($requirefulltree);
6372 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
6373 $ADMIN->purge_children($requirefulltree);
6376 if (!$ADMIN->loaded) {
6377 // we process this file first to create categories first and in correct order
6378 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
6380 // now we process all other files in admin/settings to build the admin tree
6381 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
6382 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
6383 continue;
6385 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
6386 // plugins are loaded last - they may insert pages anywhere
6387 continue;
6389 require($file);
6391 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
6393 $ADMIN->loaded = true;
6396 return $ADMIN;
6399 /// settings utility functions
6402 * This function applies default settings.
6404 * @param object $node, NULL means complete tree, null by default
6405 * @param bool $unconditional if true overrides all values with defaults, null buy default
6407 function admin_apply_default_settings($node=NULL, $unconditional=true) {
6408 global $CFG;
6410 if (is_null($node)) {
6411 $node = admin_get_root(true, true);
6414 if ($node instanceof admin_category) {
6415 $entries = array_keys($node->children);
6416 foreach ($entries as $entry) {
6417 admin_apply_default_settings($node->children[$entry], $unconditional);
6420 } else if ($node instanceof admin_settingpage) {
6421 foreach ($node->settings as $setting) {
6422 if (!$unconditional and !is_null($setting->get_setting())) {
6423 //do not override existing defaults
6424 continue;
6426 $defaultsetting = $setting->get_defaultsetting();
6427 if (is_null($defaultsetting)) {
6428 // no value yet - default maybe applied after admin user creation or in upgradesettings
6429 continue;
6431 $setting->write_setting($defaultsetting);
6437 * Store changed settings, this function updates the errors variable in $ADMIN
6439 * @param object $formdata from form
6440 * @return int number of changed settings
6442 function admin_write_settings($formdata) {
6443 global $CFG, $SITE, $DB;
6445 $olddbsessions = !empty($CFG->dbsessions);
6446 $formdata = (array)$formdata;
6448 $data = array();
6449 foreach ($formdata as $fullname=>$value) {
6450 if (strpos($fullname, 's_') !== 0) {
6451 continue; // not a config value
6453 $data[$fullname] = $value;
6456 $adminroot = admin_get_root();
6457 $settings = admin_find_write_settings($adminroot, $data);
6459 $count = 0;
6460 foreach ($settings as $fullname=>$setting) {
6461 /** @var $setting admin_setting */
6462 $original = $setting->get_setting();
6463 $error = $setting->write_setting($data[$fullname]);
6464 if ($error !== '') {
6465 $adminroot->errors[$fullname] = new stdClass();
6466 $adminroot->errors[$fullname]->data = $data[$fullname];
6467 $adminroot->errors[$fullname]->id = $setting->get_id();
6468 $adminroot->errors[$fullname]->error = $error;
6470 if ($setting->post_write_settings($original)) {
6471 $count++;
6475 if ($olddbsessions != !empty($CFG->dbsessions)) {
6476 require_logout();
6479 // Now update $SITE - just update the fields, in case other people have a
6480 // a reference to it (e.g. $PAGE, $COURSE).
6481 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
6482 foreach (get_object_vars($newsite) as $field => $value) {
6483 $SITE->$field = $value;
6486 // now reload all settings - some of them might depend on the changed
6487 admin_get_root(true);
6488 return $count;
6492 * Internal recursive function - finds all settings from submitted form
6494 * @param object $node Instance of admin_category, or admin_settingpage
6495 * @param array $data
6496 * @return array
6498 function admin_find_write_settings($node, $data) {
6499 $return = array();
6501 if (empty($data)) {
6502 return $return;
6505 if ($node instanceof admin_category) {
6506 $entries = array_keys($node->children);
6507 foreach ($entries as $entry) {
6508 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
6511 } else if ($node instanceof admin_settingpage) {
6512 foreach ($node->settings as $setting) {
6513 $fullname = $setting->get_full_name();
6514 if (array_key_exists($fullname, $data)) {
6515 $return[$fullname] = $setting;
6521 return $return;
6525 * Internal function - prints the search results
6527 * @param string $query String to search for
6528 * @return string empty or XHTML
6530 function admin_search_settings_html($query) {
6531 global $CFG, $OUTPUT;
6533 if (textlib::strlen($query) < 2) {
6534 return '';
6536 $query = textlib::strtolower($query);
6538 $adminroot = admin_get_root();
6539 $findings = $adminroot->search($query);
6540 $return = '';
6541 $savebutton = false;
6543 foreach ($findings as $found) {
6544 $page = $found->page;
6545 $settings = $found->settings;
6546 if ($page->is_hidden()) {
6547 // hidden pages are not displayed in search results
6548 continue;
6550 if ($page instanceof admin_externalpage) {
6551 $return .= $OUTPUT->heading(get_string('searchresults','admin').' - <a href="'.$page->url.'">'.highlight($query, $page->visiblename).'</a>', 2, 'main');
6552 } else if ($page instanceof admin_settingpage) {
6553 $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');
6554 } else {
6555 continue;
6557 if (!empty($settings)) {
6558 $return .= '<fieldset class="adminsettings">'."\n";
6559 foreach ($settings as $setting) {
6560 if (empty($setting->nosave)) {
6561 $savebutton = true;
6563 $return .= '<div class="clearer"><!-- --></div>'."\n";
6564 $fullname = $setting->get_full_name();
6565 if (array_key_exists($fullname, $adminroot->errors)) {
6566 $data = $adminroot->errors[$fullname]->data;
6567 } else {
6568 $data = $setting->get_setting();
6569 // do not use defaults if settings not available - upgradesettings handles the defaults!
6571 $return .= $setting->output_html($data, $query);
6573 $return .= '</fieldset>';
6577 if ($savebutton) {
6578 $return .= '<div class="form-buttons"><input class="form-submit" type="submit" value="'.get_string('savechanges','admin').'" /></div>';
6581 return $return;
6585 * Internal function - returns arrays of html pages with uninitialised settings
6587 * @param object $node Instance of admin_category or admin_settingpage
6588 * @return array
6590 function admin_output_new_settings_by_page($node) {
6591 global $OUTPUT;
6592 $return = array();
6594 if ($node instanceof admin_category) {
6595 $entries = array_keys($node->children);
6596 foreach ($entries as $entry) {
6597 $return += admin_output_new_settings_by_page($node->children[$entry]);
6600 } else if ($node instanceof admin_settingpage) {
6601 $newsettings = array();
6602 foreach ($node->settings as $setting) {
6603 if (is_null($setting->get_setting())) {
6604 $newsettings[] = $setting;
6607 if (count($newsettings) > 0) {
6608 $adminroot = admin_get_root();
6609 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
6610 $page .= '<fieldset class="adminsettings">'."\n";
6611 foreach ($newsettings as $setting) {
6612 $fullname = $setting->get_full_name();
6613 if (array_key_exists($fullname, $adminroot->errors)) {
6614 $data = $adminroot->errors[$fullname]->data;
6615 } else {
6616 $data = $setting->get_setting();
6617 if (is_null($data)) {
6618 $data = $setting->get_defaultsetting();
6621 $page .= '<div class="clearer"><!-- --></div>'."\n";
6622 $page .= $setting->output_html($data);
6624 $page .= '</fieldset>';
6625 $return[$node->name] = $page;
6629 return $return;
6633 * Format admin settings
6635 * @param object $setting
6636 * @param string $title label element
6637 * @param string $form form fragment, html code - not highlighted automatically
6638 * @param string $description
6639 * @param bool $label link label to id, true by default
6640 * @param string $warning warning text
6641 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
6642 * @param string $query search query to be highlighted
6643 * @return string XHTML
6645 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
6646 global $CFG;
6648 $name = empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name";
6649 $fullname = $setting->get_full_name();
6651 // sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate
6652 if ($label) {
6653 $labelfor = 'for = "'.$setting->get_id().'"';
6654 } else {
6655 $labelfor = '';
6658 $override = '';
6659 if (empty($setting->plugin)) {
6660 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
6661 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6663 } else {
6664 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
6665 $override = '<div class="form-overridden">'.get_string('configoverride', 'admin').'</div>';
6669 if ($warning !== '') {
6670 $warning = '<div class="form-warning">'.$warning.'</div>';
6673 if (is_null($defaultinfo)) {
6674 $defaultinfo = '';
6675 } else {
6676 if ($defaultinfo === '') {
6677 $defaultinfo = get_string('emptysettingvalue', 'admin');
6679 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
6680 $defaultinfo = '<div class="form-defaultinfo">'.get_string('defaultsettinginfo', 'admin', $defaultinfo).'</div>';
6684 $str = '
6685 <div class="form-item clearfix" id="admin-'.$setting->name.'">
6686 <div class="form-label">
6687 <label '.$labelfor.'>'.highlightfast($query, $title).$override.$warning.'</label>
6688 <span class="form-shortname">'.highlightfast($query, $name).'</span>
6689 </div>
6690 <div class="form-setting">'.$form.$defaultinfo.'</div>
6691 <div class="form-description">'.highlight($query, markdown_to_html($description)).'</div>
6692 </div>';
6694 $adminroot = admin_get_root();
6695 if (array_key_exists($fullname, $adminroot->errors)) {
6696 $str = '<fieldset class="error"><legend>'.$adminroot->errors[$fullname]->error.'</legend>'.$str.'</fieldset>';
6699 return $str;
6703 * Based on find_new_settings{@link ()} in upgradesettings.php
6704 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
6706 * @param object $node Instance of admin_category, or admin_settingpage
6707 * @return boolean true if any settings haven't been initialised, false if they all have
6709 function any_new_admin_settings($node) {
6711 if ($node instanceof admin_category) {
6712 $entries = array_keys($node->children);
6713 foreach ($entries as $entry) {
6714 if (any_new_admin_settings($node->children[$entry])) {
6715 return true;
6719 } else if ($node instanceof admin_settingpage) {
6720 foreach ($node->settings as $setting) {
6721 if ($setting->get_setting() === NULL) {
6722 return true;
6727 return false;
6731 * Moved from admin/replace.php so that we can use this in cron
6733 * @param string $search string to look for
6734 * @param string $replace string to replace
6735 * @return bool success or fail
6737 function db_replace($search, $replace) {
6738 global $DB, $CFG, $OUTPUT;
6740 // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
6741 $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log',
6742 'filter_config', 'sessions', 'events_queue', 'repository_instance_config',
6743 'block_instances', '');
6745 // Turn off time limits, sometimes upgrades can be slow.
6746 @set_time_limit(0);
6748 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
6749 return false;
6751 foreach ($tables as $table) {
6753 if (in_array($table, $skiptables)) { // Don't process these
6754 continue;
6757 if ($columns = $DB->get_columns($table)) {
6758 $DB->set_debug(true);
6759 foreach ($columns as $column => $data) {
6760 if (in_array($data->meta_type, array('C', 'X'))) { // Text stuff only
6761 //TODO: this should be definitively moved to DML driver to do the actual replace, this is not going to work for MSSQL and Oracle...
6762 $DB->execute("UPDATE {".$table."} SET $column = REPLACE($column, ?, ?)", array($search, $replace));
6765 $DB->set_debug(false);
6769 // delete modinfo caches
6770 rebuild_course_cache(0, true);
6772 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
6773 $blocks = get_plugin_list('block');
6774 foreach ($blocks as $blockname=>$fullblock) {
6775 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
6776 continue;
6779 if (!is_readable($fullblock.'/lib.php')) {
6780 continue;
6783 $function = 'block_'.$blockname.'_global_db_replace';
6784 include_once($fullblock.'/lib.php');
6785 if (!function_exists($function)) {
6786 continue;
6789 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
6790 $function($search, $replace);
6791 echo $OUTPUT->notification("...finished", 'notifysuccess');
6794 return true;
6798 * Manage repository settings
6800 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6802 class admin_setting_managerepository extends admin_setting {
6803 /** @var string */
6804 private $baseurl;
6807 * calls parent::__construct with specific arguments
6809 public function __construct() {
6810 global $CFG;
6811 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
6812 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
6816 * Always returns true, does nothing
6818 * @return true
6820 public function get_setting() {
6821 return true;
6825 * Always returns true does nothing
6827 * @return true
6829 public function get_defaultsetting() {
6830 return true;
6834 * Always returns s_managerepository
6836 * @return string Always return 's_managerepository'
6838 public function get_full_name() {
6839 return 's_managerepository';
6843 * Always returns '' doesn't do anything
6845 public function write_setting($data) {
6846 $url = $this->baseurl . '&amp;new=' . $data;
6847 return '';
6848 // TODO
6849 // Should not use redirect and exit here
6850 // Find a better way to do this.
6851 // redirect($url);
6852 // exit;
6856 * Searches repository plugins for one that matches $query
6858 * @param string $query The string to search for
6859 * @return bool true if found, false if not
6861 public function is_related($query) {
6862 if (parent::is_related($query)) {
6863 return true;
6866 $repositories= get_plugin_list('repository');
6867 foreach ($repositories as $p => $dir) {
6868 if (strpos($p, $query) !== false) {
6869 return true;
6872 foreach (repository::get_types() as $instance) {
6873 $title = $instance->get_typename();
6874 if (strpos(textlib::strtolower($title), $query) !== false) {
6875 return true;
6878 return false;
6882 * Helper function that generates a moodle_url object
6883 * relevant to the repository
6886 function repository_action_url($repository) {
6887 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
6891 * Builds XHTML to display the control
6893 * @param string $data Unused
6894 * @param string $query
6895 * @return string XHTML
6897 public function output_html($data, $query='') {
6898 global $CFG, $USER, $OUTPUT;
6900 // Get strings that are used
6901 $strshow = get_string('on', 'repository');
6902 $strhide = get_string('off', 'repository');
6903 $strdelete = get_string('disabled', 'repository');
6905 $actionchoicesforexisting = array(
6906 'show' => $strshow,
6907 'hide' => $strhide,
6908 'delete' => $strdelete
6911 $actionchoicesfornew = array(
6912 'newon' => $strshow,
6913 'newoff' => $strhide,
6914 'delete' => $strdelete
6917 $return = '';
6918 $return .= $OUTPUT->box_start('generalbox');
6920 // Set strings that are used multiple times
6921 $settingsstr = get_string('settings');
6922 $disablestr = get_string('disable');
6924 // Table to list plug-ins
6925 $table = new html_table();
6926 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
6927 $table->align = array('left', 'center', 'center', 'center', 'center');
6928 $table->data = array();
6930 // Get list of used plug-ins
6931 $repositorytypes = repository::get_types();
6932 if (!empty($repositorytypes)) {
6933 // Array to store plugins being used
6934 $alreadyplugins = array();
6935 $totalrepositorytypes = count($repositorytypes);
6936 $updowncount = 1;
6937 foreach ($repositorytypes as $i) {
6938 $settings = '';
6939 $typename = $i->get_typename();
6940 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
6941 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
6942 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
6944 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
6945 // Calculate number of instances in order to display them for the Moodle administrator
6946 if (!empty($instanceoptionnames)) {
6947 $params = array();
6948 $params['context'] = array(get_system_context());
6949 $params['onlyvisible'] = false;
6950 $params['type'] = $typename;
6951 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
6952 // site instances
6953 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
6954 $params['context'] = array();
6955 $instances = repository::static_function($typename, 'get_instances', $params);
6956 $courseinstances = array();
6957 $userinstances = array();
6959 foreach ($instances as $instance) {
6960 $repocontext = context::instance_by_id($instance->instance->contextid);
6961 if ($repocontext->contextlevel == CONTEXT_COURSE) {
6962 $courseinstances[] = $instance;
6963 } else if ($repocontext->contextlevel == CONTEXT_USER) {
6964 $userinstances[] = $instance;
6967 // course instances
6968 $instancenumber = count($courseinstances);
6969 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
6971 // user private instances
6972 $instancenumber = count($userinstances);
6973 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
6974 } else {
6975 $admininstancenumbertext = "";
6976 $courseinstancenumbertext = "";
6977 $userinstancenumbertext = "";
6980 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
6982 $settings .= $OUTPUT->container_start('mdl-left');
6983 $settings .= '<br/>';
6984 $settings .= $admininstancenumbertext;
6985 $settings .= '<br/>';
6986 $settings .= $courseinstancenumbertext;
6987 $settings .= '<br/>';
6988 $settings .= $userinstancenumbertext;
6989 $settings .= $OUTPUT->container_end();
6991 // Get the current visibility
6992 if ($i->get_visible()) {
6993 $currentaction = 'show';
6994 } else {
6995 $currentaction = 'hide';
6998 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
7000 // Display up/down link
7001 $updown = '';
7002 // Should be done with CSS instead.
7003 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
7005 if ($updowncount > 1) {
7006 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
7007 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" class=\"iconsmall\" /></a>&nbsp;";
7009 else {
7010 $updown .= $spacer;
7012 if ($updowncount < $totalrepositorytypes) {
7013 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
7014 $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" class=\"iconsmall\" /></a>";
7016 else {
7017 $updown .= $spacer;
7020 $updowncount++;
7022 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
7024 if (!in_array($typename, $alreadyplugins)) {
7025 $alreadyplugins[] = $typename;
7030 // Get all the plugins that exist on disk
7031 $plugins = get_plugin_list('repository');
7032 if (!empty($plugins)) {
7033 foreach ($plugins as $plugin => $dir) {
7034 // Check that it has not already been listed
7035 if (!in_array($plugin, $alreadyplugins)) {
7036 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
7037 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
7042 $return .= html_writer::table($table);
7043 $return .= $OUTPUT->box_end();
7044 return highlight($query, $return);
7049 * Special checkbox for enable mobile web service
7050 * If enable then we store the service id of the mobile service into config table
7051 * If disable then we unstore the service id from the config table
7053 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
7055 /** @var boolean True means that the capability 'webservice/xmlrpc:use' is set for authenticated user role */
7056 private $xmlrpcuse;
7057 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
7058 private $restuse;
7061 * Return true if Authenticated user role has the capability 'webservice/xmlrpc:use' and 'webservice/rest:use', otherwise false.
7063 * @return boolean
7065 private function is_protocol_cap_allowed() {
7066 global $DB, $CFG;
7068 // We keep xmlrpc enabled for backward compatibility.
7069 // If the $this->xmlrpcuse variable is not set, it needs to be set.
7070 if (empty($this->xmlrpcuse) and $this->xmlrpcuse!==false) {
7071 $params = array();
7072 $params['permission'] = CAP_ALLOW;
7073 $params['roleid'] = $CFG->defaultuserroleid;
7074 $params['capability'] = 'webservice/xmlrpc:use';
7075 $this->xmlrpcuse = $DB->record_exists('role_capabilities', $params);
7078 // If the $this->restuse variable is not set, it needs to be set.
7079 if (empty($this->restuse) and $this->restuse!==false) {
7080 $params = array();
7081 $params['permission'] = CAP_ALLOW;
7082 $params['roleid'] = $CFG->defaultuserroleid;
7083 $params['capability'] = 'webservice/rest:use';
7084 $this->restuse = $DB->record_exists('role_capabilities', $params);
7087 return ($this->xmlrpcuse && $this->restuse);
7091 * Set the 'webservice/xmlrpc:use'/'webservice/rest:use' to the Authenticated user role (allow or not)
7092 * @param type $status true to allow, false to not set
7094 private function set_protocol_cap($status) {
7095 global $CFG;
7096 if ($status and !$this->is_protocol_cap_allowed()) {
7097 //need to allow the cap
7098 $permission = CAP_ALLOW;
7099 $assign = true;
7100 } else if (!$status and $this->is_protocol_cap_allowed()){
7101 //need to disallow the cap
7102 $permission = CAP_INHERIT;
7103 $assign = true;
7105 if (!empty($assign)) {
7106 $systemcontext = get_system_context();
7107 assign_capability('webservice/xmlrpc:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7108 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
7113 * Builds XHTML to display the control.
7114 * The main purpose of this overloading is to display a warning when https
7115 * is not supported by the server
7116 * @param string $data Unused
7117 * @param string $query
7118 * @return string XHTML
7120 public function output_html($data, $query='') {
7121 global $CFG, $OUTPUT;
7122 $html = parent::output_html($data, $query);
7124 if ((string)$data === $this->yes) {
7125 require_once($CFG->dirroot . "/lib/filelib.php");
7126 $curl = new curl();
7127 $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); //force https url
7128 $curl->head($httpswwwroot . "/login/index.php");
7129 $info = $curl->get_info();
7130 if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
7131 $html .= $OUTPUT->notification(get_string('nohttpsformobilewarning', 'admin'));
7135 return $html;
7139 * Retrieves the current setting using the objects name
7141 * @return string
7143 public function get_setting() {
7144 global $CFG;
7146 // For install cli script, $CFG->defaultuserroleid is not set so return 0
7147 // Or if web services aren't enabled this can't be,
7148 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
7149 return 0;
7152 require_once($CFG->dirroot . '/webservice/lib.php');
7153 $webservicemanager = new webservice();
7154 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7155 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
7156 return $this->config_read($this->name); //same as returning 1
7157 } else {
7158 return 0;
7163 * Save the selected setting
7165 * @param string $data The selected site
7166 * @return string empty string or error message
7168 public function write_setting($data) {
7169 global $DB, $CFG;
7171 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
7172 if (empty($CFG->defaultuserroleid)) {
7173 return '';
7176 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
7178 require_once($CFG->dirroot . '/webservice/lib.php');
7179 $webservicemanager = new webservice();
7181 $updateprotocol = false;
7182 if ((string)$data === $this->yes) {
7183 //code run when enable mobile web service
7184 //enable web service systeme if necessary
7185 set_config('enablewebservices', true);
7187 //enable mobile service
7188 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7189 $mobileservice->enabled = 1;
7190 $webservicemanager->update_external_service($mobileservice);
7192 //enable xml-rpc server
7193 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7195 if (!in_array('xmlrpc', $activeprotocols)) {
7196 $activeprotocols[] = 'xmlrpc';
7197 $updateprotocol = true;
7200 if (!in_array('rest', $activeprotocols)) {
7201 $activeprotocols[] = 'rest';
7202 $updateprotocol = true;
7205 if ($updateprotocol) {
7206 set_config('webserviceprotocols', implode(',', $activeprotocols));
7209 //allow xml-rpc:use capability for authenticated user
7210 $this->set_protocol_cap(true);
7212 } else {
7213 //disable web service system if no other services are enabled
7214 $otherenabledservices = $DB->get_records_select('external_services',
7215 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
7216 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
7217 if (empty($otherenabledservices)) {
7218 set_config('enablewebservices', false);
7220 //also disable xml-rpc server
7221 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7222 $protocolkey = array_search('xmlrpc', $activeprotocols);
7223 if ($protocolkey !== false) {
7224 unset($activeprotocols[$protocolkey]);
7225 $updateprotocol = true;
7228 $protocolkey = array_search('rest', $activeprotocols);
7229 if ($protocolkey !== false) {
7230 unset($activeprotocols[$protocolkey]);
7231 $updateprotocol = true;
7234 if ($updateprotocol) {
7235 set_config('webserviceprotocols', implode(',', $activeprotocols));
7238 //disallow xml-rpc:use capability for authenticated user
7239 $this->set_protocol_cap(false);
7242 //disable the mobile service
7243 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
7244 $mobileservice->enabled = 0;
7245 $webservicemanager->update_external_service($mobileservice);
7248 return (parent::write_setting($data));
7253 * Special class for management of external services
7255 * @author Petr Skoda (skodak)
7257 class admin_setting_manageexternalservices extends admin_setting {
7259 * Calls parent::__construct with specific arguments
7261 public function __construct() {
7262 $this->nosave = true;
7263 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
7267 * Always returns true, does nothing
7269 * @return true
7271 public function get_setting() {
7272 return true;
7276 * Always returns true, does nothing
7278 * @return true
7280 public function get_defaultsetting() {
7281 return true;
7285 * Always returns '', does not write anything
7287 * @return string Always returns ''
7289 public function write_setting($data) {
7290 // do not write any setting
7291 return '';
7295 * Checks if $query is one of the available external services
7297 * @param string $query The string to search for
7298 * @return bool Returns true if found, false if not
7300 public function is_related($query) {
7301 global $DB;
7303 if (parent::is_related($query)) {
7304 return true;
7307 $services = $DB->get_records('external_services', array(), 'id, name');
7308 foreach ($services as $service) {
7309 if (strpos(textlib::strtolower($service->name), $query) !== false) {
7310 return true;
7313 return false;
7317 * Builds the XHTML to display the control
7319 * @param string $data Unused
7320 * @param string $query
7321 * @return string
7323 public function output_html($data, $query='') {
7324 global $CFG, $OUTPUT, $DB;
7326 // display strings
7327 $stradministration = get_string('administration');
7328 $stredit = get_string('edit');
7329 $strservice = get_string('externalservice', 'webservice');
7330 $strdelete = get_string('delete');
7331 $strplugin = get_string('plugin', 'admin');
7332 $stradd = get_string('add');
7333 $strfunctions = get_string('functions', 'webservice');
7334 $strusers = get_string('users');
7335 $strserviceusers = get_string('serviceusers', 'webservice');
7337 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
7338 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
7339 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
7341 // built in services
7342 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
7343 $return = "";
7344 if (!empty($services)) {
7345 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
7349 $table = new html_table();
7350 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
7351 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7352 $table->id = 'builtinservices';
7353 $table->attributes['class'] = 'admintable externalservices generaltable';
7354 $table->data = array();
7356 // iterate through auth plugins and add to the display table
7357 foreach ($services as $service) {
7358 $name = $service->name;
7360 // hide/show link
7361 if ($service->enabled) {
7362 $displayname = "<span>$name</span>";
7363 } else {
7364 $displayname = "<span class=\"dimmed_text\">$name</span>";
7367 $plugin = $service->component;
7369 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7371 if ($service->restrictedusers) {
7372 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7373 } else {
7374 $users = get_string('allusers', 'webservice');
7377 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7379 // add a row to the table
7380 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
7382 $return .= html_writer::table($table);
7385 // Custom services
7386 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
7387 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
7389 $table = new html_table();
7390 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
7391 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
7392 $table->id = 'customservices';
7393 $table->attributes['class'] = 'admintable externalservices generaltable';
7394 $table->data = array();
7396 // iterate through auth plugins and add to the display table
7397 foreach ($services as $service) {
7398 $name = $service->name;
7400 // hide/show link
7401 if ($service->enabled) {
7402 $displayname = "<span>$name</span>";
7403 } else {
7404 $displayname = "<span class=\"dimmed_text\">$name</span>";
7407 // delete link
7408 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
7410 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
7412 if ($service->restrictedusers) {
7413 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
7414 } else {
7415 $users = get_string('allusers', 'webservice');
7418 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
7420 // add a row to the table
7421 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
7423 // add new custom service option
7424 $return .= html_writer::table($table);
7426 $return .= '<br />';
7427 // add a token to the table
7428 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
7430 return highlight($query, $return);
7435 * Special class for overview of external services
7437 * @author Jerome Mouneyrac
7439 class admin_setting_webservicesoverview extends admin_setting {
7442 * Calls parent::__construct with specific arguments
7444 public function __construct() {
7445 $this->nosave = true;
7446 parent::__construct('webservicesoverviewui',
7447 get_string('webservicesoverview', 'webservice'), '', '');
7451 * Always returns true, does nothing
7453 * @return true
7455 public function get_setting() {
7456 return true;
7460 * Always returns true, does nothing
7462 * @return true
7464 public function get_defaultsetting() {
7465 return true;
7469 * Always returns '', does not write anything
7471 * @return string Always returns ''
7473 public function write_setting($data) {
7474 // do not write any setting
7475 return '';
7479 * Builds the XHTML to display the control
7481 * @param string $data Unused
7482 * @param string $query
7483 * @return string
7485 public function output_html($data, $query='') {
7486 global $CFG, $OUTPUT;
7488 $return = "";
7489 $brtag = html_writer::empty_tag('br');
7491 // Enable mobile web service
7492 $enablemobile = new admin_setting_enablemobileservice('enablemobilewebservice',
7493 get_string('enablemobilewebservice', 'admin'),
7494 get_string('configenablemobilewebservice',
7495 'admin', ''), 0); //we don't want to display it but to know the ws mobile status
7496 $manageserviceurl = new moodle_url("/admin/settings.php?section=externalservices");
7497 $wsmobileparam = new stdClass();
7498 $wsmobileparam->enablemobileservice = get_string('enablemobilewebservice', 'admin');
7499 $wsmobileparam->manageservicelink = html_writer::link($manageserviceurl,
7500 get_string('externalservices', 'webservice'));
7501 $mobilestatus = $enablemobile->get_setting()?get_string('mobilewsenabled', 'webservice'):get_string('mobilewsdisabled', 'webservice');
7502 $wsmobileparam->wsmobilestatus = html_writer::tag('strong', $mobilestatus);
7503 $return .= $OUTPUT->heading(get_string('enablemobilewebservice', 'admin'), 3, 'main');
7504 $return .= $brtag . get_string('enablemobilewsoverview', 'webservice', $wsmobileparam)
7505 . $brtag . $brtag;
7507 /// One system controlling Moodle with Token
7508 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
7509 $table = new html_table();
7510 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7511 get_string('description'));
7512 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7513 $table->id = 'onesystemcontrol';
7514 $table->attributes['class'] = 'admintable wsoverview generaltable';
7515 $table->data = array();
7517 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
7518 . $brtag . $brtag;
7520 /// 1. Enable Web Services
7521 $row = array();
7522 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7523 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7524 array('href' => $url));
7525 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7526 if ($CFG->enablewebservices) {
7527 $status = get_string('yes');
7529 $row[1] = $status;
7530 $row[2] = get_string('enablewsdescription', 'webservice');
7531 $table->data[] = $row;
7533 /// 2. Enable protocols
7534 $row = array();
7535 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7536 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7537 array('href' => $url));
7538 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7539 //retrieve activated protocol
7540 $active_protocols = empty($CFG->webserviceprotocols) ?
7541 array() : explode(',', $CFG->webserviceprotocols);
7542 if (!empty($active_protocols)) {
7543 $status = "";
7544 foreach ($active_protocols as $protocol) {
7545 $status .= $protocol . $brtag;
7548 $row[1] = $status;
7549 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7550 $table->data[] = $row;
7552 /// 3. Create user account
7553 $row = array();
7554 $url = new moodle_url("/user/editadvanced.php?id=-1");
7555 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
7556 array('href' => $url));
7557 $row[1] = "";
7558 $row[2] = get_string('createuserdescription', 'webservice');
7559 $table->data[] = $row;
7561 /// 4. Add capability to users
7562 $row = array();
7563 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7564 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
7565 array('href' => $url));
7566 $row[1] = "";
7567 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
7568 $table->data[] = $row;
7570 /// 5. Select a web service
7571 $row = array();
7572 $url = new moodle_url("/admin/settings.php?section=externalservices");
7573 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7574 array('href' => $url));
7575 $row[1] = "";
7576 $row[2] = get_string('createservicedescription', 'webservice');
7577 $table->data[] = $row;
7579 /// 6. Add functions
7580 $row = array();
7581 $url = new moodle_url("/admin/settings.php?section=externalservices");
7582 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7583 array('href' => $url));
7584 $row[1] = "";
7585 $row[2] = get_string('addfunctionsdescription', 'webservice');
7586 $table->data[] = $row;
7588 /// 7. Add the specific user
7589 $row = array();
7590 $url = new moodle_url("/admin/settings.php?section=externalservices");
7591 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
7592 array('href' => $url));
7593 $row[1] = "";
7594 $row[2] = get_string('selectspecificuserdescription', 'webservice');
7595 $table->data[] = $row;
7597 /// 8. Create token for the specific user
7598 $row = array();
7599 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
7600 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
7601 array('href' => $url));
7602 $row[1] = "";
7603 $row[2] = get_string('createtokenforuserdescription', 'webservice');
7604 $table->data[] = $row;
7606 /// 9. Enable the documentation
7607 $row = array();
7608 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
7609 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
7610 array('href' => $url));
7611 $status = '<span class="warning">' . get_string('no') . '</span>';
7612 if ($CFG->enablewsdocumentation) {
7613 $status = get_string('yes');
7615 $row[1] = $status;
7616 $row[2] = get_string('enabledocumentationdescription', 'webservice');
7617 $table->data[] = $row;
7619 /// 10. Test the service
7620 $row = array();
7621 $url = new moodle_url("/admin/webservice/testclient.php");
7622 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7623 array('href' => $url));
7624 $row[1] = "";
7625 $row[2] = get_string('testwithtestclientdescription', 'webservice');
7626 $table->data[] = $row;
7628 $return .= html_writer::table($table);
7630 /// Users as clients with token
7631 $return .= $brtag . $brtag . $brtag;
7632 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
7633 $table = new html_table();
7634 $table->head = array(get_string('step', 'webservice'), get_string('status'),
7635 get_string('description'));
7636 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
7637 $table->id = 'userasclients';
7638 $table->attributes['class'] = 'admintable wsoverview generaltable';
7639 $table->data = array();
7641 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
7642 $brtag . $brtag;
7644 /// 1. Enable Web Services
7645 $row = array();
7646 $url = new moodle_url("/admin/search.php?query=enablewebservices");
7647 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
7648 array('href' => $url));
7649 $status = html_writer::tag('span', get_string('no'), array('class' => 'statuscritical'));
7650 if ($CFG->enablewebservices) {
7651 $status = get_string('yes');
7653 $row[1] = $status;
7654 $row[2] = get_string('enablewsdescription', 'webservice');
7655 $table->data[] = $row;
7657 /// 2. Enable protocols
7658 $row = array();
7659 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
7660 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
7661 array('href' => $url));
7662 $status = html_writer::tag('span', get_string('none'), array('class' => 'statuscritical'));
7663 //retrieve activated protocol
7664 $active_protocols = empty($CFG->webserviceprotocols) ?
7665 array() : explode(',', $CFG->webserviceprotocols);
7666 if (!empty($active_protocols)) {
7667 $status = "";
7668 foreach ($active_protocols as $protocol) {
7669 $status .= $protocol . $brtag;
7672 $row[1] = $status;
7673 $row[2] = get_string('enableprotocolsdescription', 'webservice');
7674 $table->data[] = $row;
7677 /// 3. Select a web service
7678 $row = array();
7679 $url = new moodle_url("/admin/settings.php?section=externalservices");
7680 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
7681 array('href' => $url));
7682 $row[1] = "";
7683 $row[2] = get_string('createserviceforusersdescription', 'webservice');
7684 $table->data[] = $row;
7686 /// 4. Add functions
7687 $row = array();
7688 $url = new moodle_url("/admin/settings.php?section=externalservices");
7689 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
7690 array('href' => $url));
7691 $row[1] = "";
7692 $row[2] = get_string('addfunctionsdescription', 'webservice');
7693 $table->data[] = $row;
7695 /// 5. Add capability to users
7696 $row = array();
7697 $url = new moodle_url("/admin/roles/check.php?contextid=1");
7698 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
7699 array('href' => $url));
7700 $row[1] = "";
7701 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
7702 $table->data[] = $row;
7704 /// 6. Test the service
7705 $row = array();
7706 $url = new moodle_url("/admin/webservice/testclient.php");
7707 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
7708 array('href' => $url));
7709 $row[1] = "";
7710 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
7711 $table->data[] = $row;
7713 $return .= html_writer::table($table);
7715 return highlight($query, $return);
7722 * Special class for web service protocol administration.
7724 * @author Petr Skoda (skodak)
7726 class admin_setting_managewebserviceprotocols extends admin_setting {
7729 * Calls parent::__construct with specific arguments
7731 public function __construct() {
7732 $this->nosave = true;
7733 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
7737 * Always returns true, does nothing
7739 * @return true
7741 public function get_setting() {
7742 return true;
7746 * Always returns true, does nothing
7748 * @return true
7750 public function get_defaultsetting() {
7751 return true;
7755 * Always returns '', does not write anything
7757 * @return string Always returns ''
7759 public function write_setting($data) {
7760 // do not write any setting
7761 return '';
7765 * Checks if $query is one of the available webservices
7767 * @param string $query The string to search for
7768 * @return bool Returns true if found, false if not
7770 public function is_related($query) {
7771 if (parent::is_related($query)) {
7772 return true;
7775 $protocols = get_plugin_list('webservice');
7776 foreach ($protocols as $protocol=>$location) {
7777 if (strpos($protocol, $query) !== false) {
7778 return true;
7780 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
7781 if (strpos(textlib::strtolower($protocolstr), $query) !== false) {
7782 return true;
7785 return false;
7789 * Builds the XHTML to display the control
7791 * @param string $data Unused
7792 * @param string $query
7793 * @return string
7795 public function output_html($data, $query='') {
7796 global $CFG, $OUTPUT;
7798 // display strings
7799 $stradministration = get_string('administration');
7800 $strsettings = get_string('settings');
7801 $stredit = get_string('edit');
7802 $strprotocol = get_string('protocol', 'webservice');
7803 $strenable = get_string('enable');
7804 $strdisable = get_string('disable');
7805 $strversion = get_string('version');
7806 $struninstall = get_string('uninstallplugin', 'admin');
7808 $protocols_available = get_plugin_list('webservice');
7809 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
7810 ksort($protocols_available);
7812 foreach ($active_protocols as $key=>$protocol) {
7813 if (empty($protocols_available[$protocol])) {
7814 unset($active_protocols[$key]);
7818 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
7819 $return .= $OUTPUT->box_start('generalbox webservicesui');
7821 $table = new html_table();
7822 $table->head = array($strprotocol, $strversion, $strenable, $struninstall, $strsettings);
7823 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7824 $table->id = 'webserviceprotocols';
7825 $table->attributes['class'] = 'admintable generaltable';
7826 $table->data = array();
7828 // iterate through auth plugins and add to the display table
7829 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
7830 foreach ($protocols_available as $protocol => $location) {
7831 $name = get_string('pluginname', 'webservice_'.$protocol);
7833 $plugin = new stdClass();
7834 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
7835 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
7837 $version = isset($plugin->version) ? $plugin->version : '';
7839 // hide/show link
7840 if (in_array($protocol, $active_protocols)) {
7841 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
7842 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/hide') . "\" class=\"iconsmall\" alt=\"$strdisable\" /></a>";
7843 $displayname = "<span>$name</span>";
7844 } else {
7845 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
7846 $hideshow .= "<img src=\"" . $OUTPUT->pix_url('t/show') . "\" class=\"iconsmall\" alt=\"$strenable\" /></a>";
7847 $displayname = "<span class=\"dimmed_text\">$name</span>";
7850 // delete link
7851 $uninstall = "<a href=\"$url&amp;action=uninstall&amp;webservice=$protocol\">$struninstall</a>";
7853 // settings link
7854 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
7855 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
7856 } else {
7857 $settings = '';
7860 // add a row to the table
7861 $table->data[] = array($displayname, $version, $hideshow, $uninstall, $settings);
7863 $return .= html_writer::table($table);
7864 $return .= get_string('configwebserviceplugins', 'webservice');
7865 $return .= $OUTPUT->box_end();
7867 return highlight($query, $return);
7873 * Special class for web service token administration.
7875 * @author Jerome Mouneyrac
7877 class admin_setting_managewebservicetokens extends admin_setting {
7880 * Calls parent::__construct with specific arguments
7882 public function __construct() {
7883 $this->nosave = true;
7884 parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
7888 * Always returns true, does nothing
7890 * @return true
7892 public function get_setting() {
7893 return true;
7897 * Always returns true, does nothing
7899 * @return true
7901 public function get_defaultsetting() {
7902 return true;
7906 * Always returns '', does not write anything
7908 * @return string Always returns ''
7910 public function write_setting($data) {
7911 // do not write any setting
7912 return '';
7916 * Builds the XHTML to display the control
7918 * @param string $data Unused
7919 * @param string $query
7920 * @return string
7922 public function output_html($data, $query='') {
7923 global $CFG, $OUTPUT, $DB, $USER;
7925 // display strings
7926 $stroperation = get_string('operation', 'webservice');
7927 $strtoken = get_string('token', 'webservice');
7928 $strservice = get_string('service', 'webservice');
7929 $struser = get_string('user');
7930 $strcontext = get_string('context', 'webservice');
7931 $strvaliduntil = get_string('validuntil', 'webservice');
7932 $striprestriction = get_string('iprestriction', 'webservice');
7934 $return = $OUTPUT->box_start('generalbox webservicestokenui');
7936 $table = new html_table();
7937 $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation);
7938 $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'centeralign');
7939 $table->id = 'webservicetokens';
7940 $table->attributes['class'] = 'admintable generaltable';
7941 $table->data = array();
7943 $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
7945 //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN
7947 //here retrieve token list (including linked users firstname/lastname and linked services name)
7948 $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid
7949 FROM {external_tokens} t, {user} u, {external_services} s
7950 WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id";
7951 $tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT));
7952 if (!empty($tokens)) {
7953 foreach ($tokens as $token) {
7954 //TODO: retrieve context
7956 $delete = "<a href=\"".$tokenpageurl."&amp;action=delete&amp;tokenid=".$token->id."\">";
7957 $delete .= get_string('delete')."</a>";
7959 $validuntil = '';
7960 if (!empty($token->validuntil)) {
7961 $validuntil = userdate($token->validuntil, get_string('strftimedatetime', 'langconfig'));
7964 $iprestriction = '';
7965 if (!empty($token->iprestriction)) {
7966 $iprestriction = $token->iprestriction;
7969 $userprofilurl = new moodle_url('/user/profile.php?id='.$token->userid);
7970 $useratag = html_writer::start_tag('a', array('href' => $userprofilurl));
7971 $useratag .= $token->firstname." ".$token->lastname;
7972 $useratag .= html_writer::end_tag('a');
7974 //check user missing capabilities
7975 require_once($CFG->dirroot . '/webservice/lib.php');
7976 $webservicemanager = new webservice();
7977 $usermissingcaps = $webservicemanager->get_missing_capabilities_by_users(
7978 array(array('id' => $token->userid)), $token->serviceid);
7980 if (!is_siteadmin($token->userid) and
7981 array_key_exists($token->userid, $usermissingcaps)) {
7982 $missingcapabilities = implode(', ',
7983 $usermissingcaps[$token->userid]);
7984 if (!empty($missingcapabilities)) {
7985 $useratag .= html_writer::tag('div',
7986 get_string('usermissingcaps', 'webservice',
7987 $missingcapabilities)
7988 . '&nbsp;' . $OUTPUT->help_icon('missingcaps', 'webservice'),
7989 array('class' => 'missingcaps'));
7993 $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete);
7996 $return .= html_writer::table($table);
7997 } else {
7998 $return .= get_string('notoken', 'webservice');
8001 $return .= $OUTPUT->box_end();
8002 // add a token to the table
8003 $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
8004 $return .= get_string('add')."</a>";
8006 return highlight($query, $return);
8012 * Colour picker
8014 * @copyright 2010 Sam Hemelryk
8015 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8017 class admin_setting_configcolourpicker extends admin_setting {
8020 * Information for previewing the colour
8022 * @var array|null
8024 protected $previewconfig = null;
8028 * @param string $name
8029 * @param string $visiblename
8030 * @param string $description
8031 * @param string $defaultsetting
8032 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
8034 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig=null) {
8035 $this->previewconfig = $previewconfig;
8036 parent::__construct($name, $visiblename, $description, $defaultsetting);
8040 * Return the setting
8042 * @return mixed returns config if successful else null
8044 public function get_setting() {
8045 return $this->config_read($this->name);
8049 * Saves the setting
8051 * @param string $data
8052 * @return bool
8054 public function write_setting($data) {
8055 $data = $this->validate($data);
8056 if ($data === false) {
8057 return get_string('validateerror', 'admin');
8059 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
8063 * Validates the colour that was entered by the user
8065 * @param string $data
8066 * @return string|false
8068 protected function validate($data) {
8070 * List of valid HTML colour names
8072 * @var array
8074 $colornames = array(
8075 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
8076 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
8077 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
8078 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
8079 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
8080 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
8081 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
8082 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
8083 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
8084 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
8085 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
8086 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
8087 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
8088 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
8089 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
8090 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
8091 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
8092 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
8093 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
8094 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
8095 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
8096 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
8097 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
8098 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
8099 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
8100 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
8101 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
8102 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
8103 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
8104 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
8105 'whitesmoke', 'yellow', 'yellowgreen'
8108 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
8109 if (strpos($data, '#')!==0) {
8110 $data = '#'.$data;
8112 return $data;
8113 } else if (in_array(strtolower($data), $colornames)) {
8114 return $data;
8115 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
8116 return $data;
8117 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
8118 return $data;
8119 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
8120 return $data;
8121 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
8122 return $data;
8123 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
8124 return $data;
8125 } else if (empty($data)) {
8126 return $this->defaultsetting;
8127 } else {
8128 return false;
8133 * Generates the HTML for the setting
8135 * @global moodle_page $PAGE
8136 * @global core_renderer $OUTPUT
8137 * @param string $data
8138 * @param string $query
8140 public function output_html($data, $query = '') {
8141 global $PAGE, $OUTPUT;
8142 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
8143 $content = html_writer::start_tag('div', array('class'=>'form-colourpicker defaultsnext'));
8144 $content .= html_writer::tag('div', $OUTPUT->pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', array('class'=>'loadingicon')), array('class'=>'admin_colourpicker clearfix'));
8145 $content .= html_writer::empty_tag('input', array('type'=>'text','id'=>$this->get_id(), 'name'=>$this->get_full_name(), 'value'=>$data, 'size'=>'12'));
8146 if (!empty($this->previewconfig)) {
8147 $content .= html_writer::empty_tag('input', array('type'=>'button','id'=>$this->get_id().'_preview', 'value'=>get_string('preview'), 'class'=>'admin_colourpicker_preview'));
8149 $content .= html_writer::end_tag('div');
8150 return format_admin_setting($this, $this->visiblename, $content, $this->description, false, '', $this->get_defaultsetting(), $query);
8156 * Class used for uploading of one file into file storage,
8157 * the file name is stored in config table.
8159 * Please note you need to implement your own '_pluginfile' callback function,
8160 * this setting only stores the file, it does not deal with file serving.
8162 * @copyright 2013 Petr Skoda {@link http://skodak.org}
8163 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8165 class admin_setting_configstoredfile extends admin_setting {
8166 /** @var array file area options - should be one file only */
8167 protected $options;
8168 /** @var string name of the file area */
8169 protected $filearea;
8170 /** @var int intemid */
8171 protected $itemid;
8172 /** @var string used for detection of changes */
8173 protected $oldhashes;
8176 * Create new stored file setting.
8178 * @param string $name low level setting name
8179 * @param string $visiblename human readable setting name
8180 * @param string $description description of setting
8181 * @param mixed $filearea file area for file storage
8182 * @param int $itemid itemid for file storage
8183 * @param array $options file area options
8185 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
8186 parent::__construct($name, $visiblename, $description, '');
8187 $this->filearea = $filearea;
8188 $this->itemid = $itemid;
8189 $this->options = (array)$options;
8193 * Applies defaults and returns all options.
8194 * @return array
8196 protected function get_options() {
8197 global $CFG;
8199 require_once("$CFG->libdir/filelib.php");
8200 require_once("$CFG->dirroot/repository/lib.php");
8201 $defaults = array(
8202 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
8203 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
8204 'context' => context_system::instance());
8205 foreach($this->options as $k => $v) {
8206 $defaults[$k] = $v;
8209 return $defaults;
8212 public function get_setting() {
8213 return $this->config_read($this->name);
8216 public function write_setting($data) {
8217 global $USER;
8219 // Let's not deal with validation here, this is for admins only.
8220 $current = $this->get_setting();
8221 if (empty($data)) {
8222 // Most probably applying default settings.
8223 if ($current === null) {
8224 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
8226 return '';
8227 } else if (!is_number($data)) {
8228 // Draft item id is expected here!
8229 return get_string('errorsetting', 'admin');
8232 $options = $this->get_options();
8233 $fs = get_file_storage();
8234 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8236 $this->oldhashes = null;
8237 if ($current) {
8238 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8239 if ($file = $fs->get_file_by_hash($hash)) {
8240 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
8242 unset($file);
8245 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
8246 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
8247 $usercontext = context_user::instance($USER->id);
8248 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.')) {
8249 return get_string('errorsetting', 'admin');
8253 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8254 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
8256 $filepath = '';
8257 if ($files) {
8258 /** @var stored_file $file */
8259 $file = reset($files);
8260 $filepath = $file->get_filepath().$file->get_filename();
8263 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
8266 public function post_write_settings($original) {
8267 $options = $this->get_options();
8268 $fs = get_file_storage();
8269 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8271 $current = $this->get_setting();
8272 $newhashes = null;
8273 if ($current) {
8274 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
8275 if ($file = $fs->get_file_by_hash($hash)) {
8276 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
8278 unset($file);
8281 if ($this->oldhashes === $newhashes) {
8282 $this->oldhashes = null;
8283 return false;
8285 $this->oldhashes = null;
8287 $callbackfunction = $this->updatedcallback;
8288 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
8289 $callbackfunction($this->get_full_name());
8291 return true;
8294 public function output_html($data, $query = '') {
8295 global $PAGE, $CFG;
8297 $options = $this->get_options();
8298 $id = $this->get_id();
8299 $elname = $this->get_full_name();
8300 $draftitemid = file_get_submitted_draft_itemid($elname);
8301 $component = is_null($this->plugin) ? 'core' : $this->plugin;
8302 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
8304 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
8305 require_once("$CFG->dirroot/lib/form/filemanager.php");
8307 $fmoptions = new stdClass();
8308 $fmoptions->mainfile = $options['mainfile'];
8309 $fmoptions->maxbytes = $options['maxbytes'];
8310 $fmoptions->maxfiles = $options['maxfiles'];
8311 $fmoptions->client_id = uniqid();
8312 $fmoptions->itemid = $draftitemid;
8313 $fmoptions->subdirs = $options['subdirs'];
8314 $fmoptions->target = $id;
8315 $fmoptions->accepted_types = $options['accepted_types'];
8316 $fmoptions->return_types = $options['return_types'];
8317 $fmoptions->context = $options['context'];
8318 $fmoptions->areamaxbytes = $options['areamaxbytes'];
8320 $fm = new form_filemanager($fmoptions);
8321 $output = $PAGE->get_renderer('core', 'files');
8322 $html = $output->render($fm);
8324 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
8325 $html .= '<input value="" id="'.$id.'" type="hidden" />';
8327 return format_admin_setting($this, $this->visiblename,
8328 '<div class="form-filemanager">'.$html.'</div>', $this->description, true, '', '', $query);
8334 * Administration interface for user specified regular expressions for device detection.
8336 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8338 class admin_setting_devicedetectregex extends admin_setting {
8341 * Calls parent::__construct with specific args
8343 * @param string $name
8344 * @param string $visiblename
8345 * @param string $description
8346 * @param mixed $defaultsetting
8348 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
8349 global $CFG;
8350 parent::__construct($name, $visiblename, $description, $defaultsetting);
8354 * Return the current setting(s)
8356 * @return array Current settings array
8358 public function get_setting() {
8359 global $CFG;
8361 $config = $this->config_read($this->name);
8362 if (is_null($config)) {
8363 return null;
8366 return $this->prepare_form_data($config);
8370 * Save selected settings
8372 * @param array $data Array of settings to save
8373 * @return bool
8375 public function write_setting($data) {
8376 if (empty($data)) {
8377 $data = array();
8380 if ($this->config_write($this->name, $this->process_form_data($data))) {
8381 return ''; // success
8382 } else {
8383 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
8388 * Return XHTML field(s) for regexes
8390 * @param array $data Array of options to set in HTML
8391 * @return string XHTML string for the fields and wrapping div(s)
8393 public function output_html($data, $query='') {
8394 global $OUTPUT;
8396 $out = html_writer::start_tag('table', array('border' => 1, 'class' => 'generaltable'));
8397 $out .= html_writer::start_tag('thead');
8398 $out .= html_writer::start_tag('tr');
8399 $out .= html_writer::tag('th', get_string('devicedetectregexexpression', 'admin'));
8400 $out .= html_writer::tag('th', get_string('devicedetectregexvalue', 'admin'));
8401 $out .= html_writer::end_tag('tr');
8402 $out .= html_writer::end_tag('thead');
8403 $out .= html_writer::start_tag('tbody');
8405 if (empty($data)) {
8406 $looplimit = 1;
8407 } else {
8408 $looplimit = (count($data)/2)+1;
8411 for ($i=0; $i<$looplimit; $i++) {
8412 $out .= html_writer::start_tag('tr');
8414 $expressionname = 'expression'.$i;
8416 if (!empty($data[$expressionname])){
8417 $expression = $data[$expressionname];
8418 } else {
8419 $expression = '';
8422 $out .= html_writer::tag('td',
8423 html_writer::empty_tag('input',
8424 array(
8425 'type' => 'text',
8426 'class' => 'form-text',
8427 'name' => $this->get_full_name().'[expression'.$i.']',
8428 'value' => $expression,
8430 ), array('class' => 'c'.$i)
8433 $valuename = 'value'.$i;
8435 if (!empty($data[$valuename])){
8436 $value = $data[$valuename];
8437 } else {
8438 $value= '';
8441 $out .= html_writer::tag('td',
8442 html_writer::empty_tag('input',
8443 array(
8444 'type' => 'text',
8445 'class' => 'form-text',
8446 'name' => $this->get_full_name().'[value'.$i.']',
8447 'value' => $value,
8449 ), array('class' => 'c'.$i)
8452 $out .= html_writer::end_tag('tr');
8455 $out .= html_writer::end_tag('tbody');
8456 $out .= html_writer::end_tag('table');
8458 return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', null, $query);
8462 * Converts the string of regexes
8464 * @see self::process_form_data()
8465 * @param $regexes string of regexes
8466 * @return array of form fields and their values
8468 protected function prepare_form_data($regexes) {
8470 $regexes = json_decode($regexes);
8472 $form = array();
8474 $i = 0;
8476 foreach ($regexes as $value => $regex) {
8477 $expressionname = 'expression'.$i;
8478 $valuename = 'value'.$i;
8480 $form[$expressionname] = $regex;
8481 $form[$valuename] = $value;
8482 $i++;
8485 return $form;
8489 * Converts the data from admin settings form into a string of regexes
8491 * @see self::prepare_form_data()
8492 * @param array $data array of admin form fields and values
8493 * @return false|string of regexes
8495 protected function process_form_data(array $form) {
8497 $count = count($form); // number of form field values
8499 if ($count % 2) {
8500 // we must get five fields per expression
8501 return false;
8504 $regexes = array();
8505 for ($i = 0; $i < $count / 2; $i++) {
8506 $expressionname = "expression".$i;
8507 $valuename = "value".$i;
8509 $expression = trim($form['expression'.$i]);
8510 $value = trim($form['value'.$i]);
8512 if (empty($expression)){
8513 continue;
8516 $regexes[$value] = $expression;
8519 $regexes = json_encode($regexes);
8521 return $regexes;
8526 * Multiselect for current modules
8528 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8530 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
8531 private $excludesystem;
8534 * Calls parent::__construct - note array $choices is not required
8536 * @param string $name setting name
8537 * @param string $visiblename localised setting name
8538 * @param string $description setting description
8539 * @param array $defaultsetting a plain array of default module ids
8540 * @param bool $excludesystem If true, excludes modules with 'system' archetype
8542 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
8543 $excludesystem = true) {
8544 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
8545 $this->excludesystem = $excludesystem;
8549 * Loads an array of current module choices
8551 * @return bool always return true
8553 public function load_choices() {
8554 if (is_array($this->choices)) {
8555 return true;
8557 $this->choices = array();
8559 global $CFG, $DB;
8560 $records = $DB->get_records('modules', array('visible'=>1), 'name');
8561 foreach ($records as $record) {
8562 // Exclude modules if the code doesn't exist
8563 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
8564 // Also exclude system modules (if specified)
8565 if (!($this->excludesystem &&
8566 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
8567 MOD_ARCHETYPE_SYSTEM)) {
8568 $this->choices[$record->id] = $record->name;
8572 return true;