MDL-17678 typo fix thanks to reporter Chardelle Busch
[moodle.git] / lib / adminlib.php
CommitLineData
db26acd4 1<?php
2
0c079f19 3// This file is part of Moodle - http://moodle.org/
4//
db26acd4 5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
0c079f19 14//
db26acd4 15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
88a7228a 17
18/**
0c079f19 19 * Functions and classes used during installation, upgrades and for admin settings.
db26acd4 20 *
0c079f19 21 * ADMIN SETTINGS TREE INTRODUCTION
db26acd4 22 *
23 * This file performs the following tasks:
24 * -it defines the necessary objects and interfaces to build the Moodle
25 * admin hierarchy
61ef8f9f 26 * -it defines the admin_externalpage_setup()
db26acd4 27 *
28 * ADMIN_SETTING OBJECTS
29 *
30 * Moodle settings are represented by objects that inherit from the admin_setting
31 * class. These objects encapsulate how to read a setting, how to write a new value
32 * to a setting, and how to appropriately display the HTML to modify the setting.
33 *
34 * ADMIN_SETTINGPAGE OBJECTS
35 *
36 * The admin_setting objects are then grouped into admin_settingpages. The latter
37 * appear in the Moodle admin tree block. All interaction with admin_settingpage
38 * objects is handled by the admin/settings.php file.
39 *
40 * ADMIN_EXTERNALPAGE OBJECTS
41 *
42 * There are some settings in Moodle that are too complex to (efficiently) handle
43 * with admin_settingpages. (Consider, for example, user management and displaying
44 * lists of users.) In this case, we use the admin_externalpage object. This object
45 * places a link to an external PHP file in the admin tree block.
46 *
47 * If you're using an admin_externalpage object for some settings, you can take
48 * advantage of the admin_externalpage_* functions. For example, suppose you wanted
49 * to add a foo.php file into admin. First off, you add the following line to
50 * admin/settings/first.php (at the end of the file) or to some other file in
51 * admin/settings:
52 * <code>
53 * $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
54 * $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
55 * </code>
56 *
57 * Next, in foo.php, your file structure would resemble the following:
58 * <code>
4d553cb2 59 * require(dirname(dirname(dirname(__FILE__))).'/config.php');
db26acd4 60 * require_once($CFG->libdir.'/adminlib.php');
61 * admin_externalpage_setup('foo');
62 * // functionality like processing form submissions goes here
4d553cb2 63 * echo $OUTPUT->header();
db26acd4 64 * // your HTML goes here
4d553cb2 65 * echo $OUTPUT->footer();
db26acd4 66 * </code>
67 *
68 * The admin_externalpage_setup() function call ensures the user is logged in,
69 * and makes sure that they have the proper role permission to access the page.
01a2ce80 70 * It also configures all $PAGE properties needed for navigation.
db26acd4 71 *
72 * ADMIN_CATEGORY OBJECTS
73 *
74 * Above and beyond all this, we have admin_category objects. These objects
75 * appear as folders in the admin tree block. They contain admin_settingpage's,
76 * admin_externalpage's, and other admin_category's.
77 *
78 * OTHER NOTES
79 *
80 * admin_settingpage's, admin_externalpage's, and admin_category's all inherit
81 * from part_of_admin_tree (a pseudointerface). This interface insists that
82 * a class has a check_access method for access permissions, a locate method
83 * used to find a specific node in the admin tree and find parent path.
84 *
85 * admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
86 * interface ensures that the class implements a recursive add function which
87 * accepts a part_of_admin_tree object and searches for the proper place to
88 * put it. parentable_part_of_admin_tree implies part_of_admin_tree.
89 *
90 * Please note that the $this->name field of any part_of_admin_tree must be
91 * UNIQUE throughout the ENTIRE admin tree.
92 *
93 * The $this->name field of an admin_setting object (which is *not* part_of_
94 * admin_tree) must be unique on the respective admin_settingpage where it is
95 * used.
96 *
0c079f19 97 * Original author: Vincenzo K. Marcovecchio
98 * Maintainer: Petr Skoda
db26acd4 99 *
78bfb562
PS
100 * @package core
101 * @subpackage admin
102 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
103 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
88a7228a 104 */
105
78bfb562
PS
106defined('MOODLE_INTERNAL') || die();
107
598b38f7 108/// Add libraries
109require_once($CFG->libdir.'/ddllib.php');
b1f93b15 110require_once($CFG->libdir.'/xmlize.php');
111
bba0beae 112define('INSECURE_DATAROOT_WARNING', 1);
113define('INSECURE_DATAROOT_ERROR', 2);
0c079f19 114
ed996ede 115/**
116 * Automatically clean-up all plugin data and remove the plugin DB tables
117 *
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
122 */
123function uninstall_plugin($type, $name) {
124 global $CFG, $DB, $OUTPUT;
125
846e4e17
PS
126 // recursively uninstall all module subplugins first
127 if ($type === 'mod') {
128 if (file_exists("$CFG->dirroot/$name/db/subplugins.php")) {
129 $subplugins = array();
130 include("$moddir/db/subplugins.php");
131 foreach ($subplugins as $subplugintype=>$dir) {
132 $instances = get_plugin_list($subplugintype);
133 foreach ($instances as $subpluginname => $notusedpluginpath) {
134 uninstall_plugin($subplugintype, $subpluginname);
135 }
ed996ede 136 }
137 }
846e4e17 138
ed996ede 139 }
140
2138244c 141 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
142
143 if ($type === 'mod') {
144 $pluginname = $name; // eg. 'forum'
70d35fe6
PS
145 if (get_string_manager()->string_exists('modulename', $component)) {
146 $strpluginname = get_string('modulename', $component);
147 } else {
148 $strpluginname = $component;
149 }
df997f84 150
2138244c 151 } else {
9baf6825 152 $pluginname = $component;
70d35fe6
PS
153 if (get_string_manager()->string_exists('pluginname', $component)) {
154 $strpluginname = get_string('pluginname', $component);
155 } else {
156 $strpluginname = $component;
157 }
2138244c 158 }
df997f84 159
ed996ede 160 echo $OUTPUT->heading($pluginname);
161
162 $plugindirectory = get_plugin_directory($type, $name);
163 $uninstalllib = $plugindirectory . '/db/uninstall.php';
164 if (file_exists($uninstalllib)) {
165 require_once($uninstalllib);
2138244c 166 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
ed996ede 167 if (function_exists($uninstallfunction)) {
2138244c 168 if (!$uninstallfunction()) {
ed996ede 169 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $pluginname);
170 }
171 }
172 }
173
df997f84 174 if ($type === 'mod') {
9baf6825 175 // perform cleanup tasks specific for activity modules
ed996ede 176
ed996ede 177 if (!$module = $DB->get_record('modules', array('name' => $name))) {
178 print_error('moduledoesnotexist', 'error');
179 }
180
181 // delete all the relevant instances from all course sections
182 if ($coursemods = $DB->get_records('course_modules', array('module' => $module->id))) {
183 foreach ($coursemods as $coursemod) {
184 if (!delete_mod_from_section($coursemod->id, $coursemod->section)) {
185 echo $OUTPUT->notification("Could not delete the $strpluginname with id = $coursemod->id from section $coursemod->section");
186 }
187 }
188 }
189
190 // clear course.modinfo for courses that used this module
191 $sql = "UPDATE {course}
192 SET modinfo=''
193 WHERE id IN (SELECT DISTINCT course
194 FROM {course_modules}
195 WHERE module=?)";
196 $DB->execute($sql, array($module->id));
197
198 // delete all the course module records
2138244c 199 $DB->delete_records('course_modules', array('module' => $module->id));
ed996ede 200
201 // delete module contexts
202 if ($coursemods) {
203 foreach ($coursemods as $coursemod) {
204 if (!delete_context(CONTEXT_MODULE, $coursemod->id)) {
205 echo $OUTPUT->notification("Could not delete the context for $strpluginname with id = $coursemod->id");
206 }
207 }
208 }
209
210 // delete the module entry itself
2138244c 211 $DB->delete_records('modules', array('name' => $module->name));
ed996ede 212
213 // cleanup the gradebook
214 require_once($CFG->libdir.'/gradelib.php');
215 grade_uninstalled_module($module->name);
216
217 // Perform any custom uninstall tasks
218 if (file_exists($CFG->dirroot . '/mod/' . $module->name . '/lib.php')) {
219 require_once($CFG->dirroot . '/mod/' . $module->name . '/lib.php');
220 $uninstallfunction = $module->name . '_uninstall';
221 if (function_exists($uninstallfunction)) {
222 debugging("{$uninstallfunction}() has been deprecated. Use the plugin's db/uninstall.php instead", DEBUG_DEVELOPER);
2138244c 223 if (!$uninstallfunction()) {
ed996ede 224 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $module->name.'!');
225 }
226 }
227 }
df997f84
PS
228
229 } else if ($type === 'enrol') {
230 // NOTE: this is a bit brute force way - it will not trigger events and hooks properly
231 // nuke all role assignments
232 role_unassign_all(array('component'=>$component));
233 // purge participants
234 $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($name));
235 // purge enrol instances
236 $DB->delete_records('enrol', array('enrol'=>$name));
237 // tweak enrol settings
238 if (!empty($CFG->enrol_plugins_enabled)) {
239 $enabledenrols = explode(',', $CFG->enrol_plugins_enabled);
240 $enabledenrols = array_unique($enabledenrols);
241 $enabledenrols = array_flip($enabledenrols);
242 unset($enabledenrols[$name]);
243 $enabledenrols = array_flip($enabledenrols);
244 if (is_array($enabledenrols)) {
245 set_config('enrol_plugins_enabled', implode(',', $enabledenrols));
246 }
247 }
ed996ede 248 }
249
70d35fe6 250 // perform clean-up task common for all the plugin/subplugin types
ed996ede 251
252 // delete calendar events
2138244c 253 $DB->delete_records('event', array('modulename' => $pluginname));
ed996ede 254
255 // delete all the logs
2138244c 256 $DB->delete_records('log', array('module' => $pluginname));
ed996ede 257
258 // delete log_display information
c6d75bff 259 $DB->delete_records('log_display', array('component' => $component));
ed996ede 260
261 // delete the module configuration records
2138244c 262 unset_all_config_for_plugin($pluginname);
ed996ede 263
264 // delete the plugin tables
265 $xmldbfilepath = $plugindirectory . '/db/install.xml';
266 drop_plugin_tables($pluginname, $xmldbfilepath, false);
267
ed996ede 268 // delete the capabilities that were defined by this module
269 capabilities_cleanup($component);
270
875e5f07 271 // remove event handlers and dequeue pending events
ed996ede 272 events_uninstall($component);
273
274 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
275}
276
b1f93b15 277/**
278 * Delete all plugin tables
db26acd4 279 *
db26acd4 280 * @param string $name Name of plugin, used as table prefix
281 * @param string $file Path to install.xml file
282 * @param bool $feedback defaults to true
0c079f19 283 * @return bool Always returns true
b1f93b15 284 */
285function drop_plugin_tables($name, $file, $feedback=true) {
286 global $CFG, $DB;
287
288 // first try normal delete
8b4ca8f7 289 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
b1f93b15 290 return true;
291 }
292
293 // then try to find all tables that start with name and are not in any xml file
294 $used_tables = get_used_table_names();
295
296 $tables = $DB->get_tables();
297
298 /// Iterate over, fixing id fields as necessary
299 foreach ($tables as $table) {
300 if (in_array($table, $used_tables)) {
301 continue;
302 }
303
8b4ca8f7 304 if (strpos($table, $name) !== 0) {
305 continue;
306 }
307
b1f93b15 308 // found orphan table --> delete it
309 if ($DB->get_manager()->table_exists($table)) {
310 $xmldb_table = new xmldb_table($table);
eee5d9bb 311 $DB->get_manager()->drop_table($xmldb_table);
b1f93b15 312 }
313 }
314
315 return true;
316}
317
318/**
875e5f07 319 * Returns names of all known tables == tables that moodle knows about.
db26acd4 320 *
321 * @return array Array of lowercase table names
b1f93b15 322 */
323function get_used_table_names() {
324 $table_names = array();
325 $dbdirs = get_db_directories();
326
327 foreach ($dbdirs as $dbdir) {
328 $file = $dbdir.'/install.xml';
329
330 $xmldb_file = new xmldb_file($file);
331
332 if (!$xmldb_file->fileExists()) {
333 continue;
334 }
335
336 $loaded = $xmldb_file->loadXMLStructure();
73fa96d5 337 $structure = $xmldb_file->getStructure();
b1f93b15 338
339 if ($loaded and $tables = $structure->getTables()) {
340 foreach($tables as $table) {
341 $table_names[] = strtolower($table->name);
342 }
343 }
344 }
345
346 return $table_names;
347}
348
349/**
350 * Returns list of all directories where we expect install.xml files
db26acd4 351 * @return array Array of paths
b1f93b15 352 */
353function get_db_directories() {
354 global $CFG;
355
356 $dbdirs = array();
357
9baf6825 358 /// First, the main one (lib/db)
b1f93b15 359 $dbdirs[] = $CFG->libdir.'/db';
360
9baf6825 361 /// Then, all the ones defined by get_plugin_types()
17da2e6f 362 $plugintypes = get_plugin_types();
363 foreach ($plugintypes as $plugintype => $pluginbasedir) {
364 if ($plugins = get_plugin_list($plugintype)) {
365 foreach ($plugins as $plugin => $plugindir) {
366 $dbdirs[] = $plugindir.'/db';
76b6c644 367 }
b91b274b 368 }
7cdd8b22 369 }
b1f93b15 370
b1f93b15 371 return $dbdirs;
372}
373
eef868d1 374/**
61460dd6 375 * Try to obtain or release the cron lock.
61460dd6 376 * @param string $name name of lock
875e5f07
PS
377 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
378 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
61460dd6 379 * @return bool true if lock obtained
f3221af9 380 */
61460dd6 381function set_cron_lock($name, $until, $ignorecurrent=false) {
f33e1ed4 382 global $DB;
f3221af9 383 if (empty($name)) {
61460dd6 384 debugging("Tried to get a cron lock for a null fieldname");
f3221af9 385 return false;
386 }
387
61460dd6 388 // remove lock by force == remove from config table
389 if (is_null($until)) {
390 set_config($name, null);
f3221af9 391 return true;
392 }
393
61460dd6 394 if (!$ignorecurrent) {
9baf6825 395 // read value from db - other processes might have changed it
f33e1ed4 396 $value = $DB->get_field('config', 'value', array('name'=>$name));
61460dd6 397
398 if ($value and $value > time()) {
9baf6825 399 //lock active
61460dd6 400 return false;
f3221af9 401 }
402 }
61460dd6 403
404 set_config($name, $until);
f3221af9 405 return true;
406}
a597f8a8 407
bba0beae 408/**
409 * Test if and critical warnings are present
410 * @return bool
411 */
412function admin_critical_warnings_present() {
413 global $SESSION;
414
415 if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
416 return 0;
417 }
418
419 if (!isset($SESSION->admin_critical_warning)) {
420 $SESSION->admin_critical_warning = 0;
fbf2c91e 421 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
bba0beae 422 $SESSION->admin_critical_warning = 1;
423 }
424 }
425
426 return $SESSION->admin_critical_warning;
427}
428
61f9c4b4 429/**
db26acd4 430 * Detects if float supports at least 10 decimal digits
431 *
875e5f07 432 * Detects if float supports at least 10 decimal digits
61f9c4b4 433 * and also if float-->string conversion works as expected.
db26acd4 434 *
61f9c4b4 435 * @return bool true if problem found
436 */
437function is_float_problem() {
438 $num1 = 2009010200.01;
439 $num2 = 2009010200.02;
440
441 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
442}
443
57e35f32 444/**
445 * Try to verify that dataroot is not accessible from web.
57e35f32 446 *
db26acd4 447 * Try to verify that dataroot is not accessible from web.
448 * It is not 100% correct but might help to reduce number of vulnerable sites.
57e35f32 449 * Protection from httpd.conf and .htaccess is not detected properly.
0c079f19 450 *
db26acd4 451 * @uses INSECURE_DATAROOT_WARNING
452 * @uses INSECURE_DATAROOT_ERROR
453 * @param bool $fetchtest try to test public access by fetching file, default false
875e5f07 454 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
57e35f32 455 */
bba0beae 456function is_dataroot_insecure($fetchtest=false) {
57e35f32 457 global $CFG;
458
459 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
460
461 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
462 $rp = strrev(trim($rp, '/'));
463 $rp = explode('/', $rp);
464 foreach($rp as $r) {
465 if (strpos($siteroot, '/'.$r.'/') === 0) {
466 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
467 } else {
468 break; // probably alias root
469 }
470 }
471
472 $siteroot = strrev($siteroot);
473 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
474
bba0beae 475 if (strpos($dataroot, $siteroot) !== 0) {
476 return false;
477 }
478
479 if (!$fetchtest) {
480 return INSECURE_DATAROOT_WARNING;
481 }
482
483 // now try all methods to fetch a test file using http protocol
484
485 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
486 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
487 $httpdocroot = $matches[1];
488 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
489 if (make_upload_directory('diag', false) === false) {
490 return INSECURE_DATAROOT_WARNING;
491 }
492 $testfile = $CFG->dataroot.'/diag/public.txt';
493 if (!file_exists($testfile)) {
494 file_put_contents($testfile, 'test file, do not delete');
495 }
496 $teststr = trim(file_get_contents($testfile));
497 if (empty($teststr)) {
9baf6825 498 // hmm, strange
bba0beae 499 return INSECURE_DATAROOT_WARNING;
500 }
501
502 $testurl = $datarooturl.'/diag/public.txt';
041a4b0f 503 if (extension_loaded('curl') and
504 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
505 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
506 ($ch = @curl_init($testurl)) !== false) {
bba0beae 507 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
508 curl_setopt($ch, CURLOPT_HEADER, false);
509 $data = curl_exec($ch);
510 if (!curl_errno($ch)) {
511 $data = trim($data);
512 if ($data === $teststr) {
513 curl_close($ch);
514 return INSECURE_DATAROOT_ERROR;
515 }
516 }
517 curl_close($ch);
518 }
519
520 if ($data = @file_get_contents($testurl)) {
521 $data = trim($data);
522 if ($data === $teststr) {
523 return INSECURE_DATAROOT_ERROR;
524 }
525 }
526
527 preg_match('|https?://([^/]+)|i', $testurl, $matches);
528 $sitename = $matches[1];
529 $error = 0;
530 if ($fp = @fsockopen($sitename, 80, $error)) {
531 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
532 $localurl = $matches[1];
533 $out = "GET $localurl HTTP/1.1\r\n";
534 $out .= "Host: $sitename\r\n";
535 $out .= "Connection: Close\r\n\r\n";
536 fwrite($fp, $out);
537 $data = '';
538 $incoming = false;
539 while (!feof($fp)) {
540 if ($incoming) {
541 $data .= fgets($fp, 1024);
542 } else if (@fgets($fp, 1024) === "\r\n") {
9baf6825 543 $incoming = true;
544 }
bba0beae 545 }
546 fclose($fp);
547 $data = trim($data);
548 if ($data === $teststr) {
549 return INSECURE_DATAROOT_ERROR;
550 }
57e35f32 551 }
bba0beae 552
553 return INSECURE_DATAROOT_WARNING;
57e35f32 554}
6e4dc10f 555
6e4dc10f 556/// CLASS DEFINITIONS /////////////////////////////////////////////////////////
557
558/**
875e5f07 559 * Interface for anything appearing in the admin tree
6e4dc10f 560 *
875e5f07 561 * The interface that is implemented by anything that appears in the admin tree
6e4dc10f 562 * block. It forces inheriting classes to define a method for checking user permissions
563 * and methods for finding something in the admin tree.
564 *
db26acd4 565 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 566 */
73fa96d5 567interface part_of_admin_tree {
6e4dc10f 568
9baf6825 569/**
570 * Finds a named part_of_admin_tree.
571 *
572 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
573 * and not parentable_part_of_admin_tree, then this function should only check if
574 * $this->name matches $name. If it does, it should return a reference to $this,
575 * otherwise, it should return a reference to NULL.
576 *
577 * If a class inherits parentable_part_of_admin_tree, this method should be called
578 * recursively on all child objects (assuming, of course, the parent object's name
579 * doesn't match the search criterion).
580 *
581 * @param string $name The internal name of the part_of_admin_tree we're searching for.
582 * @return mixed An object reference or a NULL reference.
583 */
73fa96d5 584 public function locate($name);
4672d955 585
586 /**
587 * Removes named part_of_admin_tree.
588 *
589 * @param string $name The internal name of the part_of_admin_tree we want to remove.
a8a66c96 590 * @return bool success.
4672d955 591 */
73fa96d5 592 public function prune($name);
4672d955 593
220a90c5 594 /**
595 * Search using query
db26acd4 596 * @param string $query
220a90c5 597 * @return mixed array-object structure of found settings and pages
598 */
73fa96d5 599 public function search($query);
220a90c5 600
6e4dc10f 601 /**
602 * Verifies current user's access to this part_of_admin_tree.
603 *
604 * Used to check if the current user has access to this part of the admin tree or
605 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
606 * then this method is usually just a call to has_capability() in the site context.
607 *
608 * If a class inherits parentable_part_of_admin_tree, this method should return the
609 * logical OR of the return of check_access() on all child objects.
610 *
611 * @return bool True if the user has access, false if she doesn't.
612 */
73fa96d5 613 public function check_access();
eef868d1 614
a8a66c96 615 /**
875e5f07 616 * Mostly useful for removing of some parts of the tree in admin tree block.
a8a66c96 617 *
618 * @return True is hidden from normal list view
619 */
73fa96d5 620 public function is_hidden();
427649bf
PS
621
622 /**
623 * Show we display Save button at the page bottom?
624 * @return bool
625 */
626 public function show_save();
6e4dc10f 627}
628
629/**
875e5f07 630 * Interface implemented by any part_of_admin_tree that has children.
6e4dc10f 631 *
875e5f07 632 * The interface implemented by any part_of_admin_tree that can be a parent
6e4dc10f 633 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
eef868d1 634 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
6e4dc10f 635 * include an add method for adding other part_of_admin_tree objects as children.
636 *
db26acd4 637 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 638 */
73fa96d5 639interface parentable_part_of_admin_tree extends part_of_admin_tree {
eef868d1 640
9baf6825 641/**
642 * Adds a part_of_admin_tree object to the admin tree.
643 *
644 * Used to add a part_of_admin_tree object to this object or a child of this
645 * object. $something should only be added if $destinationname matches
646 * $this->name. If it doesn't, add should be called on child objects that are
647 * also parentable_part_of_admin_tree's.
648 *
649 * @param string $destinationname The internal name of the new parent for $something.
650 * @param part_of_admin_tree $something The object to be added.
651 * @return bool True on success, false on failure.
652 */
73fa96d5 653 public function add($destinationname, $something);
eef868d1 654
6e4dc10f 655}
656
657/**
658 * The object used to represent folders (a.k.a. categories) in the admin tree block.
eef868d1 659 *
6e4dc10f 660 * Each admin_category object contains a number of part_of_admin_tree objects.
661 *
db26acd4 662 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 663 */
73fa96d5 664class admin_category implements parentable_part_of_admin_tree {
6e4dc10f 665
9baf6825 666/** @var mixed An array of part_of_admin_tree objects that are this object's children */
73fa96d5 667 public $children;
0c079f19 668 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 669 public $name;
0c079f19 670 /** @var string The displayed name for this category. Usually obtained through get_string() */
73fa96d5 671 public $visiblename;
0c079f19 672 /** @var bool Should this category be hidden in admin tree block? */
73fa96d5 673 public $hidden;
0c079f19 674 /** @var mixed Either a string or an array or strings */
73fa96d5 675 public $path;
0c079f19 676 /** @var mixed Either a string or an array or strings */
73fa96d5 677 public $visiblepath;
6e4dc10f 678
679 /**
680 * Constructor for an empty admin category
681 *
682 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
683 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
db26acd4 684 * @param bool $hidden hide category in admin tree block, defaults to false
6e4dc10f 685 */
73fa96d5 686 public function __construct($name, $visiblename, $hidden=false) {
220a90c5 687 $this->children = array();
688 $this->name = $name;
6e4dc10f 689 $this->visiblename = $visiblename;
220a90c5 690 $this->hidden = $hidden;
6e4dc10f 691 }
eef868d1 692
6e4dc10f 693 /**
220a90c5 694 * Returns a reference to the part_of_admin_tree object with internal name $name.
6e4dc10f 695 *
220a90c5 696 * @param string $name The internal name of the object we want.
697 * @param bool $findpath initialize path and visiblepath arrays
0c079f19 698 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
db26acd4 699 * defaults to false
6e4dc10f 700 */
73fa96d5 701 public function locate($name, $findpath=false) {
6e4dc10f 702 if ($this->name == $name) {
220a90c5 703 if ($findpath) {
704 $this->visiblepath[] = $this->visiblename;
705 $this->path[] = $this->name;
706 }
707 return $this;
6e4dc10f 708 }
eef868d1 709
220a90c5 710 $return = NULL;
711 foreach($this->children as $childid=>$unused) {
73fa96d5 712 if ($return = $this->children[$childid]->locate($name, $findpath)) {
220a90c5 713 break;
6e4dc10f 714 }
715 }
eef868d1 716
220a90c5 717 if (!is_null($return) and $findpath) {
718 $return->visiblepath[] = $this->visiblename;
719 $return->path[] = $this->name;
720 }
eef868d1 721
220a90c5 722 return $return;
6e4dc10f 723 }
724
725 /**
220a90c5 726 * Search using query
db26acd4 727 *
728 * @param string query
220a90c5 729 * @return mixed array-object structure of found settings and pages
6e4dc10f 730 */
73fa96d5 731 public function search($query) {
220a90c5 732 $result = array();
733 foreach ($this->children as $child) {
3cea9c55 734 $subsearch = $child->search($query);
735 if (!is_array($subsearch)) {
736 debugging('Incorrect search result from '.$child->name);
737 continue;
738 }
739 $result = array_merge($result, $subsearch);
6e4dc10f 740 }
220a90c5 741 return $result;
6e4dc10f 742 }
743
4672d955 744 /**
745 * Removes part_of_admin_tree object with internal name $name.
746 *
747 * @param string $name The internal name of the object we want to remove.
a8a66c96 748 * @return bool success
4672d955 749 */
73fa96d5 750 public function prune($name) {
4672d955 751
752 if ($this->name == $name) {
753 return false; //can not remove itself
754 }
755
756 foreach($this->children as $precedence => $child) {
757 if ($child->name == $name) {
9baf6825 758 // found it!
eef868d1 759 unset($this->children[$precedence]);
4672d955 760 return true;
761 }
762 if ($this->children[$precedence]->prune($name)) {
763 return true;
764 }
765 }
766 return false;
767 }
768
6e4dc10f 769 /**
770 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
771 *
220a90c5 772 * @param string $destinationame The internal name of the immediate parent that we want for $something.
875e5f07 773 * @param mixed $something A part_of_admin_tree or setting instance to be added.
220a90c5 774 * @return bool True if successfully added, false if $something can not be added.
6e4dc10f 775 */
73fa96d5 776 public function add($parentname, $something) {
777 $parent = $this->locate($parentname);
220a90c5 778 if (is_null($parent)) {
779 debugging('parent does not exist!');
6e4dc10f 780 return false;
781 }
782
73fa96d5 783 if ($something instanceof part_of_admin_tree) {
784 if (!($parent instanceof parentable_part_of_admin_tree)) {
220a90c5 785 debugging('error - parts of tree can be inserted only into parentable parts');
786 return false;
6e4dc10f 787 }
220a90c5 788 $parent->children[] = $something;
6e4dc10f 789 return true;
eef868d1 790
220a90c5 791 } else {
792 debugging('error - can not add this element');
793 return false;
6e4dc10f 794 }
eef868d1 795
6e4dc10f 796 }
eef868d1 797
6e4dc10f 798 /**
799 * Checks if the user has access to anything in this category.
800 *
875e5f07 801 * @return bool True if the user has access to at least one child in this category, false otherwise.
6e4dc10f 802 */
73fa96d5 803 public function check_access() {
6e4dc10f 804 foreach ($this->children as $child) {
220a90c5 805 if ($child->check_access()) {
806 return true;
807 }
6e4dc10f 808 }
220a90c5 809 return false;
6e4dc10f 810 }
eef868d1 811
a8a66c96 812 /**
813 * Is this category hidden in admin tree block?
814 *
815 * @return bool True if hidden
816 */
73fa96d5 817 public function is_hidden() {
a8a66c96 818 return $this->hidden;
819 }
427649bf
PS
820
821 /**
822 * Show we display Save button at the page bottom?
823 * @return bool
824 */
825 public function show_save() {
826 foreach ($this->children as $child) {
827 if ($child->show_save()) {
828 return true;
829 }
830 }
831 return false;
832 }
6e4dc10f 833}
834
db26acd4 835/**
0c079f19 836 * Root of admin settings tree, does not have any parent.
db26acd4 837 *
838 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 839 */
220a90c5 840class admin_root extends admin_category {
9baf6825 841/** @var array List of errors */
73fa96d5 842 public $errors;
0c079f19 843 /** @var string search query */
73fa96d5 844 public $search;
875e5f07 845 /** @var bool full tree flag - true means all settings required, false only pages required */
73fa96d5 846 public $fulltree;
0c079f19 847 /** @var bool flag indicating loaded tree */
73fa96d5 848 public $loaded;
875e5f07 849 /** @var mixed site custom defaults overriding defaults in settings files*/
cd3acbf2 850 public $custom_defaults;
851
db26acd4 852 /**
0c079f19 853 * @param bool $fulltree true means all settings required,
db26acd4 854 * false only pages required
855 */
73fa96d5 856 public function __construct($fulltree) {
cd3acbf2 857 global $CFG;
858
73fa96d5 859 parent::__construct('root', get_string('administration'), false);
220a90c5 860 $this->errors = array();
861 $this->search = '';
73fa96d5 862 $this->fulltree = $fulltree;
863 $this->loaded = false;
cd3acbf2 864
865 // load custom defaults if found
866 $this->custom_defaults = null;
867 $defaultsfile = "$CFG->dirroot/local/defaults.php";
868 if (is_readable($defaultsfile)) {
869 $defaults = array();
870 include($defaultsfile);
871 if (is_array($defaults) and count($defaults)) {
872 $this->custom_defaults = $defaults;
873 }
874 }
73fa96d5 875 }
876
db26acd4 877 /**
878 * Empties children array, and sets loaded to false
879 *
880 * @param bool $requirefulltree
881 */
73fa96d5 882 public function purge_children($requirefulltree) {
883 $this->children = array();
884 $this->fulltree = ($requirefulltree || $this->fulltree);
885 $this->loaded = false;
220a90c5 886 }
887}
888
6e4dc10f 889/**
890 * Links external PHP pages into the admin tree.
891 *
892 * See detailed usage example at the top of this document (adminlib.php)
893 *
db26acd4 894 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 895 */
73fa96d5 896class admin_externalpage implements part_of_admin_tree {
6e4dc10f 897
9baf6825 898/** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 899 public $name;
eef868d1 900
0c079f19 901 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
73fa96d5 902 public $visiblename;
eef868d1 903
0c079f19 904 /** @var string The external URL that we should link to when someone requests this external page. */
73fa96d5 905 public $url;
eef868d1 906
0c079f19 907 /** @var string The role capability/permission a user must have to access this external page. */
73fa96d5 908 public $req_capability;
eef868d1 909
0c079f19 910 /** @var object The context in which capability/permission should be checked, default is site context. */
73fa96d5 911 public $context;
84c8ede0 912
0c079f19 913 /** @var bool hidden in admin tree block. */
73fa96d5 914 public $hidden;
a8a66c96 915
0c079f19 916 /** @var mixed either string or array of string */
73fa96d5 917 public $path;
918 public $visiblepath;
220a90c5 919
6e4dc10f 920 /**
921 * Constructor for adding an external page into the admin tree.
922 *
923 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
924 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
925 * @param string $url The external URL that we should link to when someone requests this external page.
38d2d43b 926 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
92f00846 927 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
928 * @param context $context The context the page relates to. Not sure what happens
929 * if you specify something other than system or front page. Defaults to system.
6e4dc10f 930 */
73fa96d5 931 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
220a90c5 932 $this->name = $name;
6e4dc10f 933 $this->visiblename = $visiblename;
220a90c5 934 $this->url = $url;
38d2d43b 935 if (is_array($req_capability)) {
936 $this->req_capability = $req_capability;
937 } else {
938 $this->req_capability = array($req_capability);
939 }
92f00846 940 $this->hidden = $hidden;
84c8ede0 941 $this->context = $context;
6e4dc10f 942 }
eef868d1 943
6e4dc10f 944 /**
945 * Returns a reference to the part_of_admin_tree object with internal name $name.
946 *
947 * @param string $name The internal name of the object we want.
db26acd4 948 * @param bool $findpath defaults to false
6e4dc10f 949 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
950 */
73fa96d5 951 public function locate($name, $findpath=false) {
220a90c5 952 if ($this->name == $name) {
953 if ($findpath) {
954 $this->visiblepath = array($this->visiblename);
955 $this->path = array($this->name);
956 }
957 return $this;
958 } else {
959 $return = NULL;
960 return $return;
961 }
6e4dc10f 962 }
4672d955 963
db26acd4 964 /**
965 * This function always returns false, required function by interface
966 *
967 * @param string $name
968 * @return false
969 */
73fa96d5 970 public function prune($name) {
4672d955 971 return false;
972 }
973
220a90c5 974 /**
975 * Search using query
db26acd4 976 *
977 * @param string $query
220a90c5 978 * @return mixed array-object structure of found settings and pages
979 */
73fa96d5 980 public function search($query) {
220a90c5 981 $textlib = textlib_get_instance();
982
983 $found = false;
984 if (strpos(strtolower($this->name), $query) !== false) {
985 $found = true;
986 } else if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
9baf6825 987 $found = true;
988 }
220a90c5 989 if ($found) {
990 $result = new object();
991 $result->page = $this;
992 $result->settings = array();
993 return array($this->name => $result);
994 } else {
995 return array();
996 }
997 }
998
6e4dc10f 999 /**
2ce38b70 1000 * Determines if the current user has access to this external page based on $this->req_capability.
db26acd4 1001 *
6e4dc10f 1002 * @return bool True if user has access, false otherwise.
1003 */
73fa96d5 1004 public function check_access() {
1caea91e 1005 global $CFG;
84c8ede0 1006 $context = empty($this->context) ? get_context_instance(CONTEXT_SYSTEM) : $this->context;
38d2d43b 1007 foreach($this->req_capability as $cap) {
4f0c2d00 1008 if (has_capability($cap, $context)) {
38d2d43b 1009 return true;
1010 }
1011 }
1012 return false;
6e4dc10f 1013 }
1014
a8a66c96 1015 /**
1016 * Is this external page hidden in admin tree block?
1017 *
1018 * @return bool True if hidden
1019 */
73fa96d5 1020 public function is_hidden() {
a8a66c96 1021 return $this->hidden;
1022 }
1023
427649bf
PS
1024 /**
1025 * Show we display Save button at the page bottom?
1026 * @return bool
1027 */
1028 public function show_save() {
1029 return false;
1030 }
6e4dc10f 1031}
1032
1033/**
1034 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1035 *
db26acd4 1036 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 1037 */
73fa96d5 1038class admin_settingpage implements part_of_admin_tree {
6e4dc10f 1039
9baf6825 1040/** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 1041 public $name;
eef868d1 1042
0c079f19 1043 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
73fa96d5 1044 public $visiblename;
0c079f19 1045
1046 /** @var mixed An array of admin_setting objects that are part of this setting page. */
73fa96d5 1047 public $settings;
eef868d1 1048
0c079f19 1049 /** @var string The role capability/permission a user must have to access this external page. */
73fa96d5 1050 public $req_capability;
eef868d1 1051
0c079f19 1052 /** @var object The context in which capability/permission should be checked, default is site context. */
73fa96d5 1053 public $context;
84c8ede0 1054
0c079f19 1055 /** @var bool hidden in admin tree block. */
73fa96d5 1056 public $hidden;
a8a66c96 1057
0c079f19 1058 /** @var mixed string of paths or array of strings of paths */
73fa96d5 1059 public $path;
1060 public $visiblepath;
220a90c5 1061
db26acd4 1062 /**
1063 * see admin_settingpage for details of this function
0c079f19 1064 *
db26acd4 1065 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1066 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1067 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1068 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1069 * @param context $context The context the page relates to. Not sure what happens
1070 * if you specify something other than system or front page. Defaults to system.
1071 */
73fa96d5 1072 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
220a90c5 1073 $this->settings = new object();
1074 $this->name = $name;
1075 $this->visiblename = $visiblename;
1076 if (is_array($req_capability)) {
1077 $this->req_capability = $req_capability;
6e4dc10f 1078 } else {
220a90c5 1079 $this->req_capability = array($req_capability);
6e4dc10f 1080 }
220a90c5 1081 $this->hidden = $hidden;
1082 $this->context = $context;
6e4dc10f 1083 }
eef868d1 1084
0c079f19 1085 /**
db26acd4 1086 * see admin_category
1087 *
1088 * @param string $name
1089 * @param bool $findpath
1090 * @return mixed Object (this) if name == this->name, else returns null
1091 */
73fa96d5 1092 public function locate($name, $findpath=false) {
220a90c5 1093 if ($this->name == $name) {
1094 if ($findpath) {
1095 $this->visiblepath = array($this->visiblename);
1096 $this->path = array($this->name);
1097 }
1098 return $this;
1099 } else {
1100 $return = NULL;
1101 return $return;
1102 }
6e4dc10f 1103 }
4672d955 1104
0c079f19 1105 /**
1106 * Search string in settings page.
1107 *
db26acd4 1108 * @param string $query
1109 * @return array
1110 */
73fa96d5 1111 public function search($query) {
220a90c5 1112 $found = array();
4672d955 1113
220a90c5 1114 foreach ($this->settings as $setting) {
1115 if ($setting->is_related($query)) {
1116 $found[] = $setting;
1117 }
1118 }
1119
1120 if ($found) {
1121 $result = new object();
1122 $result->page = $this;
1123 $result->settings = $found;
1124 return array($this->name => $result);
1125 }
1126
1127 $textlib = textlib_get_instance();
1128
1129 $found = false;
1130 if (strpos(strtolower($this->name), $query) !== false) {
1131 $found = true;
1132 } else if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
9baf6825 1133 $found = true;
1134 }
220a90c5 1135 if ($found) {
1136 $result = new object();
1137 $result->page = $this;
1138 $result->settings = array();
1139 return array($this->name => $result);
38d2d43b 1140 } else {
220a90c5 1141 return array();
38d2d43b 1142 }
6e4dc10f 1143 }
eef868d1 1144
db26acd4 1145 /**
1146 * This function always returns false, required by interface
1147 *
1148 * @param string $name
1149 * @return bool Always false
1150 */
73fa96d5 1151 public function prune($name) {
6e4dc10f 1152 return false;
1153 }
eef868d1 1154
220a90c5 1155 /**
db26acd4 1156 * adds an admin_setting to this admin_settingpage
1157 *
220a90c5 1158 * 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
1159 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
db26acd4 1160 *
220a90c5 1161 * @param object $setting is the admin_setting object you want to add
db26acd4 1162 * @return bool true if successful, false if not
220a90c5 1163 */
73fa96d5 1164 public function add($setting) {
1165 if (!($setting instanceof admin_setting)) {
220a90c5 1166 debugging('error - not a setting instance');
1167 return false;
1168 }
1169
1170 $this->settings->{$setting->name} = $setting;
1171 return true;
1172 }
1173
db26acd4 1174 /**
1175 * see admin_externalpage
1176 *
1177 * @return bool Returns true for yes false for no
1178 */
73fa96d5 1179 public function check_access() {
1caea91e 1180 global $CFG;
84c8ede0 1181 $context = empty($this->context) ? get_context_instance(CONTEXT_SYSTEM) : $this->context;
38d2d43b 1182 foreach($this->req_capability as $cap) {
4f0c2d00 1183 if (has_capability($cap, $context)) {
38d2d43b 1184 return true;
1185 }
1186 }
1187 return false;
6e4dc10f 1188 }
eef868d1 1189
220a90c5 1190 /**
1191 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
db26acd4 1192 * @return string Returns an XHTML string
220a90c5 1193 */
73fa96d5 1194 public function output_html() {
1195 $adminroot = admin_get_root();
220a90c5 1196 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
6e4dc10f 1197 foreach($this->settings as $setting) {
220a90c5 1198 $fullname = $setting->get_full_name();
1199 if (array_key_exists($fullname, $adminroot->errors)) {
1200 $data = $adminroot->errors[$fullname]->data;
6e4dc10f 1201 } else {
220a90c5 1202 $data = $setting->get_setting();
79698344 1203 // do not use defaults if settings not available - upgrade settings handles the defaults!
6e4dc10f 1204 }
220a90c5 1205 $return .= $setting->output_html($data);
6e4dc10f 1206 }
220a90c5 1207 $return .= '</fieldset>';
6e4dc10f 1208 return $return;
1209 }
1210
a8a66c96 1211 /**
875e5f07 1212 * Is this settings page hidden in admin tree block?
a8a66c96 1213 *
1214 * @return bool True if hidden
1215 */
73fa96d5 1216 public function is_hidden() {
a8a66c96 1217 return $this->hidden;
1218 }
1219
427649bf
PS
1220 /**
1221 * Show we display Save button at the page bottom?
1222 * @return bool
1223 */
1224 public function show_save() {
1225 foreach($this->settings as $setting) {
1226 if (empty($setting->nosave)) {
1227 return true;
1228 }
1229 }
1230 return false;
1231 }
6e4dc10f 1232}
1233
1234
220a90c5 1235/**
1236 * Admin settings class. Only exists on setting pages.
1237 * Read & write happens at this level; no authentication.
db26acd4 1238 *
1239 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1240 */
301bf0b2 1241abstract class admin_setting {
9baf6825 1242/** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
73fa96d5 1243 public $name;
0c079f19 1244 /** @var string localised name */
73fa96d5 1245 public $visiblename;
3fa37159 1246 /** @var string localised long description in Markdown format */
73fa96d5 1247 public $description;
0c079f19 1248 /** @var mixed Can be string or array of string */
73fa96d5 1249 public $defaultsetting;
0c079f19 1250 /** @var string */
73fa96d5 1251 public $updatedcallback;
0c079f19 1252 /** @var mixed can be String or Null. Null means main config table */
73fa96d5 1253 public $plugin; // null means main config table
427649bf
PS
1254 /** @var bool true indicates this setting does not actually save anything, just information */
1255 public $nosave = false;
6e4dc10f 1256
220a90c5 1257 /**
1258 * Constructor
0c079f19 1259 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
db26acd4 1260 * or 'myplugin/mysetting' for ones in config_plugins.
1a41e806 1261 * @param string $visiblename localised name
1262 * @param string $description localised long description
220a90c5 1263 * @param mixed $defaultsetting string or array depending on implementation
1264 */
73fa96d5 1265 public function __construct($name, $visiblename, $description, $defaultsetting) {
7fb0303d 1266 $this->parse_setting_name($name);
220a90c5 1267 $this->visiblename = $visiblename;
3fa37159 1268 $this->description = markdown_to_html($description);
6e4dc10f 1269 $this->defaultsetting = $defaultsetting;
1270 }
eef868d1 1271
7fb0303d 1272 /**
db26acd4 1273 * Set up $this->name and potentially $this->plugin
1274 *
7fb0303d 1275 * Set up $this->name and possibly $this->plugin based on whether $name looks
1276 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1277 * on the names, that is, output a developer debug warning if the name
1278 * contains anything other than [a-zA-Z0-9_]+.
1279 *
1280 * @param string $name the setting name passed in to the constructor.
1281 */
1282 private function parse_setting_name($name) {
1283 $bits = explode('/', $name);
1284 if (count($bits) > 2) {
1285 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1286 }
1287 $this->name = array_pop($bits);
1288 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1289 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1290 }
1291 if (!empty($bits)) {
1292 $this->plugin = array_pop($bits);
cd3acbf2 1293 if ($this->plugin === 'moodle') {
1294 $this->plugin = null;
1295 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
9baf6825 1296 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1297 }
7fb0303d 1298 }
1299 }
1300
db26acd4 1301 /**
1302 * Returns the fullname prefixed by the plugin
1303 * @return string
1304 */
73fa96d5 1305 public function get_full_name() {
220a90c5 1306 return 's_'.$this->plugin.'_'.$this->name;
1307 }
1308
db26acd4 1309 /**
1310 * Returns the ID string based on plugin and name
1311 * @return string
1312 */
73fa96d5 1313 public function get_id() {
220a90c5 1314 return 'id_s_'.$this->plugin.'_'.$this->name;
1315 }
1316
db26acd4 1317 /**
1318 * Returns the config if possible
1319 *
db26acd4 1320 * @return mixed returns config if successfull else null
1321 */
73fa96d5 1322 public function config_read($name) {
220a90c5 1323 global $CFG;
eb6a973c 1324 if (!empty($this->plugin)) {
220a90c5 1325 $value = get_config($this->plugin, $name);
1326 return $value === false ? NULL : $value;
1327
1328 } else {
1329 if (isset($CFG->$name)) {
1330 return $CFG->$name;
1331 } else {
1332 return NULL;
1333 }
1334 }
1335 }
1336
301bf0b2 1337 /**
db26acd4 1338 * Used to set a config pair and log change
301bf0b2 1339 *
db26acd4 1340 * @param string $name
1341 * @param mixed $value Gets converted to string if not null
875e5f07 1342 * @return bool Write setting to config table
301bf0b2 1343 */
73fa96d5 1344 public function config_write($name, $value) {
301bf0b2 1345 global $DB, $USER, $CFG;
1346
427649bf
PS
1347 if ($this->nosave) {
1348 return true;
1349 }
1350
301bf0b2 1351 // make sure it is a real change
1352 $oldvalue = get_config($this->plugin, $name);
1353 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1354 $value = is_null($value) ? null : (string)$value;
1355
1356 if ($oldvalue === $value) {
1357 return true;
1358 }
1359
1360 // store change
1361 set_config($name, $value, $this->plugin);
1362
301bf0b2 1363 // log change
1364 $log = new object();
31a99877 1365 $log->userid = during_initial_install() ? 0 :$USER->id; // 0 as user id during install
301bf0b2 1366 $log->timemodified = time();
1367 $log->plugin = $this->plugin;
1368 $log->name = $name;
1369 $log->value = $value;
1370 $log->oldvalue = $oldvalue;
1371 $DB->insert_record('config_log', $log);
1372
1373 return true; // BC only
220a90c5 1374 }
1375
1376 /**
1377 * Returns current value of this setting
1378 * @return mixed array or string depending on instance, NULL means not set yet
1379 */
301bf0b2 1380 public abstract function get_setting();
eef868d1 1381
220a90c5 1382 /**
1383 * Returns default setting if exists
1384 * @return mixed array or string depending on instance; NULL means no default, user must supply
1385 */
73fa96d5 1386 public function get_defaultsetting() {
cd3acbf2 1387 $adminroot = admin_get_root(false, false);
1388 if (!empty($adminroot->custom_defaults)) {
1389 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1390 if (isset($adminroot->custom_defaults[$plugin])) {
875e5f07 1391 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
cd3acbf2 1392 return $adminroot->custom_defaults[$plugin][$this->name];
1393 }
1394 }
1395 }
8e5da17a 1396 return $this->defaultsetting;
1397 }
1398
220a90c5 1399 /**
1400 * Store new setting
db26acd4 1401 *
1402 * @param mixed $data string or array, must not be NULL
1403 * @return string empty string if ok, string error message otherwise
220a90c5 1404 */
301bf0b2 1405 public abstract function write_setting($data);
eef868d1 1406
220a90c5 1407 /**
1408 * Return part of form with setting
db26acd4 1409 * This function should always be overwritten
1410 *
1411 * @param mixed $data array or string depending on setting
1412 * @param string $query
220a90c5 1413 * @return string
1414 */
73fa96d5 1415 public function output_html($data, $query='') {
9baf6825 1416 // should be overridden
220a90c5 1417 return;
1418 }
1419
1420 /**
db26acd4 1421 * Function called if setting updated - cleanup, cache reset, etc.
1422 * @param string $functionname Sets the function name
220a90c5 1423 */
73fa96d5 1424 public function set_updatedcallback($functionname) {
220a90c5 1425 $this->updatedcallback = $functionname;
1426 }
1427
1428 /**
1429 * Is setting related to query text - used when searching
1430 * @param string $query
1431 * @return bool
1432 */
73fa96d5 1433 public function is_related($query) {
220a90c5 1434 if (strpos(strtolower($this->name), $query) !== false) {
1435 return true;
1436 }
1437 $textlib = textlib_get_instance();
1438 if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
1439 return true;
1440 }
1441 if (strpos($textlib->strtolower($this->description), $query) !== false) {
1442 return true;
1443 }
587c7040 1444 $current = $this->get_setting();
1445 if (!is_null($current)) {
1446 if (is_string($current)) {
1447 if (strpos($textlib->strtolower($current), $query) !== false) {
1448 return true;
1449 }
1450 }
1451 }
1452 $default = $this->get_defaultsetting();
1453 if (!is_null($default)) {
1454 if (is_string($default)) {
1455 if (strpos($textlib->strtolower($default), $query) !== false) {
1456 return true;
1457 }
1458 }
1459 }
220a90c5 1460 return false;
6e4dc10f 1461 }
220a90c5 1462}
eef868d1 1463
220a90c5 1464/**
1465 * No setting - just heading and text.
db26acd4 1466 *
1467 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1468 */
1469class admin_setting_heading extends admin_setting {
9baf6825 1470/**
1471 * not a setting, just text
1472 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1473 * @param string $heading heading
1474 * @param string $information text in box
1475 */
73fa96d5 1476 public function __construct($name, $heading, $information) {
427649bf 1477 $this->nosave = true;
73fa96d5 1478 parent::__construct($name, $heading, $information, '');
220a90c5 1479 }
1480
db26acd4 1481 /**
1482 * Always returns true
1483 * @return bool Always returns true
1484 */
73fa96d5 1485 public function get_setting() {
220a90c5 1486 return true;
1487 }
1488
db26acd4 1489 /**
1490 * Always returns true
1491 * @return bool Always returns true
1492 */
73fa96d5 1493 public function get_defaultsetting() {
220a90c5 1494 return true;
1495 }
1496
db26acd4 1497 /**
1498 * Never write settings
1499 * @return string Always returns an empty string
1500 */
73fa96d5 1501 public function write_setting($data) {
9baf6825 1502 // do not write any setting
220a90c5 1503 return '';
1504 }
0c079f19 1505
db26acd4 1506 /**
1507 * Returns an HTML string
1508 * @return string Returns an HTML string
1509 */
73fa96d5 1510 public function output_html($data, $query='') {
3c159385 1511 global $OUTPUT;
220a90c5 1512 $return = '';
1513 if ($this->visiblename != '') {
206dd861 1514 $return .= $OUTPUT->heading($this->visiblename, 3, 'main');
220a90c5 1515 }
1516 if ($this->description != '') {
ea85e1ee 1517 $return .= $OUTPUT->box(highlight($query, $this->description), 'generalbox formsettingheading');
220a90c5 1518 }
1519 return $return;
1520 }
1521}
6e4dc10f 1522
220a90c5 1523/**
1524 * The most flexibly setting, user is typing text
db26acd4 1525 *
1526 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1527 */
6e4dc10f 1528class admin_setting_configtext extends admin_setting {
1529
9baf6825 1530/** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
73fa96d5 1531 public $paramtype;
0c079f19 1532 /** @var int default field size */
73fa96d5 1533 public $size;
6e4dc10f 1534
220a90c5 1535 /**
875e5f07 1536 * Config text constructor
db26acd4 1537 *
1a41e806 1538 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
220a90c5 1539 * @param string $visiblename localised
1540 * @param string $description long localised info
1541 * @param string $defaultsetting
1542 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
f7633b0f 1543 * @param int $size default field size
220a90c5 1544 */
73fa96d5 1545 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
6e4dc10f 1546 $this->paramtype = $paramtype;
f7633b0f 1547 if (!is_null($size)) {
1548 $this->size = $size;
1549 } else {
1550 $this->size = ($paramtype == PARAM_INT) ? 5 : 30;
1551 }
73fa96d5 1552 parent::__construct($name, $visiblename, $description, $defaultsetting);
6e4dc10f 1553 }
1554
db26acd4 1555 /**
1556 * Return the setting
1557 *
875e5f07 1558 * @return mixed returns config if successful else null
db26acd4 1559 */
73fa96d5 1560 public function get_setting() {
220a90c5 1561 return $this->config_read($this->name);
6e4dc10f 1562 }
eef868d1 1563
73fa96d5 1564 public function write_setting($data) {
8cad6cca 1565 if ($this->paramtype === PARAM_INT and $data === '') {
9baf6825 1566 // do not complain if '' used instead of 0
8cad6cca 1567 $data = 0;
1568 }
220a90c5 1569 // $data is a string
c5d2d0dd 1570 $validated = $this->validate($data);
e33fbf87 1571 if ($validated !== true) {
1572 return $validated;
c235598d 1573 }
220a90c5 1574 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 1575 }
1576
e33fbf87 1577 /**
1578 * Validate data before storage
1579 * @param string data
1580 * @return mixed true if ok string if error found
1581 */
73fa96d5 1582 public function validate($data) {
58aaa8e9 1583 // allow paramtype to be a custom regex if it is the form of /pattern/
1584 if (preg_match('#^/.*/$#', $this->paramtype)) {
e33fbf87 1585 if (preg_match($this->paramtype, $data)) {
1586 return true;
1587 } else {
1588 return get_string('validateerror', 'admin');
1589 }
1590
9e24fbd1 1591 } else if ($this->paramtype === PARAM_RAW) {
4ea56b3f 1592 return true;
9baf6825 1593
4ea56b3f 1594 } else {
1595 $cleaned = clean_param($data, $this->paramtype);
1596 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
1597 return true;
e33fbf87 1598 } else {
4ea56b3f 1599 return get_string('validateerror', 'admin');
e33fbf87 1600 }
4ea56b3f 1601 }
c235598d 1602 }
1603
db26acd4 1604 /**
1605 * Return an XHTML string for the setting
1606 * @return string Returns an XHTML string
1607 */
73fa96d5 1608 public function output_html($data, $query='') {
220a90c5 1609 $default = $this->get_defaultsetting();
1610
220a90c5 1611 return format_admin_setting($this, $this->visiblename,
9baf6825 1612 '<div class="form-text defaultsnext"><input type="text" size="'.$this->size.'" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($data).'" /></div>',
1613 $this->description, true, '', $default, $query);
6e4dc10f 1614 }
6e4dc10f 1615}
1616
220a90c5 1617/**
1618 * General text area without html editor.
db26acd4 1619 *
1620 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1621 */
1622class admin_setting_configtextarea extends admin_setting_configtext {
4fe2250a 1623 private $rows;
1624 private $cols;
eba8cd63 1625
db26acd4 1626 /**
1627 * @param string $name
1628 * @param string $visiblename
1629 * @param string $description
1630 * @param mixed $defaultsetting string or array
1631 * @param mixed $paramtype
1632 * @param string $cols The number of columns to make the editor
1633 * @param string $rows The number of rows to make the editor
1634 */
73fa96d5 1635 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
220a90c5 1636 $this->rows = $rows;
1637 $this->cols = $cols;
73fa96d5 1638 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
eba8cd63 1639 }
db26acd4 1640 /**
1641 * Returns an XHTML string for the editor
1642 *
1643 * @param string $data
1644 * @param string $query
1645 * @return string XHTML string for the editor
1646 */
73fa96d5 1647 public function output_html($data, $query='') {
220a90c5 1648 $default = $this->get_defaultsetting();
1649
587c7040 1650 $defaultinfo = $default;
1651 if (!is_null($default) and $default !== '') {
1652 $defaultinfo = "\n".$default;
c5d2d0dd 1653 }
220a90c5 1654
1655 return format_admin_setting($this, $this->visiblename,
9baf6825 1656 '<div class="form-textarea" ><textarea rows="'. $this->rows .'" cols="'. $this->cols .'" id="'. $this->get_id() .'" name="'. $this->get_full_name() .'">'. s($data) .'</textarea></div>',
1657 $this->description, true, '', $defaultinfo, $query);
4fe2250a 1658 }
1659}
1660
1661/**
1662 * General text area with html editor.
1663 */
1664class admin_setting_confightmleditor extends admin_setting_configtext {
1665 private $rows;
1666 private $cols;
0c079f19 1667
4fe2250a 1668 /**
1669 * @param string $name
1670 * @param string $visiblename
1671 * @param string $description
1672 * @param mixed $defaultsetting string or array
1673 * @param mixed $paramtype
1674 */
1675 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
1676 $this->rows = $rows;
1677 $this->cols = $cols;
1678 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
ff5fe311 1679 editors_head_setup();
4fe2250a 1680 }
1681 /**
1682 * Returns an XHTML string for the editor
1683 *
1684 * @param string $data
1685 * @param string $query
1686 * @return string XHTML string for the editor
1687 */
1688 public function output_html($data, $query='') {
1689 $default = $this->get_defaultsetting();
1690
1691 $defaultinfo = $default;
1692 if (!is_null($default) and $default !== '') {
1693 $defaultinfo = "\n".$default;
1694 }
1695
20e5da7d 1696 $editor = editors_get_preferred_editor(FORMAT_HTML);
69429650 1697 $editor->use_editor($this->get_id(), array('noclean'=>true));
4fe2250a 1698
1699 return format_admin_setting($this, $this->visiblename,
9baf6825 1700 '<div class="form-textarea"><textarea rows="'. $this->rows .'" cols="'. $this->cols .'" id="'. $this->get_id() .'" name="'. $this->get_full_name() .'">'. s($data) .'</textarea></div>',
1701 $this->description, true, '', $defaultinfo, $query);
220a90c5 1702 }
1703}
1704
1705/**
1706 * Password field, allows unmasking of password
db26acd4 1707 *
1708 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1709 */
1710class admin_setting_configpasswordunmask extends admin_setting_configtext {
9baf6825 1711/**
1712 * Constructor
1713 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1714 * @param string $visiblename localised
1715 * @param string $description long localised info
1716 * @param string $defaultsetting default password
1717 */
73fa96d5 1718 public function __construct($name, $visiblename, $description, $defaultsetting) {
1719 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
220a90c5 1720 }
0c079f19 1721
db26acd4 1722 /**
1723 * Returns XHTML for the field
1724 * Writes Javascript into the HTML below right before the last div
1725 *
1726 * @todo Make javascript available through newer methods if possible
1727 * @param string $data Value for the field
1728 * @param string $query Passed as final argument for format_admin_setting
1729 * @return string XHTML field
1730 */
73fa96d5 1731 public function output_html($data, $query='') {
220a90c5 1732 $id = $this->get_id();
1733 $unmask = get_string('unmaskpassword', 'form');
1734 $unmaskjs = '<script type="text/javascript">
eba8cd63 1735//<![CDATA[
633239f6 1736var is_ie = (navigator.userAgent.toLowerCase().indexOf("msie") != -1);
1737
1738document.getElementById("'.$id.'").setAttribute("autocomplete", "off");
1739
1740var unmaskdiv = document.getElementById("'.$id.'unmaskdiv");
1741
1742var unmaskchb = document.createElement("input");
1743unmaskchb.setAttribute("type", "checkbox");
1744unmaskchb.setAttribute("id", "'.$id.'unmask");
1745unmaskchb.onchange = function() {unmaskPassword("'.$id.'");};
1746unmaskdiv.appendChild(unmaskchb);
1747
1748var unmasklbl = document.createElement("label");
1749unmasklbl.innerHTML = "'.addslashes_js($unmask).'";
1750if (is_ie) {
1751 unmasklbl.setAttribute("htmlFor", "'.$id.'unmask");
1752} else {
1753 unmasklbl.setAttribute("for", "'.$id.'unmask");
1754}
1755unmaskdiv.appendChild(unmasklbl);
1756
1757if (is_ie) {
1758 // ugly hack to work around the famous onchange IE bug
1759 unmaskchb.onclick = function() {this.blur();};
1760 unmaskdiv.onclick = function() {this.blur();};
1761}
eba8cd63 1762//]]>
1763</script>';
220a90c5 1764 return format_admin_setting($this, $this->visiblename,
9baf6825 1765 '<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>',
1766 $this->description, true, '', NULL, $query);
220a90c5 1767 }
1768}
1769
1770/**
e9c0fa35 1771 * Path to directory
db26acd4 1772 *
1773 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1774 */
e9c0fa35 1775class admin_setting_configfile extends admin_setting_configtext {
9baf6825 1776/**
1777 * Constructor
1778 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1779 * @param string $visiblename localised
1780 * @param string $description long localised info
1781 * @param string $defaultdirectory default directory location
1782 */
73fa96d5 1783 public function __construct($name, $visiblename, $description, $defaultdirectory) {
1784 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
220a90c5 1785 }
1786
db26acd4 1787 /**
1788 * Returns XHTML for the field
0c079f19 1789 *
db26acd4 1790 * Returns XHTML for the field and also checks whether the file
1791 * specified in $data exists using file_exists()
1792 *
1793 * @param string $data File name and path to use in value attr
1794 * @param string $query
1795 * @return string XHTML field
1796 */
73fa96d5 1797 public function output_html($data, $query='') {
220a90c5 1798 $default = $this->get_defaultsetting();
1799
220a90c5 1800 if ($data) {
e9c0fa35 1801 if (file_exists($data)) {
220a90c5 1802 $executable = '<span class="pathok">&#x2714;</span>';
1803 } else {
1804 $executable = '<span class="patherror">&#x2718;</span>';
1805 }
1806 } else {
1807 $executable = '';
1808 }
1809
1810 return format_admin_setting($this, $this->visiblename,
9baf6825 1811 '<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>',
1812 $this->description, true, '', $default, $query);
eba8cd63 1813 }
220a90c5 1814}
1815
1816/**
e9c0fa35 1817 * Path to executable file
db26acd4 1818 *
1819 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1820 */
e9c0fa35 1821class admin_setting_configexecutable extends admin_setting_configfile {
1822
9baf6825 1823/**
1824 * Returns an XHTML field
1825 *
1826 * @param string $data This is the value for the field
1827 * @param string $query
1828 * @return string XHTML field
1829 */
73fa96d5 1830 public function output_html($data, $query='') {
e9c0fa35 1831 $default = $this->get_defaultsetting();
1832
1833 if ($data) {
1834 if (file_exists($data) and is_executable($data)) {
1835 $executable = '<span class="pathok">&#x2714;</span>';
1836 } else {
1837 $executable = '<span class="patherror">&#x2718;</span>';
1838 }
1839 } else {
1840 $executable = '';
1841 }
1842
1843 return format_admin_setting($this, $this->visiblename,
9baf6825 1844 '<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>',
1845 $this->description, true, '', $default, $query);
220a90c5 1846 }
e9c0fa35 1847}
220a90c5 1848
e9c0fa35 1849/**
1850 * Path to directory
db26acd4 1851 *
1852 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
e9c0fa35 1853 */
1854class admin_setting_configdirectory extends admin_setting_configfile {
db26acd4 1855
9baf6825 1856/**
1857 * Returns an XHTML field
1858 *
1859 * @param string $data This is the value for the field
1860 * @param string $query
1861 * @return string XHTML
1862 */
73fa96d5 1863 public function output_html($data, $query='') {
220a90c5 1864 $default = $this->get_defaultsetting();
1865
220a90c5 1866 if ($data) {
1867 if (file_exists($data) and is_dir($data)) {
1868 $executable = '<span class="pathok">&#x2714;</span>';
1869 } else {
1870 $executable = '<span class="patherror">&#x2718;</span>';
1871 }
1872 } else {
1873 $executable = '';
1874 }
9ba38673 1875
220a90c5 1876 return format_admin_setting($this, $this->visiblename,
9baf6825 1877 '<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>',
1878 $this->description, true, '', $default, $query);
220a90c5 1879 }
eba8cd63 1880}
1881
220a90c5 1882/**
1883 * Checkbox
db26acd4 1884 *
1885 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1886 */
6e4dc10f 1887class admin_setting_configcheckbox extends admin_setting {
9baf6825 1888/** @var string Value used when checked */
73fa96d5 1889 public $yes;
0c079f19 1890 /** @var string Value used when not checked */
73fa96d5 1891 public $no;
6e4dc10f 1892
220a90c5 1893 /**
1894 * Constructor
1a41e806 1895 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
220a90c5 1896 * @param string $visiblename localised
1897 * @param string $description long localised info
1898 * @param string $defaultsetting
1899 * @param string $yes value used when checked
1900 * @param string $no value used when not checked
1901 */
73fa96d5 1902 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
1903 parent::__construct($name, $visiblename, $description, $defaultsetting);
220a90c5 1904 $this->yes = (string)$yes;
1905 $this->no = (string)$no;
6e4dc10f 1906 }
1907
db26acd4 1908 /**
1909 * Retrieves the current setting using the objects name
1910 *
1911 * @return string
1912 */
73fa96d5 1913 public function get_setting() {
220a90c5 1914 return $this->config_read($this->name);
6e4dc10f 1915 }
eef868d1 1916
db26acd4 1917 /**
1918 * Sets the value for the setting
1919 *
1920 * Sets the value for the setting to either the yes or no values
1921 * of the object by comparing $data to yes
1922 *
1923 * @param mixed $data Gets converted to str for comparison against yes value
1924 * @return string empty string or error
1925 */
73fa96d5 1926 public function write_setting($data) {
220a90c5 1927 if ((string)$data === $this->yes) { // convert to strings before comparison
1928 $data = $this->yes;
6e4dc10f 1929 } else {
220a90c5 1930 $data = $this->no;
6e4dc10f 1931 }
220a90c5 1932 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 1933 }
1934
db26acd4 1935 /**
1936 * Returns an XHTML checkbox field
1937 *
1938 * @param string $data If $data matches yes then checkbox is checked
1939 * @param string $query
1940 * @return string XHTML field
1941 */
73fa96d5 1942 public function output_html($data, $query='') {
220a90c5 1943 $default = $this->get_defaultsetting();
1944
1945 if (!is_null($default)) {
1946 if ((string)$default === $this->yes) {
587c7040 1947 $defaultinfo = get_string('checkboxyes', 'admin');
220a90c5 1948 } else {
587c7040 1949 $defaultinfo = get_string('checkboxno', 'admin');
220a90c5 1950 }
c8218a42 1951 } else {
587c7040 1952 $defaultinfo = NULL;
c8218a42 1953 }
220a90c5 1954
1955 if ((string)$data === $this->yes) { // convert to strings before comparison
1956 $checked = 'checked="checked"';
1957 } else {
1958 $checked = '';
1959 }
1960
1961 return format_admin_setting($this, $this->visiblename,
9baf6825 1962 '<div class="form-checkbox defaultsnext" ><input type="hidden" name="'.$this->get_full_name().'" value="'.s($this->no).'" /> '
1963 .'<input type="checkbox" id="'.$this->get_id().'" name="'.$this->get_full_name().'" value="'.s($this->yes).'" '.$checked.' /></div>',
1964 $this->description, true, '', $defaultinfo, $query);
6e4dc10f 1965 }
6e4dc10f 1966}
1967
220a90c5 1968/**
1969 * Multiple checkboxes, each represents different value, stored in csv format
db26acd4 1970 *
1971 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1972 */
1973class admin_setting_configmulticheckbox extends admin_setting {
9baf6825 1974/** @var array Array of choices value=>label */
73fa96d5 1975 public $choices;
eef868d1 1976
220a90c5 1977 /**
db26acd4 1978 * Constructor: uses parent::__construct
1979 *
1a41e806 1980 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
220a90c5 1981 * @param string $visiblename localised
1982 * @param string $description long localised info
1983 * @param array $defaultsetting array of selected
1984 * @param array $choices array of $value=>$label for each checkbox
1985 */
73fa96d5 1986 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
6e4dc10f 1987 $this->choices = $choices;
73fa96d5 1988 parent::__construct($name, $visiblename, $description, $defaultsetting);
6e4dc10f 1989 }
1990
0a784551 1991 /**
73fa96d5 1992 * This public function may be used in ancestors for lazy loading of choices
db26acd4 1993 *
1994 * @todo Check if this function is still required content commented out only returns true
1995 * @return bool true if loaded, false if error
0a784551 1996 */
73fa96d5 1997 public function load_choices() {
0a784551 1998 /*
220a90c5 1999 if (is_array($this->choices)) {
2000 return true;
0a784551 2001 }
2002 .... load choices here
2003 */
220a90c5 2004 return true;
2005 }
2006
2007 /**
2008 * Is setting related to query text - used when searching
db26acd4 2009 *
220a90c5 2010 * @param string $query
db26acd4 2011 * @return bool true on related, false on not or failure
220a90c5 2012 */
73fa96d5 2013 public function is_related($query) {
220a90c5 2014 if (!$this->load_choices() or empty($this->choices)) {
2015 return false;
2016 }
2017 if (parent::is_related($query)) {
2018 return true;
2019 }
2020
2021 $textlib = textlib_get_instance();
2022 foreach ($this->choices as $desc) {
2023 if (strpos($textlib->strtolower($desc), $query) !== false) {
2024 return true;
2025 }
2026 }
2027 return false;
0a784551 2028 }
2029
db26acd4 2030 /**
2031 * Returns the current setting if it is set
2032 *
2033 * @return mixed null if null, else an array
2034 */
73fa96d5 2035 public function get_setting() {
220a90c5 2036 $result = $this->config_read($this->name);
10f19c49 2037
220a90c5 2038 if (is_null($result)) {
2039 return NULL;
2040 }
2041 if ($result === '') {
2042 return array();
2043 }
10f19c49 2044 $enabled = explode(',', $result);
2045 $setting = array();
2046 foreach ($enabled as $option) {
2047 $setting[$option] = 1;
2048 }
2049 return $setting;
6e4dc10f 2050 }
eef868d1 2051
db26acd4 2052 /**
2053 * Saves the setting(s) provided in $data
2054 *
2055 * @param array $data An array of data, if not array returns empty str
2056 * @return mixed empty string on useless data or bool true=success, false=failed
2057 */
73fa96d5 2058 public function write_setting($data) {
220a90c5 2059 if (!is_array($data)) {
2060 return ''; // ignore it
2061 }
2062 if (!$this->load_choices() or empty($this->choices)) {
2063 return '';
2064 }
2065 unset($data['xxxxx']);
2066 $result = array();
2067 foreach ($data as $key => $value) {
2068 if ($value and array_key_exists($key, $this->choices)) {
2069 $result[] = $key;
2070 }
2071 }
2072 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
6e4dc10f 2073 }
0c079f19 2074
db26acd4 2075 /**
2076 * Returns XHTML field(s) as required by choices
2077 *
2078 * Relies on data being an array should data ever be another valid vartype with
2079 * acceptable value this may cause a warning/error
2080 * if (!is_array($data)) would fix the problem
2081 *
2082 * @todo Add vartype handling to ensure $data is an array
2083 *
2084 * @param array $data An array of checked values
2085 * @param string $query
2086 * @return string XHTML field
2087 */
73fa96d5 2088 public function output_html($data, $query='') {
220a90c5 2089 if (!$this->load_choices() or empty($this->choices)) {
2090 return '';
2091 }
2092 $default = $this->get_defaultsetting();
2093 if (is_null($default)) {
2094 $default = array();
2095 }
2096 if (is_null($data)) {
775f811a 2097 $data = array();
220a90c5 2098 }
220a90c5 2099 $options = array();
2100 $defaults = array();
10f19c49 2101 foreach ($this->choices as $key=>$description) {
2102 if (!empty($data[$key])) {
220a90c5 2103 $checked = 'checked="checked"';
2104 } else {
2105 $checked = '';
2106 }
10f19c49 2107 if (!empty($default[$key])) {
220a90c5 2108 $defaults[] = $description;
2109 }
2110
2111 $options[] = '<input type="checkbox" id="'.$this->get_id().'_'.$key.'" name="'.$this->get_full_name().'['.$key.']" value="1" '.$checked.' />'
9baf6825 2112 .'<label for="'.$this->get_id().'_'.$key.'">'.highlightfast($query, $description).'</label>';
220a90c5 2113 }
2114
587c7040 2115 if (is_null($default)) {
2116 $defaultinfo = NULL;
2117 } else if (!empty($defaults)) {
9baf6825 2118 $defaultinfo = implode(', ', $defaults);
2119 } else {
2120 $defaultinfo = get_string('none');
2121 }
220a90c5 2122
2123 $return = '<div class="form-multicheckbox">';
2124 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
2125 if ($options) {
2126 $return .= '<ul>';
2127 foreach ($options as $option) {
2128 $return .= '<li>'.$option.'</li>';
2129 }
2130 $return .= '</ul>';
6e4dc10f 2131 }
587c7040 2132 $return .= '</div>';
6153cf58 2133
587c7040 2134 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
c5d2d0dd 2135
6e4dc10f 2136 }
6e4dc10f 2137}
2138
220a90c5 2139/**
2140 * Multiple checkboxes 2, value stored as string 00101011
db26acd4 2141 *
2142 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2143 */
2144class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
db26acd4 2145
9baf6825 2146/**
2147 * Returns the setting if set
2148 *
2149 * @return mixed null if not set, else an array of set settings
2150 */
73fa96d5 2151 public function get_setting() {
220a90c5 2152 $result = $this->config_read($this->name);
2153 if (is_null($result)) {
2154 return NULL;
2155 }
2156 if (!$this->load_choices()) {
2157 return NULL;
2158 }
2159 $result = str_pad($result, count($this->choices), '0');
2160 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
2161 $setting = array();
2162 foreach ($this->choices as $key=>$unused) {
2163 $value = array_shift($result);
2164 if ($value) {
10f19c49 2165 $setting[$key] = 1;
220a90c5 2166 }
2167 }
2168 return $setting;
2169 }
6e4dc10f 2170
db26acd4 2171 /**
2172 * Save setting(s) provided in $data param
2173 *
2174 * @param array $data An array of settings to save
2175 * @return mixed empty string for bad data or bool true=>success, false=>error
2176 */
73fa96d5 2177 public function write_setting($data) {
220a90c5 2178 if (!is_array($data)) {
2179 return ''; // ignore it
2180 }
2181 if (!$this->load_choices() or empty($this->choices)) {
2182 return '';
2183 }
2184 $result = '';
2185 foreach ($this->choices as $key=>$unused) {
2186 if (!empty($data[$key])) {
2187 $result .= '1';
2188 } else {
2189 $result .= '0';
2190 }
2191 }
2192 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
2193 }
2194}
2195
2196/**
2197 * Select one value from list
db26acd4 2198 *
2199 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2200 */
2201class admin_setting_configselect extends admin_setting {
9baf6825 2202/** @var array Array of choices value=>label */
73fa96d5 2203 public $choices;
6e4dc10f 2204
220a90c5 2205 /**
2206 * Constructor
1a41e806 2207 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
220a90c5 2208 * @param string $visiblename localised
2209 * @param string $description long localised info
2210 * @param string $defaultsetting
2211 * @param array $choices array of $value=>$label for each selection
2212 */
73fa96d5 2213 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
220a90c5 2214 $this->choices = $choices;
73fa96d5 2215 parent::__construct($name, $visiblename, $description, $defaultsetting);
220a90c5 2216 }
2217
2218 /**
2219 * This function may be used in ancestors for lazy loading of choices
db26acd4 2220 *
0c079f19 2221 * Override this method if loading of choices is expensive, such
2222 * as when it requires multiple db requests.
2223 *
db26acd4 2224 * @return bool true if loaded, false if error
220a90c5 2225 */
73fa96d5 2226 public function load_choices() {
220a90c5 2227 /*
2228 if (is_array($this->choices)) {
2229 return true;
6e4dc10f 2230 }
220a90c5 2231 .... load choices here
2232 */
2233 return true;
6e4dc10f 2234 }
2235
db26acd4 2236 /**
2237 * Check if this is $query is related to a choice
2238 *
2239 * @param string $query
2240 * @return bool true if related, false if not
2241 */
73fa96d5 2242 public function is_related($query) {
407d8134 2243 if (parent::is_related($query)) {
2244 return true;
2245 }
2246 if (!$this->load_choices()) {
2247 return false;
2248 }
2249 $textlib = textlib_get_instance();
2250 foreach ($this->choices as $key=>$value) {
2251 if (strpos($textlib->strtolower($key), $query) !== false) {
2252 return true;
2253 }
2254 if (strpos($textlib->strtolower($value), $query) !== false) {
2255 return true;
2256 }
c5d2d0dd 2257 }
407d8134 2258 return false;
2259 }
2260
db26acd4 2261 /**
2262 * Return the setting
0c079f19 2263 *
875e5f07 2264 * @return mixed returns config if successful else null
db26acd4 2265 */
73fa96d5 2266 public function get_setting() {
220a90c5 2267 return $this->config_read($this->name);
6e4dc10f 2268 }
eef868d1 2269
db26acd4 2270 /**
2271 * Save a setting
2272 *
2273 * @param string $data
2274 * @return string empty of error string
2275 */
73fa96d5 2276 public function write_setting($data) {
220a90c5 2277 if (!$this->load_choices() or empty($this->choices)) {
2278 return '';
2279 }
2280 if (!array_key_exists($data, $this->choices)) {
2281 return ''; // ignore it
2282 }
eef868d1 2283
220a90c5 2284 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2285 }
eef868d1 2286
e2249afe 2287 /**
db26acd4 2288 * Returns XHTML select field
2289 *
2290 * Ensure the options are loaded, and generate the XHTML for the select
e2249afe 2291 * element and any warning message. Separating this out from output_html
2292 * makes it easier to subclass this class.
2293 *
2294 * @param string $data the option to show as selected.
2295 * @param string $current the currently selected option in the database, null if none.
2296 * @param string $default the default selected option.
2297 * @return array the HTML for the select element, and a warning message.
2298 */
73fa96d5 2299 public function output_select_html($data, $current, $default, $extraname = '') {
220a90c5 2300 if (!$this->load_choices() or empty($this->choices)) {
e2249afe 2301 return array('', '');
6e4dc10f 2302 }
220a90c5 2303
9c305ba1 2304 $warning = '';
2305 if (is_null($current)) {
9baf6825 2306 // first run
9c305ba1 2307 } else if (empty($current) and (array_key_exists('', $this->choices) or array_key_exists(0, $this->choices))) {
2308 // no warning
9baf6825 2309 } else if (!array_key_exists($current, $this->choices)) {
2310 $warning = get_string('warningcurrentsetting', 'admin', s($current));
2311 if (!is_null($default) and $data == $current) {
2312 $data = $default; // use default instead of first value when showing the form
2313 }
2314 }
9c305ba1 2315
e2249afe 2316 $selecthtml = '<select id="'.$this->get_id().'" name="'.$this->get_full_name().$extraname.'">';
6e4dc10f 2317 foreach ($this->choices as $key => $value) {
9baf6825 2318 // the string cast is needed because key may be integer - 0 is equal to most strings!
e2249afe 2319 $selecthtml .= '<option value="'.$key.'"'.((string)$key==$data ? ' selected="selected"' : '').'>'.$value.'</option>';
eef868d1 2320 }
e2249afe 2321 $selecthtml .= '</select>';
2322 return array($selecthtml, $warning);
2323 }
2324
db26acd4 2325 /**
2326 * Returns XHTML select field and wrapping div(s)
2327 *
2328 * @see output_select_html()
0c079f19 2329 *
db26acd4 2330 * @param string $data the option to show as selected
2331 * @param string $query
2332 * @return string XHTML field and wrapping div
2333 */
73fa96d5 2334 public function output_html($data, $query='') {
e2249afe 2335 $default = $this->get_defaultsetting();
2336 $current = $this->get_setting();
2337
2338 list($selecthtml, $warning) = $this->output_select_html($data, $current, $default);
2339 if (!$selecthtml) {
2340 return '';
2341 }
2342
2343 if (!is_null($default) and array_key_exists($default, $this->choices)) {
2344 $defaultinfo = $this->choices[$default];
2345 } else {
2346 $defaultinfo = NULL;
2347 }
2348
2349 $return = '<div class="form-select defaultsnext">' . $selecthtml . '</div>';
220a90c5 2350
587c7040 2351 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
6e4dc10f 2352 }
6e4dc10f 2353}
2354
220a90c5 2355/**
2356 * Select multiple items from list
db26acd4 2357 *
2358 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2359 */
6e4dc10f 2360class admin_setting_configmultiselect extends admin_setting_configselect {
9baf6825 2361/**
2362 * Constructor
2363 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2364 * @param string $visiblename localised
2365 * @param string $description long localised info
2366 * @param array $defaultsetting array of selected items
2367 * @param array $choices array of $value=>$label for each list item
2368 */
73fa96d5 2369 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
2370 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
6e4dc10f 2371 }
2372
db26acd4 2373 /**
2374 * Returns the select setting(s)
2375 *
2376 * @return mixed null or array. Null if no settings else array of setting(s)
2377 */
73fa96d5 2378 public function get_setting() {
220a90c5 2379 $result = $this->config_read($this->name);
2380 if (is_null($result)) {
d7933a55 2381 return NULL;
2382 }
220a90c5 2383 if ($result === '') {
2384 return array();
2385 }
2386 return explode(',', $result);
6e4dc10f 2387 }
eef868d1 2388
db26acd4 2389 /**
2390 * Saves setting(s) provided through $data
2391 *
2392 * Potential bug in the works should anyone call with this function
2393 * using a vartype that is not an array
2394 *
2395 * @todo Add vartype handling to ensure $data is an array
2396 * @param array $data
2397 */
73fa96d5 2398 public function write_setting($data) {
220a90c5 2399 if (!is_array($data)) {
2400 return ''; //ignore it
2401 }
2402 if (!$this->load_choices() or empty($this->choices)) {
2403 return '';
2404 }
2405
a7ad48fd 2406 unset($data['xxxxx']);
2407
220a90c5 2408 $save = array();
2409 foreach ($data as $value) {
2410 if (!array_key_exists($value, $this->choices)) {
2411 continue; // ignore it
2412 }
2413 $save[] = $value;
2414 }
2415
2416 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
2417 }
2418
2419 /**
2420 * Is setting related to query text - used when searching
db26acd4 2421 *
220a90c5 2422 * @param string $query
db26acd4 2423 * @return bool true if related, false if not
220a90c5 2424 */
73fa96d5 2425 public function is_related($query) {
220a90c5 2426 if (!$this->load_choices() or empty($this->choices)) {
2427 return false;
2428 }
2429 if (parent::is_related($query)) {
2430 return true;
2431 }
2432
2433 $textlib = textlib_get_instance();
2434 foreach ($this->choices as $desc) {
2435 if (strpos($textlib->strtolower($desc), $query) !== false) {
2436 return true;
2437 }
2438 }
2439 return false;
2440 }
2441
db26acd4 2442 /**
2443 * Returns XHTML multi-select field
2444 *
2445 * @todo Add vartype handling to ensure $data is an array
2446 * @param array $data Array of values to select by default
2447 * @param string $query
2448 * @return string XHTML multi-select field
2449 */
73fa96d5 2450 public function output_html($data, $query='') {
220a90c5 2451 if (!$this->load_choices() or empty($this->choices)) {
2452 return '';
2453 }
2454 $choices = $this->choices;
2455 $default = $this->get_defaultsetting();
2456 if (is_null($default)) {
2457 $default = array();
2458 }
2459 if (is_null($data)) {
d7933a55 2460 $data = array();
2461 }
220a90c5 2462
2463 $defaults = array();
4413941f 2464 $size = min(10, count($this->choices));
a7ad48fd 2465 $return = '<div class="form-select"><input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
4413941f 2466 $return .= '<select id="'.$this->get_id().'" name="'.$this->get_full_name().'[]" size="'.$size.'" multiple="multiple">';
220a90c5 2467 foreach ($this->choices as $key => $description) {
2468 if (in_array($key, $data)) {
2469 $selected = 'selected="selected"';
2470 } else {
2471 $selected = '';
2472 }
2473 if (in_array($key, $default)) {
2474 $defaults[] = $description;
6e4dc10f 2475 }
220a90c5 2476
2477 $return .= '<option value="'.s($key).'" '.$selected.'>'.$description.'</option>';
2478 }
2479
587c7040 2480 if (is_null($default)) {
2481 $defaultinfo = NULL;
2482 } if (!empty($defaults)) {
2483 $defaultinfo = implode(', ', $defaults);
220a90c5 2484 } else {
587c7040 2485 $defaultinfo = get_string('none');
6e4dc10f 2486 }
eef868d1 2487
587c7040 2488 $return .= '</select></div>';
2489 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
6e4dc10f 2490 }
220a90c5 2491}
eef868d1 2492
220a90c5 2493/**
2494 * Time selector
db26acd4 2495 *
2496 * This is a liiitle bit messy. we're using two selects, but we're returning
220a90c5 2497 * them as an array named after $name (so we only use $name2 internally for the setting)
db26acd4 2498 *
2499 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2500 */
2501class admin_setting_configtime extends admin_setting {
9baf6825 2502/** @var string Used for setting second select (minutes) */
73fa96d5 2503 public $name2;
220a90c5 2504
2505 /**
2506 * Constructor
2507 * @param string $hoursname setting for hours
2508 * @param string $minutesname setting for hours
2509 * @param string $visiblename localised
2510 * @param string $description long localised info
2511 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
2512 */
73fa96d5 2513 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
220a90c5 2514 $this->name2 = $minutesname;
73fa96d5 2515 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
220a90c5 2516 }
2517
db26acd4 2518 /**
2519 * Get the selected time
0c079f19 2520 *
db26acd4 2521 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
2522 */
73fa96d5 2523 public function get_setting() {
220a90c5 2524 $result1 = $this->config_read($this->name);
2525 $result2 = $this->config_read($this->name2);
2526 if (is_null($result1) or is_null($result2)) {
2527 return NULL;
2528 }
2529
2530 return array('h' => $result1, 'm' => $result2);
2531 }
2532
db26acd4 2533 /**
2534 * Store the time (hours and minutes)
0c079f19 2535 *
db26acd4 2536 * @param array $data Must be form 'h'=>xx, 'm'=>xx
2537 * @return bool true if success, false if not
2538 */
73fa96d5 2539 public function write_setting($data) {
220a90c5 2540 if (!is_array($data)) {
2541 return '';
2542 }
2543
2544 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
2545 return ($result ? '' : get_string('errorsetting', 'admin'));
2546 }
2547
db26acd4 2548 /**
2549 * Returns XHTML time select fields
2550 *
2551 * @param array $data Must be form 'h'=>xx, 'm'=>xx
2552 * @param string $query
2553 * @return string XHTML time select fields and wrapping div(s)
2554 */
73fa96d5 2555 public function output_html($data, $query='') {
220a90c5 2556 $default = $this->get_defaultsetting();
2557
2558 if (is_array($default)) {
587c7040 2559 $defaultinfo = $default['h'].':'.$default['m'];
cc73de71 2560 } else {
587c7040 2561 $defaultinfo = NULL;
6e4dc10f 2562 }
220a90c5 2563
587c7040 2564 $return = '<div class="form-time defaultsnext">'.
9baf6825 2565 '<select id="'.$this->get_id().'h" name="'.$this->get_full_name().'[h]">';
220a90c5 2566 for ($i = 0; $i < 24; $i++) {
2567 $return .= '<option value="'.$i.'"'.($i == $data['h'] ? ' selected="selected"' : '').'>'.$i.'</option>';
6e4dc10f 2568 }
220a90c5 2569 $return .= '</select>:<select id="'.$this->get_id().'m" name="'.$this->get_full_name().'[m]">';
2570 for ($i = 0; $i < 60; $i += 5) {
2571 $return .= '<option value="'.$i.'"'.($i == $data['m'] ? ' selected="selected"' : '').'>'.$i.'</option>';
2572 }
587c7040 2573 $return .= '</select></div>';
2574 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', $defaultinfo, $query);
6e4dc10f 2575 }
2576
2577}
2578
db26acd4 2579/**
2580 * Used to validate a textarea used for ip addresses
2581 *
2582 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 2583 */
4e639121 2584class admin_setting_configiplist extends admin_setting_configtextarea {
db26acd4 2585
9baf6825 2586/**
2587 * Validate the contents of the textarea as IP addresses
2588 *
875e5f07 2589 * Used to validate a new line separated list of IP addresses collected from
9baf6825 2590 * a textarea control
2591 *
875e5f07 2592 * @param string $data A list of IP Addresses separated by new lines
9baf6825 2593 * @return mixed bool true for success or string:error on failure
2594 */
73fa96d5 2595 public function validate($data) {
4e639121 2596 if(!empty($data)) {
c5d2d0dd 2597 $ips = explode("\n", $data);
4e639121 2598 } else {
2599 return true;
2600 }
2601 $result = true;
2602 foreach($ips as $ip) {
2603 $ip = trim($ip);
2604 if(preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
9baf6825 2605 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
2606 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
4e639121 2607 $result = true;
2608 } else {
2609 $result = false;
2610 break;
2611 }
2612 }
9baf6825 2613 if($result) {
4e639121 2614 return true;
2615 } else {
2616 return get_string('validateerror', 'admin');
2617 }
2618 }
2619}
2620
4413941f 2621/**
db26acd4 2622 * An admin setting for selecting one or more users who have a capability
2623 * in the system context
2624 *
4413941f 2625 * An admin setting for selecting one or more users, who have a particular capability
2626 * in the system context. Warning, make sure the list will never be too long. There is
2627 * no paging or searching of this list.
2628 *
2629 * To correctly get a list of users from this config setting, you need to call the
2630 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
db26acd4 2631 *
2632 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4413941f 2633 */
2634class admin_setting_users_with_capability extends admin_setting_configmultiselect {
adf176d7 2635 /** @var string The capabilities name */
4413941f 2636 protected $capability;
adf176d7
PS
2637 /** @var int include admin users too */
2638 protected $includeadmins;
4413941f 2639
2640 /**
2641 * Constructor.
2642 *
2643 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2644 * @param string $visiblename localised name
2645 * @param string $description localised long description
2646 * @param array $defaultsetting array of usernames
2647 * @param string $capability string capability name.
875e5f07 2648 * @param bool $includeadmins include administrators
4413941f 2649 */
adf176d7
PS
2650 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
2651 $this->capability = $capability;
2652 $this->includeadmins = $includeadmins;
fb073f60 2653 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
2654 }
2655
db26acd4 2656 /**
2657 * Load all of the uses who have the capability into choice array
2658 *
2659 * @return bool Always returns true
2660 */
fb073f60 2661 function load_choices() {
2662 if (is_array($this->choices)) {
2663 return true;
2664 }
4413941f 2665 $users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM),
9baf6825 2666 $this->capability, 'u.id,u.username,u.firstname,u.lastname', 'u.lastname,u.firstname');
fb073f60 2667 $this->choices = array(
4413941f 2668 '$@NONE@$' => get_string('nobody'),
fb073f60 2669 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4413941f 2670 );
adf176d7
PS
2671 if ($this->includeadmins) {
2672 $admins = get_admins();
2673 foreach ($admins as $user) {
2674 $this->choices[$user->id] = fullname($user);
2675 }
2676 }
4413941f 2677 foreach ($users as $user) {
adf176d7 2678 $this->choices[$user->id] = fullname($user);
4413941f 2679 }
fb073f60 2680 return true;
4413941f 2681 }
2682
db26acd4 2683 /**
2684 * Returns the default setting for class
2685 *
2686 * @return mixed Array, or string. Empty string if no default
2687 */
73fa96d5 2688 public function get_defaultsetting() {
4413941f 2689 $this->load_choices();
cd3acbf2 2690 $defaultsetting = parent::get_defaultsetting();
2691 if (empty($defaultsetting)) {
4413941f 2692 return array('$@NONE@$');
cd3acbf2 2693 } else if (array_key_exists($defaultsetting, $this->choices)) {
9baf6825 2694 return $defaultsetting;
2695 } else {
2696 return '';
2697 }
4413941f 2698 }
2699
db26acd4 2700 /**
2701 * Returns the current setting
2702 *
2703 * @return mixed array or string
2704 */
73fa96d5 2705 public function get_setting() {
4413941f 2706 $result = parent::get_setting();
adf176d7
PS
2707 if ($result === null) {
2708 // this is necessary for settings upgrade
2709 return null;
2710 }
4413941f 2711 if (empty($result)) {
2712 $result = array('$@NONE@$');
2713 }
2714 return $result;
2715 }
2716
db26acd4 2717 /**
2718 * Save the chosen setting provided as $data
2719 *
2720 * @param array $data
2721 * @return mixed string or array
2722 */
73fa96d5 2723 public function write_setting($data) {
9baf6825 2724 // If all is selected, remove any explicit options.
4413941f 2725 if (in_array('$@ALL@$', $data)) {
2726 $data = array('$@ALL@$');
2727 }
875e5f07 2728 // None never needs to be written to the DB.
4413941f 2729 if (in_array('$@NONE@$', $data)) {
2730 unset($data[array_search('$@NONE@$', $data)]);
2731 }
2732 return parent::write_setting($data);
2733 }
2734}
2735
220a90c5 2736/**
2737 * Special checkbox for calendar - resets SESSION vars.
db26acd4 2738 *
2739 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2740 */
6e4dc10f 2741class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
9baf6825 2742/**
2743 * Calls the parent::__construct with default values
2744 *
2745 * name => calendar_adminseesall
2746 * visiblename => get_string('adminseesall', 'admin')
2747 * description => get_string('helpadminseesall', 'admin')
2748 * defaultsetting => 0
2749 */
73fa96d5 2750 public function __construct() {
2751 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
9baf6825 2752 get_string('helpadminseesall', 'admin'), '0');
6e4dc10f 2753 }
2754
db26acd4 2755 /**
2756 * Stores the setting passed in $data
2757 *
db26acd4 2758 * @param mixed gets converted to string for comparison
2759 * @return string empty string or error message
2760 */
73fa96d5 2761 public function write_setting($data) {
6e4dc10f 2762 global $SESSION;
2763 unset($SESSION->cal_courses_shown);
220a90c5 2764 return parent::write_setting($data);
6e4dc10f 2765 }
2766}
2767
392e7363 2768/**
2769 * Special select for settings that are altered in setup.php and can not be altered on the fly
db26acd4 2770 *
2771 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
392e7363 2772 */
2773class admin_setting_special_selectsetup extends admin_setting_configselect {
9baf6825 2774/**
2775 * Reads the setting directly from the database
2776 *
2777 * @return mixed
2778 */
73fa96d5 2779 public function get_setting() {
9baf6825 2780 // read directly from db!
392e7363 2781 return get_config(NULL, $this->name);
2782 }
2783
db26acd4 2784 /**
2785 * Save the setting passed in $data
2786 *
db26acd4 2787 * @param string $data The setting to save
2788 * @return string empty or error message
2789 */
73fa96d5 2790 public function write_setting($data) {
392e7363 2791 global $CFG;
2792 // do not change active CFG setting!
2793 $current = $CFG->{$this->name};
2794 $result = parent::write_setting($data);
2795 $CFG->{$this->name} = $current;
2796 return $result;
2797 }
2798}
2799
220a90c5 2800/**
2801 * Special select for frontpage - stores data in course table
db26acd4 2802 *
2803 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2804 */
6e4dc10f 2805class admin_setting_sitesetselect extends admin_setting_configselect {
9baf6825 2806/**
2807 * Returns the site name for the selected site
2808 *
2809 * @see get_site()
2810 * @return string The site name of the selected site
2811 */
73fa96d5 2812 public function get_setting() {
b2bf016e 2813 $site = get_site();
2814 return $site->{$this->name};
6e4dc10f 2815 }
db26acd4 2816 /**
2817 * Updates the database and save the setting
2818 *
db26acd4 2819 * @param string data
2820 * @return string empty or error message
2821 */
73fa96d5 2822 public function write_setting($data) {
b2bf016e 2823 global $DB, $SITE;
6e4dc10f 2824 if (!in_array($data, array_keys($this->choices))) {
220a90c5 2825 return get_string('errorsetting', 'admin');
6e4dc10f 2826 }
2827 $record = new stdClass();
220a90c5 2828 $record->id = SITEID;
2829 $temp = $this->name;
2830 $record->$temp = $data;
6e4dc10f 2831 $record->timemodified = time();
b2bf016e 2832 // update $SITE
2833 $SITE->{$this->name} = $data;
f33e1ed4 2834 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2835 }
6e4dc10f 2836}
2837
ca497f4f 2838/**
2839 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
2840 * block to hidden.
2841 *
2842 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2843 */
2844class admin_setting_bloglevel extends admin_setting_configselect {
2845 /**
2846 * Updates the database and save the setting
2847 *
2848 * @param string data
2849 * @return string empty or error message
2850 */
2851 public function write_setting($data) {
2852 global $DB;
2853 if ($data['bloglevel'] == 0) {
2854 $DB->set_field('block', 'visible', 0, array('name' => 'blog_menu'));
2855 } else {
2856 $DB->set_field('block', 'visible', 1, array('name' => 'blog_menu'));
2857 }
2858 return parent::write_setting($data);
2859 }
2860}
2861
220a90c5 2862/**
2863 * Special select - lists on the frontpage - hacky
db26acd4 2864 *
2865 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2866 */
2867class admin_setting_courselist_frontpage extends admin_setting {
9baf6825 2868/** @var array Array of choices value=>label */
73fa96d5 2869 public $choices;
6e4dc10f 2870
db26acd4 2871 /**
2872 * Construct override, requires one param
2873 *
db26acd4 2874 * @param bool $loggedin Is the user logged in
2875 */
73fa96d5 2876 public function __construct($loggedin) {
6e4dc10f 2877 global $CFG;
220a90c5 2878 require_once($CFG->dirroot.'/course/lib.php');
2879 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
2880 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
2881 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
2882 $defaults = array(FRONTPAGECOURSELIST);
73fa96d5 2883 parent::__construct($name, $visiblename, $description, $defaults);
6e4dc10f 2884 }
eef868d1 2885
db26acd4 2886 /**
2887 * Loads the choices available
2888 *
db26acd4 2889 * @return bool always returns true
2890 */
73fa96d5 2891 public function load_choices() {
c7da4357 2892 global $DB;
220a90c5 2893 if (is_array($this->choices)) {
2894 return true;
2895 }
2896 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
9baf6825 2897 FRONTPAGECOURSELIST => get_string('frontpagecourselist'),
2898 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
2899 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
2900 'none' => get_string('none'));
c7da4357 2901 if ($this->name == 'frontpage' and $DB->count_records('course') > FRONTPAGECOURSELIMIT) {
220a90c5 2902 unset($this->choices[FRONTPAGECOURSELIST]);
2903 }
2904 return true;
2905 }
db26acd4 2906 /**
2907 * Returns the selected settings
2908 *
2909 * @param mixed array or setting or null
2910 */
73fa96d5 2911 public function get_setting() {
220a90c5 2912 $result = $this->config_read($this->name);
2913 if (is_null($result)) {
2914 return NULL;
2915 }
2916 if ($result === '') {
2917 return array();
2918 }
2919 return explode(',', $result);
6e4dc10f 2920 }
eef868d1 2921
db26acd4 2922 /**
2923 * Save the selected options
2924 *
2925 * @param array $data
2926 * @return mixed empty string (data is not an array) or bool true=success false=failure
2927 */
73fa96d5 2928 public function write_setting($data) {
220a90c5 2929 if (!is_array($data)) {
2930 return '';
6e4dc10f 2931 }
220a90c5 2932 $this->load_choices();
2933 $save = array();
6e4dc10f 2934 foreach($data as $datum) {
220a90c5 2935 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
2936 continue;
6e4dc10f 2937 }
220a90c5 2938 $save[$datum] = $datum; // no duplicates
6e4dc10f 2939 }
220a90c5 2940 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2941 }
eef868d1 2942
db26acd4 2943 /**
2944 * Return XHTML select field and wrapping div
2945 *
2946 * @todo Add vartype handling to make sure $data is an array
2947 * @param array $data Array of elements to select by default
2948 * @return string XHTML select field and wrapping div
2949 */
73fa96d5 2950 public function output_html($data, $query='') {
220a90c5 2951 $this->load_choices();
2952 $currentsetting = array();
2953 foreach ($data as $key) {
2954 if ($key != 'none' and array_key_exists($key, $this->choices)) {
2955 $currentsetting[] = $key; // already selected first
6e4dc10f 2956 }
2957 }
220a90c5 2958
0a7e84c3 2959 $return = '<div class="form-group">';
6e4dc10f 2960 for ($i = 0; $i < count($this->choices) - 1; $i++) {
220a90c5 2961 if (!array_key_exists($i, $currentsetting)) {
2962 $currentsetting[$i] = 'none'; //none
2963 }
2964 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
6e4dc10f 2965 foreach ($this->choices as $key => $value) {
220a90c5 2966 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
6e4dc10f 2967 }
2968 $return .= '</select>';
2969 if ($i !== count($this->choices) - 2) {
975211bb 2970 $return .= '<br />';
6e4dc10f 2971 }
2972 }
0a7e84c3 2973 $return .= '</div>';
2974
587c7040 2975 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 2976 }
2977}
2978
220a90c5 2979/**
2980 * Special checkbox for frontpage - stores data in course table
db26acd4 2981 *
2982 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2983 */
6e4dc10f 2984class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
9baf6825 2985/**
2986 * Returns the current sites name
2987 *
2988 * @return string
2989 */
73fa96d5 2990 public function get_setting() {
b2bf016e 2991 $site = get_site();
2992 return $site->{$this->name};
6e4dc10f 2993 }
eef868d1 2994
db26acd4 2995 /**
2996 * Save the selected setting
2997 *
db26acd4 2998 * @param string $data The selected site
2999 * @return string empty string or error message
3000 */
73fa96d5 3001 public function write_setting($data) {
b2bf016e 3002 global $DB, $SITE;
220a90c5 3003 $record = new object();
3004 $record->id = SITEID;
3005 $record->{$this->name} = ($data == '1' ? 1 : 0);
3006 $record->timemodified = time();
b2bf016e 3007 // update $SITE
3008 $SITE->{$this->name} = $data;
f33e1ed4 3009 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 3010 }
6e4dc10f 3011}
3012
220a90c5 3013/**
3014 * Special text for frontpage - stores data in course table.
3015 * Empty string means not set here. Manual setting is required.
db26acd4 3016 *
3017 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 3018 */
6e4dc10f 3019class admin_setting_sitesettext extends admin_setting_configtext {
9baf6825 3020/**
3021 * Return the current setting
3022 *
3023 * @return mixed string or null
3024 */
73fa96d5 3025 public function get_setting() {
b2bf016e 3026 $site = get_site();
a7747679 3027 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
6e4dc10f 3028 }
90cfbd0a 3029
db26acd4 3030 /**
3031 * Validate the selected data
3032 *
3033 * @param string $data The selected value to validate
3034 * @return mixed true or message string
3035 */
73fa96d5 3036 public function validate($data) {
294ce987 3037 $cleaned = clean_param($data, PARAM_MULTILANG);
e33fbf87 3038 if ($cleaned === '') {
3039 return get_string('required');
3040 }
3041 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
3042 return true;
3043 } else {
3044 return get_string('validateerror', 'admin');
b89639f9 3045 }
b89639f9 3046 }
3047
db26acd4 3048 /**
3049 * Save the selected setting
3050 *
db26acd4 3051 * @param string $data The selected value
875e5f07 3052 * @return string empty or error message
db26acd4 3053 */
73fa96d5 3054 public function write_setting($data) {
b2bf016e 3055 global $DB, $SITE;
b89639f9 3056 $data = trim($data);
c5d2d0dd 3057 $validated = $this->validate($data);
e33fbf87 3058 if ($validated !== true) {
3059 return $validated;
90cfbd0a 3060 }
eef868d1 3061
220a90c5 3062 $record = new object();
3063 $record->id = SITEID;
f33e1ed4 3064 $record->{$this->name} = $data;
220a90c5 3065 $record->timemodified = time();
b2bf016e 3066 // update $SITE
3067 $SITE->{$this->name} = $data;
f33e1ed4 3068 return ($DB->update_record('course', $record) ? '' : get_string('dbupdatefailed', 'error'));
6e4dc10f 3069 }
6e4dc10f 3070}
3071
220a90c5 3072/**
3073 * Special text editor for site description.
db26acd4 3074 *
3075 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 3076 */
6e4dc10f 3077class admin_setting_special_frontpagedesc extends admin_setting {
9baf6825 3078/**
3079 * Calls parent::__construct with specific arguments
3080 */
73fa96d5 3081 public function __construct() {
3082 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
ff5fe311 3083 editors_head_setup();
6e4dc10f 3084 }
eef868d1 3085
db26acd4 3086 /**
3087 * Return the current setting
3088 * @return string The current setting
3089 */
73fa96d5 3090 public function get_setting() {
b2bf016e 3091 $site = get_site();
3092 return $site->{$this->name};
c626c2f4 3093 }
eef868d1 3094
db26acd4 3095 /**
3096 * Save the new setting
3097 *
db26acd4 3098 * @param string $data The new value to save
3099 * @return string empty or error message
3100 */
73fa96d5 3101 public function write_setting($data) {
b2bf016e 3102 global $DB, $SITE;
c626c2f4 3103 $record = new object();
220a90c5 3104 $record->id = SITEID;
f33e1ed4 3105 $record->{$this->name} = $data;
c626c2f4 3106 $record->timemodified = time();
b2bf016e 3107 $SITE->{$this->name} = $data;
7719860b 3108 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 3109 }
3110
db26acd4 3111 /**
3112 * Returns XHTML for the field plus wrapping div
3113 *
db26acd4 3114 * @param string $data The current value
3115 * @param string $query
3116 * @return string The XHTML output
3117 */
73fa96d5 3118 public function output_html($data, $query='') {
cb6c02c4 3119 global $CFG;
6e4dc10f 3120
220a90c5 3121 $CFG->adminusehtmleditor = can_use_html_editor();
8c37106d 3122 $return = '<div class="form-htmlarea">'.print_textarea($CFG->adminusehtmleditor, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
220a90c5 3123
587c7040 3124 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
220a90c5 3125 }
3126}
6e4dc10f 3127
db26acd4 3128/**
3129 * Special settings for emoticons
3130 *
3131 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 3132 */
93c61c18 3133class admin_setting_emoticons extends admin_setting {
3134
9baf6825 3135/**
3136 * Calls parent::__construct with specific args
3137 */
73fa96d5 3138 public function __construct() {
93c61c18 3139 global $CFG;
3140 $name = 'emoticons';
3141 $visiblename = get_string('emoticons', 'admin');
3142 $description = get_string('configemoticons', 'admin');
3143 $defaults = array('k0' => ':-)',
9baf6825 3144 'v0' => 'smiley',
3145 'k1' => ':)',
3146 'v1' => 'smiley',
3147 'k2' => ':-D',
3148 'v2' => 'biggrin',
3149 'k3' => ';-)',
3150 'v3' => 'wink',
3151 'k4' => ':-/',
3152 'v4' => 'mixed',
3153 'k5' => 'V-.',
3154 'v5' => 'thoughtful',
3155 'k6' => ':-P',
3156 'v6' => 'tongueout',
3157 'k7' => 'B-)',
3158 'v7' => 'cool',
3159 'k8' => '^-)',
3160 'v8' => 'approve',
3161 'k9' => '8-)',
3162 'v9' => 'wideeyes',
3163 'k10' => ':o)',
3164 'v10' => 'clown',
3165 'k11' => ':-(',
3166 'v11' => 'sad',
3167 'k12' => ':(',
3168 'v12' => 'sad',
3169 'k13' => '8-.',
3170 'v13' => 'shy',
3171 'k14' => ':-I',
3172 'v14' => 'blush',
3173 'k15' => ':-X',
3174 'v15' => 'kiss',
3175 'k16' => '8-o',
3176 'v16' => 'surprise',
3177 'k17' => 'P-|',
3178 'v17' => 'blackeye',
3179 'k18' => '8-[',
3180 'v18' => 'angry',
3181 'k19' => 'xx-P',
3182 'v19' => 'dead',
3183 'k20' => '|-.',
3184 'v20' => 'sleepy',
3185 'k21' => '}-]',
3186 'v21' => 'evil',
3187 'k22' => '(h)',
3188 'v22' => 'heart',
3189 'k23' => '(heart)',
3190 'v23' => 'heart',
3191 'k24' => '(y)',
3192 'v24' => 'yes',
3193 'k25' => '(n)',
3194 'v25' => 'no',
3195 'k26' => '(martin)',
3196 'v26' => 'martin',
3197 'k27' => '( )',
3198 'v27' => 'egg');
73fa96d5 3199 parent::__construct($name, $visiblename, $description, $defaults);
93c61c18 3200 }
0c079f19 3201
db26acd4 3202 /**
3203 * Return the current setting(s)
3204 *
db26acd4 3205 * @return array Current settings array
3206 */
73fa96d5 3207 public function get_setting() {
93c61c18 3208 global $CFG;
220a90c5 3209 $result = $this->config_read($this->name);
3210 if (is_null($result)) {
93c61c18 3211 return NULL;
3212 }
220a90c5 3213 $i = 0;
3214 $currentsetting = array();
3215 $items = explode('{;}', $result);
3216 foreach ($items as $item) {
9baf6825 3217 $item = explode('{:}', $item);
3218 $currentsetting['k'.$i] = $item[0];
3219 $currentsetting['v'.$i] = $item[1];
3220 $i++;
220a90c5 3221 }
3222 return $currentsetting;
93c61c18 3223 }
3224
db26acd4 3225 /**
3226 * Save selected settings
3227 *
3228 * @param array $data Array of settings to save
3229 * @return bool
3230 */
73fa96d5 3231 public function write_setting($data) {
93c61c18 3232
9baf6825 3233 // there miiight be an easier way to do this :)
3234 // if this is changed, make sure the $defaults array above is modified so that this
3235 // function processes it correctly
93c61c18 3236
3237 $keys = array();
3238 $values = array();
3239
3240 foreach ($data as $key => $value) {
3241 if (substr($key,0,1) == 'k') {
3242 $keys[substr($key,1)] = $value;
3243 } elseif (substr($key,0,1) == 'v') {
3244 $values[substr($key,1)] = $value;
3245 }
3246 }
3247
220a90c5 3248 $result = array();
93c61c18 3249 for ($i = 0; $i < count($keys); $i++) {
3250 if (($keys[$i] !== '') && ($values[$i] !== '')) {
220a90c5 3251 $result[] = clean_param($keys[$i],PARAM_NOTAGS).'{:}'.clean_param($values[$i], PARAM_NOTAGS);
93c61c18 3252 }
3253 }
3254
220a90c5 3255 return ($this->config_write($this->name, implode('{;}', $result)) ? '' : get_string('errorsetting', 'admin').$this->visiblename.'<br />');
93c61c18 3256 }
3257
db26acd4 3258 /**
3259 * Return XHTML field(s) for options
3260 *
3261 * @param array $data Array of options to set in HTML
3262 * @return string XHTML string for the fields and wrapping div(s)
3263 */
73fa96d5 3264 public function output_html($data, $query='') {
220a90c5 3265 $fullname = $this->get_full_name();
93c61c18 3266 $return = '<div class="form-group">';
220a90c5 3267 for ($i = 0; $i < count($data) / 2; $i++) {
3268 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="'.$data['k'.$i].'" />';
93c61c18 3269 $return .= '&nbsp;&nbsp;';
220a90c5 3270 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="'.$data['v'.$i].'" /><br />';
93c61c18 3271 }
220a90c5 3272 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="" />';
93c61c18 3273 $return .= '&nbsp;&nbsp;';
220a90c5 3274 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="" /><br />';
3275 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.($i + 1).']" value="" />';
93c61c18 3276 $return .= '&nbsp;&nbsp;';
220a90c5 3277 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.($i + 1).']" value="" />';
93c61c18 3278 $return .= '</div>';
3279
587c7040 3280 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
93c61c18 3281 }
3282
3283}
6e4dc10f 3284
220a90c5 3285/**
3286 * Special setting for limiting of the list of available languages.
db26acd4 3287 *
3288 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 3289 */
4642650f 3290class admin_setting_langlist extends admin_setting_configtext {
9baf6825 3291/**
3292 * Calls parent::__construct with specific arguments