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