MDL-21558 adding local plugins management page into the admin tree
[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');
117
bba0beae 118define('INSECURE_DATAROOT_WARNING', 1);
119define('INSECURE_DATAROOT_ERROR', 2);
0c079f19 120
ed996ede 121/**
122 * Automatically clean-up all plugin data and remove the plugin DB tables
123 *
124 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
125 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
126 * @uses global $OUTPUT to produce notices and other messages
127 * @return void
128 */
129function uninstall_plugin($type, $name) {
130 global $CFG, $DB, $OUTPUT;
131
132 // recursively uninstall all the subplugins first
2138244c 133 $subpluginlocations = plugin_supports($type, $name, FEATURE_MOD_SUBPLUGINS);
134 if (is_array($subpluginlocations)) {
ed996ede 135 foreach ($subpluginlocations as $subplugintype => $notusedlocationpath) {
136 $subplugins = get_plugin_list($subplugintype);
137 foreach ($subplugins as $subpluginname => $notusedpluginpath) {
138 uninstall_plugin($subplugintype, $subpluginname);
139 }
140 }
141 }
142
2138244c 143 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
144
145 if ($type === 'mod') {
146 $pluginname = $name; // eg. 'forum'
147 $strpluginname = get_string('modulename', $pluginname);
148 } else {
9baf6825 149 $pluginname = $component;
150 $strpluginname = get_string('pluginname', $pluginname); // replaces string 'modulename'
2138244c 151 }
ed996ede 152 echo $OUTPUT->heading($pluginname);
153
154 $plugindirectory = get_plugin_directory($type, $name);
155 $uninstalllib = $plugindirectory . '/db/uninstall.php';
156 if (file_exists($uninstalllib)) {
157 require_once($uninstalllib);
2138244c 158 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
ed996ede 159 if (function_exists($uninstallfunction)) {
2138244c 160 if (!$uninstallfunction()) {
ed996ede 161 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $pluginname);
162 }
163 }
164 }
165
166 if ('mod' === $type) {
9baf6825 167 // perform cleanup tasks specific for activity modules
ed996ede 168
ed996ede 169 if (!$module = $DB->get_record('modules', array('name' => $name))) {
170 print_error('moduledoesnotexist', 'error');
171 }
172
173 // delete all the relevant instances from all course sections
174 if ($coursemods = $DB->get_records('course_modules', array('module' => $module->id))) {
175 foreach ($coursemods as $coursemod) {
176 if (!delete_mod_from_section($coursemod->id, $coursemod->section)) {
177 echo $OUTPUT->notification("Could not delete the $strpluginname with id = $coursemod->id from section $coursemod->section");
178 }
179 }
180 }
181
182 // clear course.modinfo for courses that used this module
183 $sql = "UPDATE {course}
184 SET modinfo=''
185 WHERE id IN (SELECT DISTINCT course
186 FROM {course_modules}
187 WHERE module=?)";
188 $DB->execute($sql, array($module->id));
189
190 // delete all the course module records
2138244c 191 $DB->delete_records('course_modules', array('module' => $module->id));
ed996ede 192
193 // delete module contexts
194 if ($coursemods) {
195 foreach ($coursemods as $coursemod) {
196 if (!delete_context(CONTEXT_MODULE, $coursemod->id)) {
197 echo $OUTPUT->notification("Could not delete the context for $strpluginname with id = $coursemod->id");
198 }
199 }
200 }
201
202 // delete the module entry itself
2138244c 203 $DB->delete_records('modules', array('name' => $module->name));
ed996ede 204
205 // cleanup the gradebook
206 require_once($CFG->libdir.'/gradelib.php');
207 grade_uninstalled_module($module->name);
208
209 // Perform any custom uninstall tasks
210 if (file_exists($CFG->dirroot . '/mod/' . $module->name . '/lib.php')) {
211 require_once($CFG->dirroot . '/mod/' . $module->name . '/lib.php');
212 $uninstallfunction = $module->name . '_uninstall';
213 if (function_exists($uninstallfunction)) {
214 debugging("{$uninstallfunction}() has been deprecated. Use the plugin's db/uninstall.php instead", DEBUG_DEVELOPER);
2138244c 215 if (!$uninstallfunction()) {
ed996ede 216 echo $OUTPUT->notification('Encountered a problem running uninstall function for '. $module->name.'!');
217 }
218 }
219 }
220 }
221
222 // perform clean-up taks common for all the plugin/subplugin types
223
224 // delete calendar events
2138244c 225 $DB->delete_records('event', array('modulename' => $pluginname));
ed996ede 226
227 // delete all the logs
2138244c 228 $DB->delete_records('log', array('module' => $pluginname));
ed996ede 229
230 // delete log_display information
2138244c 231 $DB->delete_records('log_display', array('module' => $pluginname));
ed996ede 232
233 // delete the module configuration records
2138244c 234 unset_all_config_for_plugin($pluginname);
ed996ede 235
236 // delete the plugin tables
237 $xmldbfilepath = $plugindirectory . '/db/install.xml';
238 drop_plugin_tables($pluginname, $xmldbfilepath, false);
239
ed996ede 240 // delete the capabilities that were defined by this module
241 capabilities_cleanup($component);
242
243 // remove entent handlers and dequeue pending events
244 events_uninstall($component);
245
246 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
247}
248
b1f93b15 249/**
250 * Delete all plugin tables
db26acd4 251 *
db26acd4 252 * @param string $name Name of plugin, used as table prefix
253 * @param string $file Path to install.xml file
254 * @param bool $feedback defaults to true
0c079f19 255 * @return bool Always returns true
b1f93b15 256 */
257function drop_plugin_tables($name, $file, $feedback=true) {
258 global $CFG, $DB;
259
260 // first try normal delete
8b4ca8f7 261 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
b1f93b15 262 return true;
263 }
264
265 // then try to find all tables that start with name and are not in any xml file
266 $used_tables = get_used_table_names();
267
268 $tables = $DB->get_tables();
269
270 /// Iterate over, fixing id fields as necessary
271 foreach ($tables as $table) {
272 if (in_array($table, $used_tables)) {
273 continue;
274 }
275
8b4ca8f7 276 if (strpos($table, $name) !== 0) {
277 continue;
278 }
279
b1f93b15 280 // found orphan table --> delete it
281 if ($DB->get_manager()->table_exists($table)) {
282 $xmldb_table = new xmldb_table($table);
eee5d9bb 283 $DB->get_manager()->drop_table($xmldb_table);
b1f93b15 284 }
285 }
286
287 return true;
288}
289
290/**
291 * Returns names of all known tables == tables that moodle knowns about.
db26acd4 292 *
293 * @return array Array of lowercase table names
b1f93b15 294 */
295function get_used_table_names() {
296 $table_names = array();
297 $dbdirs = get_db_directories();
298
299 foreach ($dbdirs as $dbdir) {
300 $file = $dbdir.'/install.xml';
301
302 $xmldb_file = new xmldb_file($file);
303
304 if (!$xmldb_file->fileExists()) {
305 continue;
306 }
307
308 $loaded = $xmldb_file->loadXMLStructure();
73fa96d5 309 $structure = $xmldb_file->getStructure();
b1f93b15 310
311 if ($loaded and $tables = $structure->getTables()) {
312 foreach($tables as $table) {
313 $table_names[] = strtolower($table->name);
314 }
315 }
316 }
317
318 return $table_names;
319}
320
321/**
322 * Returns list of all directories where we expect install.xml files
db26acd4 323 * @return array Array of paths
b1f93b15 324 */
325function get_db_directories() {
326 global $CFG;
327
328 $dbdirs = array();
329
9baf6825 330 /// First, the main one (lib/db)
b1f93b15 331 $dbdirs[] = $CFG->libdir.'/db';
332
9baf6825 333 /// Then, all the ones defined by get_plugin_types()
17da2e6f 334 $plugintypes = get_plugin_types();
335 foreach ($plugintypes as $plugintype => $pluginbasedir) {
336 if ($plugins = get_plugin_list($plugintype)) {
337 foreach ($plugins as $plugin => $plugindir) {
338 $dbdirs[] = $plugindir.'/db';
76b6c644 339 }
b91b274b 340 }
7cdd8b22 341 }
b1f93b15 342
b1f93b15 343 return $dbdirs;
344}
345
eef868d1 346/**
61460dd6 347 * Try to obtain or release the cron lock.
61460dd6 348 * @param string $name name of lock
349 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionaly
db26acd4 350 * @param bool $ignorecurrent ignore current lock state, usually entend previous lock, defaults to false
61460dd6 351 * @return bool true if lock obtained
f3221af9 352 */
61460dd6 353function set_cron_lock($name, $until, $ignorecurrent=false) {
f33e1ed4 354 global $DB;
f3221af9 355 if (empty($name)) {
61460dd6 356 debugging("Tried to get a cron lock for a null fieldname");
f3221af9 357 return false;
358 }
359
61460dd6 360 // remove lock by force == remove from config table
361 if (is_null($until)) {
362 set_config($name, null);
f3221af9 363 return true;
364 }
365
61460dd6 366 if (!$ignorecurrent) {
9baf6825 367 // read value from db - other processes might have changed it
f33e1ed4 368 $value = $DB->get_field('config', 'value', array('name'=>$name));
61460dd6 369
370 if ($value and $value > time()) {
9baf6825 371 //lock active
61460dd6 372 return false;
f3221af9 373 }
374 }
61460dd6 375
376 set_config($name, $until);
f3221af9 377 return true;
378}
a597f8a8 379
bba0beae 380/**
381 * Test if and critical warnings are present
382 * @return bool
383 */
384function admin_critical_warnings_present() {
385 global $SESSION;
386
387 if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
388 return 0;
389 }
390
391 if (!isset($SESSION->admin_critical_warning)) {
392 $SESSION->admin_critical_warning = 0;
fbf2c91e 393 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
bba0beae 394 $SESSION->admin_critical_warning = 1;
395 }
396 }
397
398 return $SESSION->admin_critical_warning;
399}
400
61f9c4b4 401/**
db26acd4 402 * Detects if float supports at least 10 decimal digits
403 *
404 * Detects if float supports at least 10 deciman digits
61f9c4b4 405 * and also if float-->string conversion works as expected.
db26acd4 406 *
61f9c4b4 407 * @return bool true if problem found
408 */
409function is_float_problem() {
410 $num1 = 2009010200.01;
411 $num2 = 2009010200.02;
412
413 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
414}
415
57e35f32 416/**
417 * Try to verify that dataroot is not accessible from web.
57e35f32 418 *
db26acd4 419 * Try to verify that dataroot is not accessible from web.
420 * It is not 100% correct but might help to reduce number of vulnerable sites.
57e35f32 421 * Protection from httpd.conf and .htaccess is not detected properly.
0c079f19 422 *
db26acd4 423 * @uses INSECURE_DATAROOT_WARNING
424 * @uses INSECURE_DATAROOT_ERROR
425 * @param bool $fetchtest try to test public access by fetching file, default false
bba0beae 426 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING migth be problematic
57e35f32 427 */
bba0beae 428function is_dataroot_insecure($fetchtest=false) {
57e35f32 429 global $CFG;
430
431 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
432
433 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
434 $rp = strrev(trim($rp, '/'));
435 $rp = explode('/', $rp);
436 foreach($rp as $r) {
437 if (strpos($siteroot, '/'.$r.'/') === 0) {
438 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
439 } else {
440 break; // probably alias root
441 }
442 }
443
444 $siteroot = strrev($siteroot);
445 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
446
bba0beae 447 if (strpos($dataroot, $siteroot) !== 0) {
448 return false;
449 }
450
451 if (!$fetchtest) {
452 return INSECURE_DATAROOT_WARNING;
453 }
454
455 // now try all methods to fetch a test file using http protocol
456
457 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
458 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
459 $httpdocroot = $matches[1];
460 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
461 if (make_upload_directory('diag', false) === false) {
462 return INSECURE_DATAROOT_WARNING;
463 }
464 $testfile = $CFG->dataroot.'/diag/public.txt';
465 if (!file_exists($testfile)) {
466 file_put_contents($testfile, 'test file, do not delete');
467 }
468 $teststr = trim(file_get_contents($testfile));
469 if (empty($teststr)) {
9baf6825 470 // hmm, strange
bba0beae 471 return INSECURE_DATAROOT_WARNING;
472 }
473
474 $testurl = $datarooturl.'/diag/public.txt';
041a4b0f 475 if (extension_loaded('curl') and
476 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
477 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
478 ($ch = @curl_init($testurl)) !== false) {
bba0beae 479 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
480 curl_setopt($ch, CURLOPT_HEADER, false);
481 $data = curl_exec($ch);
482 if (!curl_errno($ch)) {
483 $data = trim($data);
484 if ($data === $teststr) {
485 curl_close($ch);
486 return INSECURE_DATAROOT_ERROR;
487 }
488 }
489 curl_close($ch);
490 }
491
492 if ($data = @file_get_contents($testurl)) {
493 $data = trim($data);
494 if ($data === $teststr) {
495 return INSECURE_DATAROOT_ERROR;
496 }
497 }
498
499 preg_match('|https?://([^/]+)|i', $testurl, $matches);
500 $sitename = $matches[1];
501 $error = 0;
502 if ($fp = @fsockopen($sitename, 80, $error)) {
503 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
504 $localurl = $matches[1];
505 $out = "GET $localurl HTTP/1.1\r\n";
506 $out .= "Host: $sitename\r\n";
507 $out .= "Connection: Close\r\n\r\n";
508 fwrite($fp, $out);
509 $data = '';
510 $incoming = false;
511 while (!feof($fp)) {
512 if ($incoming) {
513 $data .= fgets($fp, 1024);
514 } else if (@fgets($fp, 1024) === "\r\n") {
9baf6825 515 $incoming = true;
516 }
bba0beae 517 }
518 fclose($fp);
519 $data = trim($data);
520 if ($data === $teststr) {
521 return INSECURE_DATAROOT_ERROR;
522 }
57e35f32 523 }
bba0beae 524
525 return INSECURE_DATAROOT_WARNING;
57e35f32 526}
6e4dc10f 527
6e4dc10f 528/// CLASS DEFINITIONS /////////////////////////////////////////////////////////
529
530/**
531 * Pseudointerface for anything appearing in the admin tree
532 *
533 * The pseudointerface that is implemented by anything that appears in the admin tree
534 * block. It forces inheriting classes to define a method for checking user permissions
535 * and methods for finding something in the admin tree.
536 *
db26acd4 537 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 538 */
73fa96d5 539interface part_of_admin_tree {
6e4dc10f 540
9baf6825 541/**
542 * Finds a named part_of_admin_tree.
543 *
544 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
545 * and not parentable_part_of_admin_tree, then this function should only check if
546 * $this->name matches $name. If it does, it should return a reference to $this,
547 * otherwise, it should return a reference to NULL.
548 *
549 * If a class inherits parentable_part_of_admin_tree, this method should be called
550 * recursively on all child objects (assuming, of course, the parent object's name
551 * doesn't match the search criterion).
552 *
553 * @param string $name The internal name of the part_of_admin_tree we're searching for.
554 * @return mixed An object reference or a NULL reference.
555 */
73fa96d5 556 public function locate($name);
4672d955 557
558 /**
559 * Removes named part_of_admin_tree.
560 *
561 * @param string $name The internal name of the part_of_admin_tree we want to remove.
a8a66c96 562 * @return bool success.
4672d955 563 */
73fa96d5 564 public function prune($name);
4672d955 565
220a90c5 566 /**
567 * Search using query
db26acd4 568 * @param string $query
220a90c5 569 * @return mixed array-object structure of found settings and pages
570 */
73fa96d5 571 public function search($query);
220a90c5 572
6e4dc10f 573 /**
574 * Verifies current user's access to this part_of_admin_tree.
575 *
576 * Used to check if the current user has access to this part of the admin tree or
577 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
578 * then this method is usually just a call to has_capability() in the site context.
579 *
580 * If a class inherits parentable_part_of_admin_tree, this method should return the
581 * logical OR of the return of check_access() on all child objects.
582 *
583 * @return bool True if the user has access, false if she doesn't.
584 */
73fa96d5 585 public function check_access();
eef868d1 586
a8a66c96 587 /**
588 * Mostly usefull for removing of some parts of the tree in admin tree block.
589 *
590 * @return True is hidden from normal list view
591 */
73fa96d5 592 public function is_hidden();
6e4dc10f 593}
594
595/**
596 * Pseudointerface implemented by any part_of_admin_tree that has children.
597 *
598 * The pseudointerface implemented by any part_of_admin_tree that can be a parent
599 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
eef868d1 600 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
6e4dc10f 601 * include an add method for adding other part_of_admin_tree objects as children.
602 *
db26acd4 603 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 604 */
73fa96d5 605interface parentable_part_of_admin_tree extends part_of_admin_tree {
eef868d1 606
9baf6825 607/**
608 * Adds a part_of_admin_tree object to the admin tree.
609 *
610 * Used to add a part_of_admin_tree object to this object or a child of this
611 * object. $something should only be added if $destinationname matches
612 * $this->name. If it doesn't, add should be called on child objects that are
613 * also parentable_part_of_admin_tree's.
614 *
615 * @param string $destinationname The internal name of the new parent for $something.
616 * @param part_of_admin_tree $something The object to be added.
617 * @return bool True on success, false on failure.
618 */
73fa96d5 619 public function add($destinationname, $something);
eef868d1 620
6e4dc10f 621}
622
623/**
624 * The object used to represent folders (a.k.a. categories) in the admin tree block.
eef868d1 625 *
6e4dc10f 626 * Each admin_category object contains a number of part_of_admin_tree objects.
627 *
db26acd4 628 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 629 */
73fa96d5 630class admin_category implements parentable_part_of_admin_tree {
6e4dc10f 631
9baf6825 632/** @var mixed An array of part_of_admin_tree objects that are this object's children */
73fa96d5 633 public $children;
0c079f19 634 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 635 public $name;
0c079f19 636 /** @var string The displayed name for this category. Usually obtained through get_string() */
73fa96d5 637 public $visiblename;
0c079f19 638 /** @var bool Should this category be hidden in admin tree block? */
73fa96d5 639 public $hidden;
0c079f19 640 /** @var mixed Either a string or an array or strings */
73fa96d5 641 public $path;
0c079f19 642 /** @var mixed Either a string or an array or strings */
73fa96d5 643 public $visiblepath;
6e4dc10f 644
645 /**
646 * Constructor for an empty admin category
647 *
648 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
649 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
db26acd4 650 * @param bool $hidden hide category in admin tree block, defaults to false
6e4dc10f 651 */
73fa96d5 652 public function __construct($name, $visiblename, $hidden=false) {
220a90c5 653 $this->children = array();
654 $this->name = $name;
6e4dc10f 655 $this->visiblename = $visiblename;
220a90c5 656 $this->hidden = $hidden;
6e4dc10f 657 }
eef868d1 658
6e4dc10f 659 /**
220a90c5 660 * Returns a reference to the part_of_admin_tree object with internal name $name.
6e4dc10f 661 *
220a90c5 662 * @param string $name The internal name of the object we want.
663 * @param bool $findpath initialize path and visiblepath arrays
0c079f19 664 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
db26acd4 665 * defaults to false
6e4dc10f 666 */
73fa96d5 667 public function locate($name, $findpath=false) {
6e4dc10f 668 if ($this->name == $name) {
220a90c5 669 if ($findpath) {
670 $this->visiblepath[] = $this->visiblename;
671 $this->path[] = $this->name;
672 }
673 return $this;
6e4dc10f 674 }
eef868d1 675
220a90c5 676 $return = NULL;
677 foreach($this->children as $childid=>$unused) {
73fa96d5 678 if ($return = $this->children[$childid]->locate($name, $findpath)) {
220a90c5 679 break;
6e4dc10f 680 }
681 }
eef868d1 682
220a90c5 683 if (!is_null($return) and $findpath) {
684 $return->visiblepath[] = $this->visiblename;
685 $return->path[] = $this->name;
686 }
eef868d1 687
220a90c5 688 return $return;
6e4dc10f 689 }
690
691 /**
220a90c5 692 * Search using query
db26acd4 693 *
694 * @param string query
220a90c5 695 * @return mixed array-object structure of found settings and pages
6e4dc10f 696 */
73fa96d5 697 public function search($query) {
220a90c5 698 $result = array();
699 foreach ($this->children as $child) {
3cea9c55 700 $subsearch = $child->search($query);
701 if (!is_array($subsearch)) {
702 debugging('Incorrect search result from '.$child->name);
703 continue;
704 }
705 $result = array_merge($result, $subsearch);
6e4dc10f 706 }
220a90c5 707 return $result;
6e4dc10f 708 }
709
4672d955 710 /**
711 * Removes part_of_admin_tree object with internal name $name.
712 *
713 * @param string $name The internal name of the object we want to remove.
a8a66c96 714 * @return bool success
4672d955 715 */
73fa96d5 716 public function prune($name) {
4672d955 717
718 if ($this->name == $name) {
719 return false; //can not remove itself
720 }
721
722 foreach($this->children as $precedence => $child) {
723 if ($child->name == $name) {
9baf6825 724 // found it!
eef868d1 725 unset($this->children[$precedence]);
4672d955 726 return true;
727 }
728 if ($this->children[$precedence]->prune($name)) {
729 return true;
730 }
731 }
732 return false;
733 }
734
6e4dc10f 735 /**
736 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
737 *
220a90c5 738 * @param string $destinationame The internal name of the immediate parent that we want for $something.
739 * @param mixed $something A part_of_admin_tree or setting instanceto be added.
740 * @return bool True if successfully added, false if $something can not be added.
6e4dc10f 741 */
73fa96d5 742 public function add($parentname, $something) {
743 $parent = $this->locate($parentname);
220a90c5 744 if (is_null($parent)) {
745 debugging('parent does not exist!');
6e4dc10f 746 return false;
747 }
748
73fa96d5 749 if ($something instanceof part_of_admin_tree) {
750 if (!($parent instanceof parentable_part_of_admin_tree)) {
220a90c5 751 debugging('error - parts of tree can be inserted only into parentable parts');
752 return false;
6e4dc10f 753 }
220a90c5 754 $parent->children[] = $something;
6e4dc10f 755 return true;
eef868d1 756
220a90c5 757 } else {
758 debugging('error - can not add this element');
759 return false;
6e4dc10f 760 }
eef868d1 761
6e4dc10f 762 }
eef868d1 763
6e4dc10f 764 /**
765 * Checks if the user has access to anything in this category.
766 *
767 * @return bool True if the user has access to atleast one child in this category, false otherwise.
768 */
73fa96d5 769 public function check_access() {
6e4dc10f 770 foreach ($this->children as $child) {
220a90c5 771 if ($child->check_access()) {
772 return true;
773 }
6e4dc10f 774 }
220a90c5 775 return false;
6e4dc10f 776 }
eef868d1 777
a8a66c96 778 /**
779 * Is this category hidden in admin tree block?
780 *
781 * @return bool True if hidden
782 */
73fa96d5 783 public function is_hidden() {
a8a66c96 784 return $this->hidden;
785 }
6e4dc10f 786}
787
db26acd4 788/**
0c079f19 789 * Root of admin settings tree, does not have any parent.
db26acd4 790 *
791 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 792 */
220a90c5 793class admin_root extends admin_category {
9baf6825 794/** @var array List of errors */
73fa96d5 795 public $errors;
0c079f19 796 /** @var string search query */
73fa96d5 797 public $search;
0c079f19 798 /** @var bool full tree flag - true means all settings required, false onlypages required */
73fa96d5 799 public $fulltree;
0c079f19 800 /** @var bool flag indicating loaded tree */
73fa96d5 801 public $loaded;
0c079f19 802 /** @var mixed site custom defaults overriding defaults in setings files*/
cd3acbf2 803 public $custom_defaults;
804
db26acd4 805 /**
0c079f19 806 * @param bool $fulltree true means all settings required,
db26acd4 807 * false only pages required
808 */
73fa96d5 809 public function __construct($fulltree) {
cd3acbf2 810 global $CFG;
811
73fa96d5 812 parent::__construct('root', get_string('administration'), false);
220a90c5 813 $this->errors = array();
814 $this->search = '';
73fa96d5 815 $this->fulltree = $fulltree;
816 $this->loaded = false;
cd3acbf2 817
818 // load custom defaults if found
819 $this->custom_defaults = null;
820 $defaultsfile = "$CFG->dirroot/local/defaults.php";
821 if (is_readable($defaultsfile)) {
822 $defaults = array();
823 include($defaultsfile);
824 if (is_array($defaults) and count($defaults)) {
825 $this->custom_defaults = $defaults;
826 }
827 }
73fa96d5 828 }
829
db26acd4 830 /**
831 * Empties children array, and sets loaded to false
832 *
833 * @param bool $requirefulltree
834 */
73fa96d5 835 public function purge_children($requirefulltree) {
836 $this->children = array();
837 $this->fulltree = ($requirefulltree || $this->fulltree);
838 $this->loaded = false;
220a90c5 839 }
840}
841
6e4dc10f 842/**
843 * Links external PHP pages into the admin tree.
844 *
845 * See detailed usage example at the top of this document (adminlib.php)
846 *
db26acd4 847 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 848 */
73fa96d5 849class admin_externalpage implements part_of_admin_tree {
6e4dc10f 850
9baf6825 851/** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 852 public $name;
eef868d1 853
0c079f19 854 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
73fa96d5 855 public $visiblename;
eef868d1 856
0c079f19 857 /** @var string The external URL that we should link to when someone requests this external page. */
73fa96d5 858 public $url;
eef868d1 859
0c079f19 860 /** @var string The role capability/permission a user must have to access this external page. */
73fa96d5 861 public $req_capability;
eef868d1 862
0c079f19 863 /** @var object The context in which capability/permission should be checked, default is site context. */
73fa96d5 864 public $context;
84c8ede0 865
0c079f19 866 /** @var bool hidden in admin tree block. */
73fa96d5 867 public $hidden;
a8a66c96 868
0c079f19 869 /** @var mixed either string or array of string */
73fa96d5 870 public $path;
871 public $visiblepath;
220a90c5 872
6e4dc10f 873 /**
874 * Constructor for adding an external page into the admin tree.
875 *
876 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
877 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
878 * @param string $url The external URL that we should link to when someone requests this external page.
38d2d43b 879 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
92f00846 880 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
881 * @param context $context The context the page relates to. Not sure what happens
882 * if you specify something other than system or front page. Defaults to system.
6e4dc10f 883 */
73fa96d5 884 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
220a90c5 885 $this->name = $name;
6e4dc10f 886 $this->visiblename = $visiblename;
220a90c5 887 $this->url = $url;
38d2d43b 888 if (is_array($req_capability)) {
889 $this->req_capability = $req_capability;
890 } else {
891 $this->req_capability = array($req_capability);
892 }
92f00846 893 $this->hidden = $hidden;
84c8ede0 894 $this->context = $context;
6e4dc10f 895 }
eef868d1 896
6e4dc10f 897 /**
898 * Returns a reference to the part_of_admin_tree object with internal name $name.
899 *
900 * @param string $name The internal name of the object we want.
db26acd4 901 * @param bool $findpath defaults to false
6e4dc10f 902 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
903 */
73fa96d5 904 public function locate($name, $findpath=false) {
220a90c5 905 if ($this->name == $name) {
906 if ($findpath) {
907 $this->visiblepath = array($this->visiblename);
908 $this->path = array($this->name);
909 }
910 return $this;
911 } else {
912 $return = NULL;
913 return $return;
914 }
6e4dc10f 915 }
4672d955 916
db26acd4 917 /**
918 * This function always returns false, required function by interface
919 *
920 * @param string $name
921 * @return false
922 */
73fa96d5 923 public function prune($name) {
4672d955 924 return false;
925 }
926
220a90c5 927 /**
928 * Search using query
db26acd4 929 *
930 * @param string $query
220a90c5 931 * @return mixed array-object structure of found settings and pages
932 */
73fa96d5 933 public function search($query) {
220a90c5 934 $textlib = textlib_get_instance();
935
936 $found = false;
937 if (strpos(strtolower($this->name), $query) !== false) {
938 $found = true;
939 } else if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
9baf6825 940 $found = true;
941 }
220a90c5 942 if ($found) {
943 $result = new object();
944 $result->page = $this;
945 $result->settings = array();
946 return array($this->name => $result);
947 } else {
948 return array();
949 }
950 }
951
6e4dc10f 952 /**
2ce38b70 953 * Determines if the current user has access to this external page based on $this->req_capability.
db26acd4 954 *
6e4dc10f 955 * @return bool True if user has access, false otherwise.
956 */
73fa96d5 957 public function check_access() {
1caea91e 958 global $CFG;
84c8ede0 959 $context = empty($this->context) ? get_context_instance(CONTEXT_SYSTEM) : $this->context;
38d2d43b 960 foreach($this->req_capability as $cap) {
1a5b427e 961 if (is_valid_capability($cap) and has_capability($cap, $context)) {
38d2d43b 962 return true;
963 }
964 }
965 return false;
6e4dc10f 966 }
967
a8a66c96 968 /**
969 * Is this external page hidden in admin tree block?
970 *
971 * @return bool True if hidden
972 */
73fa96d5 973 public function is_hidden() {
a8a66c96 974 return $this->hidden;
975 }
976
6e4dc10f 977}
978
979/**
980 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
981 *
db26acd4 982 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6e4dc10f 983 */
73fa96d5 984class admin_settingpage implements part_of_admin_tree {
6e4dc10f 985
9baf6825 986/** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
73fa96d5 987 public $name;
eef868d1 988
0c079f19 989 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
73fa96d5 990 public $visiblename;
0c079f19 991
992 /** @var mixed An array of admin_setting objects that are part of this setting page. */
73fa96d5 993 public $settings;
eef868d1 994
0c079f19 995 /** @var string The role capability/permission a user must have to access this external page. */
73fa96d5 996 public $req_capability;
eef868d1 997
0c079f19 998 /** @var object The context in which capability/permission should be checked, default is site context. */
73fa96d5 999 public $context;
84c8ede0 1000
0c079f19 1001 /** @var bool hidden in admin tree block. */
73fa96d5 1002 public $hidden;
a8a66c96 1003
0c079f19 1004 /** @var mixed string of paths or array of strings of paths */
73fa96d5 1005 public $path;
1006 public $visiblepath;
220a90c5 1007
db26acd4 1008 /**
1009 * see admin_settingpage for details of this function
0c079f19 1010 *
db26acd4 1011 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1012 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1013 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1014 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1015 * @param context $context The context the page relates to. Not sure what happens
1016 * if you specify something other than system or front page. Defaults to system.
1017 */
73fa96d5 1018 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
220a90c5 1019 $this->settings = new object();
1020 $this->name = $name;
1021 $this->visiblename = $visiblename;
1022 if (is_array($req_capability)) {
1023 $this->req_capability = $req_capability;
6e4dc10f 1024 } else {
220a90c5 1025 $this->req_capability = array($req_capability);
6e4dc10f 1026 }
220a90c5 1027 $this->hidden = $hidden;
1028 $this->context = $context;
6e4dc10f 1029 }
eef868d1 1030
0c079f19 1031 /**
db26acd4 1032 * see admin_category
1033 *
1034 * @param string $name
1035 * @param bool $findpath
1036 * @return mixed Object (this) if name == this->name, else returns null
1037 */
73fa96d5 1038 public function locate($name, $findpath=false) {
220a90c5 1039 if ($this->name == $name) {
1040 if ($findpath) {
1041 $this->visiblepath = array($this->visiblename);
1042 $this->path = array($this->name);
1043 }
1044 return $this;
1045 } else {
1046 $return = NULL;
1047 return $return;
1048 }
6e4dc10f 1049 }
4672d955 1050
0c079f19 1051 /**
1052 * Search string in settings page.
1053 *
db26acd4 1054 * @param string $query
1055 * @return array
1056 */
73fa96d5 1057 public function search($query) {
220a90c5 1058 $found = array();
4672d955 1059
220a90c5 1060 foreach ($this->settings as $setting) {
1061 if ($setting->is_related($query)) {
1062 $found[] = $setting;
1063 }
1064 }
1065
1066 if ($found) {
1067 $result = new object();
1068 $result->page = $this;
1069 $result->settings = $found;
1070 return array($this->name => $result);
1071 }
1072
1073 $textlib = textlib_get_instance();
1074
1075 $found = false;
1076 if (strpos(strtolower($this->name), $query) !== false) {
1077 $found = true;
1078 } else if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
9baf6825 1079 $found = true;
1080 }
220a90c5 1081 if ($found) {
1082 $result = new object();
1083 $result->page = $this;
1084 $result->settings = array();
1085 return array($this->name => $result);
38d2d43b 1086 } else {
220a90c5 1087 return array();
38d2d43b 1088 }
6e4dc10f 1089 }
eef868d1 1090
db26acd4 1091 /**
1092 * This function always returns false, required by interface
1093 *
1094 * @param string $name
1095 * @return bool Always false
1096 */
73fa96d5 1097 public function prune($name) {
6e4dc10f 1098 return false;
1099 }
eef868d1 1100
220a90c5 1101 /**
db26acd4 1102 * adds an admin_setting to this admin_settingpage
1103 *
220a90c5 1104 * 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
1105 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
db26acd4 1106 *
220a90c5 1107 * @param object $setting is the admin_setting object you want to add
db26acd4 1108 * @return bool true if successful, false if not
220a90c5 1109 */
73fa96d5 1110 public function add($setting) {
1111 if (!($setting instanceof admin_setting)) {
220a90c5 1112 debugging('error - not a setting instance');
1113 return false;
1114 }
1115
1116 $this->settings->{$setting->name} = $setting;
1117 return true;
1118 }
1119
db26acd4 1120 /**
1121 * see admin_externalpage
1122 *
1123 * @return bool Returns true for yes false for no
1124 */
73fa96d5 1125 public function check_access() {
1caea91e 1126 global $CFG;
84c8ede0 1127 $context = empty($this->context) ? get_context_instance(CONTEXT_SYSTEM) : $this->context;
38d2d43b 1128 foreach($this->req_capability as $cap) {
1a5b427e 1129 if (is_valid_capability($cap) and has_capability($cap, $context)) {
38d2d43b 1130 return true;
1131 }
1132 }
1133 return false;
6e4dc10f 1134 }
eef868d1 1135
220a90c5 1136 /**
1137 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
db26acd4 1138 * @return string Returns an XHTML string
220a90c5 1139 */
73fa96d5 1140 public function output_html() {
1141 $adminroot = admin_get_root();
220a90c5 1142 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
6e4dc10f 1143 foreach($this->settings as $setting) {
220a90c5 1144 $fullname = $setting->get_full_name();
1145 if (array_key_exists($fullname, $adminroot->errors)) {
1146 $data = $adminroot->errors[$fullname]->data;
6e4dc10f 1147 } else {
220a90c5 1148 $data = $setting->get_setting();
9baf6825 1149 // do not use defaults if settings not available - upgrdesettings handles the defaults!
6e4dc10f 1150 }
220a90c5 1151 $return .= $setting->output_html($data);
6e4dc10f 1152 }
220a90c5 1153 $return .= '</fieldset>';
6e4dc10f 1154 return $return;
1155 }
1156
a8a66c96 1157 /**
1158 * Is this settigns page hidden in admin tree block?
1159 *
1160 * @return bool True if hidden
1161 */
73fa96d5 1162 public function is_hidden() {
a8a66c96 1163 return $this->hidden;
1164 }
1165
6e4dc10f 1166}
1167
1168
220a90c5 1169/**
1170 * Admin settings class. Only exists on setting pages.
1171 * Read & write happens at this level; no authentication.
db26acd4 1172 *
1173 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1174 */
301bf0b2 1175abstract class admin_setting {
9baf6825 1176/** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
73fa96d5 1177 public $name;
0c079f19 1178 /** @var string localised name */
73fa96d5 1179 public $visiblename;
0c079f19 1180 /** @var string localised long description */
73fa96d5 1181 public $description;
0c079f19 1182 /** @var mixed Can be string or array of string */
73fa96d5 1183 public $defaultsetting;
0c079f19 1184 /** @var string */
73fa96d5 1185 public $updatedcallback;
0c079f19 1186 /** @var mixed can be String or Null. Null means main config table */
73fa96d5 1187 public $plugin; // null means main config table
6e4dc10f 1188
220a90c5 1189 /**
1190 * Constructor
0c079f19 1191 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
db26acd4 1192 * or 'myplugin/mysetting' for ones in config_plugins.
1a41e806 1193 * @param string $visiblename localised name
1194 * @param string $description localised long description
220a90c5 1195 * @param mixed $defaultsetting string or array depending on implementation
1196 */
73fa96d5 1197 public function __construct($name, $visiblename, $description, $defaultsetting) {
7fb0303d 1198 $this->parse_setting_name($name);
220a90c5 1199 $this->visiblename = $visiblename;
1200 $this->description = $description;
6e4dc10f 1201 $this->defaultsetting = $defaultsetting;
1202 }
eef868d1 1203
7fb0303d 1204 /**
db26acd4 1205 * Set up $this->name and potentially $this->plugin
1206 *
7fb0303d 1207 * Set up $this->name and possibly $this->plugin based on whether $name looks
1208 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1209 * on the names, that is, output a developer debug warning if the name
1210 * contains anything other than [a-zA-Z0-9_]+.
1211 *
1212 * @param string $name the setting name passed in to the constructor.
1213 */
1214 private function parse_setting_name($name) {
1215 $bits = explode('/', $name);
1216 if (count($bits) > 2) {
1217 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1218 }
1219 $this->name = array_pop($bits);
1220 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1221 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1222 }
1223 if (!empty($bits)) {
1224 $this->plugin = array_pop($bits);
cd3acbf2 1225 if ($this->plugin === 'moodle') {
1226 $this->plugin = null;
1227 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
9baf6825 1228 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1229 }
7fb0303d 1230 }
1231 }
1232
db26acd4 1233 /**
1234 * Returns the fullname prefixed by the plugin
1235 * @return string
1236 */
73fa96d5 1237 public function get_full_name() {
220a90c5 1238 return 's_'.$this->plugin.'_'.$this->name;
1239 }
1240
db26acd4 1241 /**
1242 * Returns the ID string based on plugin and name
1243 * @return string
1244 */
73fa96d5 1245 public function get_id() {
220a90c5 1246 return 'id_s_'.$this->plugin.'_'.$this->name;
1247 }
1248
db26acd4 1249 /**
1250 * Returns the config if possible
1251 *
db26acd4 1252 * @return mixed returns config if successfull else null
1253 */
73fa96d5 1254 public function config_read($name) {
220a90c5 1255 global $CFG;
eb6a973c 1256 if (!empty($this->plugin)) {
220a90c5 1257 $value = get_config($this->plugin, $name);
1258 return $value === false ? NULL : $value;
1259
1260 } else {
1261 if (isset($CFG->$name)) {
1262 return $CFG->$name;
1263 } else {
1264 return NULL;
1265 }
1266 }
1267 }
1268
301bf0b2 1269 /**
db26acd4 1270 * Used to set a config pair and log change
301bf0b2 1271 *
db26acd4 1272 * @param string $name
1273 * @param mixed $value Gets converted to string if not null
1274 * @return bool Write setting to confix table
301bf0b2 1275 */
73fa96d5 1276 public function config_write($name, $value) {
301bf0b2 1277 global $DB, $USER, $CFG;
1278
1279 // make sure it is a real change
1280 $oldvalue = get_config($this->plugin, $name);
1281 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1282 $value = is_null($value) ? null : (string)$value;
1283
1284 if ($oldvalue === $value) {
1285 return true;
1286 }
1287
1288 // store change
1289 set_config($name, $value, $this->plugin);
1290
301bf0b2 1291 // log change
1292 $log = new object();
31a99877 1293 $log->userid = during_initial_install() ? 0 :$USER->id; // 0 as user id during install
301bf0b2 1294 $log->timemodified = time();
1295 $log->plugin = $this->plugin;
1296 $log->name = $name;
1297 $log->value = $value;
1298 $log->oldvalue = $oldvalue;
1299 $DB->insert_record('config_log', $log);
1300
1301 return true; // BC only
220a90c5 1302 }
1303
1304 /**
1305 * Returns current value of this setting
1306 * @return mixed array or string depending on instance, NULL means not set yet
1307 */
301bf0b2 1308 public abstract function get_setting();
eef868d1 1309
220a90c5 1310 /**
1311 * Returns default setting if exists
1312 * @return mixed array or string depending on instance; NULL means no default, user must supply
1313 */
73fa96d5 1314 public function get_defaultsetting() {
cd3acbf2 1315 $adminroot = admin_get_root(false, false);
1316 if (!empty($adminroot->custom_defaults)) {
1317 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1318 if (isset($adminroot->custom_defaults[$plugin])) {
1319 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid valie here ;-)
1320 return $adminroot->custom_defaults[$plugin][$this->name];
1321 }
1322 }
1323 }
8e5da17a 1324 return $this->defaultsetting;
1325 }
1326
220a90c5 1327 /**
1328 * Store new setting
db26acd4 1329 *
1330 * @param mixed $data string or array, must not be NULL
1331 * @return string empty string if ok, string error message otherwise
220a90c5 1332 */
301bf0b2 1333 public abstract function write_setting($data);
eef868d1 1334
220a90c5 1335 /**
1336 * Return part of form with setting
db26acd4 1337 * This function should always be overwritten
1338 *
1339 * @param mixed $data array or string depending on setting
1340 * @param string $query
220a90c5 1341 * @return string
1342 */
73fa96d5 1343 public function output_html($data, $query='') {
9baf6825 1344 // should be overridden
220a90c5 1345 return;
1346 }
1347
1348 /**
db26acd4 1349 * Function called if setting updated - cleanup, cache reset, etc.
1350 * @param string $functionname Sets the function name
220a90c5 1351 */
73fa96d5 1352 public function set_updatedcallback($functionname) {
220a90c5 1353 $this->updatedcallback = $functionname;
1354 }
1355
1356 /**
1357 * Is setting related to query text - used when searching
1358 * @param string $query
1359 * @return bool
1360 */
73fa96d5 1361 public function is_related($query) {
220a90c5 1362 if (strpos(strtolower($this->name), $query) !== false) {
1363 return true;
1364 }
1365 $textlib = textlib_get_instance();
1366 if (strpos($textlib->strtolower($this->visiblename), $query) !== false) {
1367 return true;
1368 }
1369 if (strpos($textlib->strtolower($this->description), $query) !== false) {
1370 return true;
1371 }
587c7040 1372 $current = $this->get_setting();
1373 if (!is_null($current)) {
1374 if (is_string($current)) {
1375 if (strpos($textlib->strtolower($current), $query) !== false) {
1376 return true;
1377 }
1378 }
1379 }
1380 $default = $this->get_defaultsetting();
1381 if (!is_null($default)) {
1382 if (is_string($default)) {
1383 if (strpos($textlib->strtolower($default), $query) !== false) {
1384 return true;
1385 }
1386 }
1387 }
220a90c5 1388 return false;
6e4dc10f 1389 }
220a90c5 1390}
eef868d1 1391
220a90c5 1392/**
1393 * No setting - just heading and text.
db26acd4 1394 *
1395 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1396 */
1397class admin_setting_heading extends admin_setting {
9baf6825 1398/**
1399 * not a setting, just text
1400 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1401 * @param string $heading heading
1402 * @param string $information text in box
1403 */
73fa96d5 1404 public function __construct($name, $heading, $information) {
1405 parent::__construct($name, $heading, $information, '');
220a90c5 1406 }
1407
db26acd4 1408 /**
1409 * Always returns true
1410 * @return bool Always returns true
1411 */
73fa96d5 1412 public function get_setting() {
220a90c5 1413 return true;
1414 }
1415
db26acd4 1416 /**
1417 * Always returns true
1418 * @return bool Always returns true
1419 */
73fa96d5 1420 public function get_defaultsetting() {
220a90c5 1421 return true;
1422 }
1423
db26acd4 1424 /**
1425 * Never write settings
1426 * @return string Always returns an empty string
1427 */
73fa96d5 1428 public function write_setting($data) {
9baf6825 1429 // do not write any setting
220a90c5 1430 return '';
1431 }
0c079f19 1432
db26acd4 1433 /**
1434 * Returns an HTML string
1435 * @return string Returns an HTML string
1436 */
73fa96d5 1437 public function output_html($data, $query='') {
3c159385 1438 global $OUTPUT;
220a90c5 1439 $return = '';
1440 if ($this->visiblename != '') {
3c159385 1441 $return .= $OUTPUT->heading('<a name="'.$this->name.'">'.highlightfast($query, $this->visiblename).'</a>', 3, 'main', true);
220a90c5 1442 }
1443 if ($this->description != '') {
ea85e1ee 1444 $return .= $OUTPUT->box(highlight($query, $this->description), 'generalbox formsettingheading');
220a90c5 1445 }
1446 return $return;
1447 }
1448}
6e4dc10f 1449
220a90c5 1450/**
1451 * The most flexibly setting, user is typing text
db26acd4 1452 *
1453 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 1454 */
6e4dc10f 1455class admin_setting_configtext extends admin_setting {
1456
9baf6825 1457/** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
73fa96d5 1458 public $paramtype;
0c079f19 1459 /** @var int default field size */
73fa96d5 1460 public $size;
6e4dc10f 1461
220a90c5 1462 /**
db26acd4 1463 * Config text contructor
1464 *
1a41e806 1465 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
220a90c5 1466 * @param string $visiblename localised
1467 * @param string $description long localised info
1468 * @param string $defaultsetting
1469 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
f7633b0f 1470 * @param int $size default field size
220a90c5 1471 */
73fa96d5 1472 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
6e4dc10f 1473 $this->paramtype = $paramtype;
f7633b0f 1474 if (!is_null($size)) {
1475 $this->size = $size;
1476 } else {
1477 $this->size = ($paramtype == PARAM_INT) ? 5 : 30;
1478 }
73fa96d5 1479 parent::__construct($name, $visiblename, $description, $defaultsetting);
6e4dc10f 1480 }
1481
db26acd4 1482 /**
1483 * Return the setting
1484 *
1485 * @return mixed returns config if successfull else null
1486 */
73fa96d5 1487 public function get_setting() {
220a90c5 1488 return $this->config_read($this->name);
6e4dc10f 1489 }
eef868d1 1490
73fa96d5 1491 public function write_setting($data) {
8cad6cca 1492 if ($this->paramtype === PARAM_INT and $data === '') {
9baf6825 1493 // do not complain if '' used instead of 0
8cad6cca 1494 $data = 0;
1495 }
220a90c5 1496 // $data is a string
c5d2d0dd 1497 $validated = $this->validate($data);
e33fbf87 1498 if ($validated !== true) {
1499 return $validated;
c235598d 1500 }
220a90c5 1501 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 1502 }
1503
e33fbf87 1504 /**
1505 * Validate data before storage
1506 * @param string data
1507 * @return mixed true if ok string if error found
1508 */
73fa96d5 1509 public function validate($data) {
58aaa8e9 1510 // allow paramtype to be a custom regex if it is the form of /pattern/
1511 if (preg_match('#^/.*/$#', $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) {
4ea56b3f 1519 return true;
9baf6825 1520
4ea56b3f 1521 } else {
1522 $cleaned = clean_param($data, $this->paramtype);
1523 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
1524 return true;
e33fbf87 1525 } else {
4ea56b3f 1526 return get_string('validateerror', 'admin');
e33fbf87 1527 }
4ea56b3f 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,
9baf6825 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>',
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,
9baf6825 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);
4fe2250a 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,
9baf6825 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>',
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 {
9baf6825 1638/**
1639 * Constructor
1640 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
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,
9baf6825 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>',
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 {
9baf6825 1703/**
1704 * Constructor
1705 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
1706 * @param string $visiblename localised
1707 * @param string $description long localised info
1708 * @param string $defaultdirectory default directory location
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,
9baf6825 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>',
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
9baf6825 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,
9baf6825 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>',
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
9baf6825 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,
9baf6825 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>',
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 {
9baf6825 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,
9baf6825 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 {
9baf6825 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.' />'
9baf6825 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)) {
9baf6825 2045 $defaultinfo = implode(', ', $defaults);
2046 } else {
2047 $defaultinfo = get_string('none');
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
9baf6825 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 {
9baf6825 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)) {
9baf6825 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
9baf6825 2236 } else if (!array_key_exists($current, $this->choices)) {
2237 $warning = get_string('warningcurrentsetting', 'admin', s($current));
2238 if (!is_null($default) and $data == $current) {
2239 $data = $default; // use default instead of first value when showing the form
2240 }
2241 }
9c305ba1 2242
e2249afe 2243 $selecthtml = '<select id="'.$this->get_id().'" name="'.$this->get_full_name().$extraname.'">';
6e4dc10f 2244 foreach ($this->choices as $key => $value) {
9baf6825 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 {
9baf6825 2288/**
2289 * Constructor
2290 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
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 {
9baf6825 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">'.
9baf6825 2492 '<select id="'.$this->get_id().'h" name="'.$this->get_full_name().'[h]">';
220a90c5 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
9baf6825 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) ||
9baf6825 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)) {
4e639121 2534 $result = true;
2535 } else {
2536 $result = false;
2537 break;
2538 }
2539 }
9baf6825 2540 if($result) {
4e639121 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 {
9baf6825 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),
9baf6825 2589 $this->capability, 'u.id,u.username,u.firstname,u.lastname', 'u.lastname,u.firstname');
fb073f60 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)) {
9baf6825 2611 return $defaultsetting;
2612 } else {
2613 return '';
2614 }
4413941f 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) {
9baf6825 2637 // If all is selected, remove any explicit options.
4413941f 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 {
9baf6825 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'),
9baf6825 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 {
9baf6825 2687/**
2688 * Reads the setting directly from the database
2689 *
2690 * @return mixed
2691 */
73fa96d5 2692 public function get_setting() {
9baf6825 2693 // read directly from db!
392e7363 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 {
9baf6825 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
ca497f4f 2751/**
2752 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
2753 * block to hidden.
2754 *
2755 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2756 */
2757class admin_setting_bloglevel extends admin_setting_configselect {
2758 /**
2759 * Updates the database and save the setting
2760 *
2761 * @param string data
2762 * @return string empty or error message
2763 */
2764 public function write_setting($data) {
2765 global $DB;
2766 if ($data['bloglevel'] == 0) {
2767 $DB->set_field('block', 'visible', 0, array('name' => 'blog_menu'));
2768 } else {
2769 $DB->set_field('block', 'visible', 1, array('name' => 'blog_menu'));
2770 }
2771 return parent::write_setting($data);
2772 }
2773}
2774
220a90c5 2775/**
2776 * Special select - lists on the frontpage - hacky
db26acd4 2777 *
2778 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2779 */
2780class admin_setting_courselist_frontpage extends admin_setting {
9baf6825 2781/** @var array Array of choices value=>label */
73fa96d5 2782 public $choices;
6e4dc10f 2783
db26acd4 2784 /**
2785 * Construct override, requires one param
2786 *
db26acd4 2787 * @param bool $loggedin Is the user logged in
2788 */
73fa96d5 2789 public function __construct($loggedin) {
6e4dc10f 2790 global $CFG;
220a90c5 2791 require_once($CFG->dirroot.'/course/lib.php');
2792 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
2793 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
2794 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
2795 $defaults = array(FRONTPAGECOURSELIST);
73fa96d5 2796 parent::__construct($name, $visiblename, $description, $defaults);
6e4dc10f 2797 }
eef868d1 2798
db26acd4 2799 /**
2800 * Loads the choices available
2801 *
db26acd4 2802 * @return bool always returns true
2803 */
73fa96d5 2804 public function load_choices() {
c7da4357 2805 global $DB;
220a90c5 2806 if (is_array($this->choices)) {
2807 return true;
2808 }
2809 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
9baf6825 2810 FRONTPAGECOURSELIST => get_string('frontpagecourselist'),
2811 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
2812 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
2813 'none' => get_string('none'));
c7da4357 2814 if ($this->name == 'frontpage' and $DB->count_records('course') > FRONTPAGECOURSELIMIT) {
220a90c5 2815 unset($this->choices[FRONTPAGECOURSELIST]);
2816 }
2817 return true;
2818 }
db26acd4 2819 /**
2820 * Returns the selected settings
2821 *
2822 * @param mixed array or setting or null
2823 */
73fa96d5 2824 public function get_setting() {
220a90c5 2825 $result = $this->config_read($this->name);
2826 if (is_null($result)) {
2827 return NULL;
2828 }
2829 if ($result === '') {
2830 return array();
2831 }
2832 return explode(',', $result);
6e4dc10f 2833 }
eef868d1 2834
db26acd4 2835 /**
2836 * Save the selected options
2837 *
2838 * @param array $data
2839 * @return mixed empty string (data is not an array) or bool true=success false=failure
2840 */
73fa96d5 2841 public function write_setting($data) {
220a90c5 2842 if (!is_array($data)) {
2843 return '';
6e4dc10f 2844 }
220a90c5 2845 $this->load_choices();
2846 $save = array();
6e4dc10f 2847 foreach($data as $datum) {
220a90c5 2848 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
2849 continue;
6e4dc10f 2850 }
220a90c5 2851 $save[$datum] = $datum; // no duplicates
6e4dc10f 2852 }
220a90c5 2853 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2854 }
eef868d1 2855
db26acd4 2856 /**
2857 * Return XHTML select field and wrapping div
2858 *
2859 * @todo Add vartype handling to make sure $data is an array
2860 * @param array $data Array of elements to select by default
2861 * @return string XHTML select field and wrapping div
2862 */
73fa96d5 2863 public function output_html($data, $query='') {
220a90c5 2864 $this->load_choices();
2865 $currentsetting = array();
2866 foreach ($data as $key) {
2867 if ($key != 'none' and array_key_exists($key, $this->choices)) {
2868 $currentsetting[] = $key; // already selected first
6e4dc10f 2869 }
2870 }
220a90c5 2871
0a7e84c3 2872 $return = '<div class="form-group">';
6e4dc10f 2873 for ($i = 0; $i < count($this->choices) - 1; $i++) {
220a90c5 2874 if (!array_key_exists($i, $currentsetting)) {
2875 $currentsetting[$i] = 'none'; //none
2876 }
2877 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
6e4dc10f 2878 foreach ($this->choices as $key => $value) {
220a90c5 2879 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
6e4dc10f 2880 }
2881 $return .= '</select>';
2882 if ($i !== count($this->choices) - 2) {
975211bb 2883 $return .= '<br />';
6e4dc10f 2884 }
2885 }
0a7e84c3 2886 $return .= '</div>';
2887
587c7040 2888 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 2889 }
2890}
2891
220a90c5 2892/**
2893 * Special checkbox for frontpage - stores data in course table
db26acd4 2894 *
2895 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2896 */
6e4dc10f 2897class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
9baf6825 2898/**
2899 * Returns the current sites name
2900 *
2901 * @return string
2902 */
73fa96d5 2903 public function get_setting() {
b2bf016e 2904 $site = get_site();
2905 return $site->{$this->name};
6e4dc10f 2906 }
eef868d1 2907
db26acd4 2908 /**
2909 * Save the selected setting
2910 *
db26acd4 2911 * @param string $data The selected site
2912 * @return string empty string or error message
2913 */
73fa96d5 2914 public function write_setting($data) {
b2bf016e 2915 global $DB, $SITE;
220a90c5 2916 $record = new object();
2917 $record->id = SITEID;
2918 $record->{$this->name} = ($data == '1' ? 1 : 0);
2919 $record->timemodified = time();
b2bf016e 2920 // update $SITE
2921 $SITE->{$this->name} = $data;
f33e1ed4 2922 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2923 }
6e4dc10f 2924}
2925
220a90c5 2926/**
2927 * Special text for frontpage - stores data in course table.
2928 * Empty string means not set here. Manual setting is required.
db26acd4 2929 *
2930 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2931 */
6e4dc10f 2932class admin_setting_sitesettext extends admin_setting_configtext {
9baf6825 2933/**
2934 * Return the current setting
2935 *
2936 * @return mixed string or null
2937 */
73fa96d5 2938 public function get_setting() {
b2bf016e 2939 $site = get_site();
a7747679 2940 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
6e4dc10f 2941 }
90cfbd0a 2942
db26acd4 2943 /**
2944 * Validate the selected data
2945 *
2946 * @param string $data The selected value to validate
2947 * @return mixed true or message string
2948 */
73fa96d5 2949 public function validate($data) {
294ce987 2950 $cleaned = clean_param($data, PARAM_MULTILANG);
e33fbf87 2951 if ($cleaned === '') {
2952 return get_string('required');
2953 }
2954 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
2955 return true;
2956 } else {
2957 return get_string('validateerror', 'admin');
b89639f9 2958 }
b89639f9 2959 }
2960
db26acd4 2961 /**
2962 * Save the selected setting
2963 *
db26acd4 2964 * @param string $data The selected value
2965 * @return string emtpy or error message
2966 */
73fa96d5 2967 public function write_setting($data) {
b2bf016e 2968 global $DB, $SITE;
b89639f9 2969 $data = trim($data);
c5d2d0dd 2970 $validated = $this->validate($data);
e33fbf87 2971 if ($validated !== true) {
2972 return $validated;
90cfbd0a 2973 }
eef868d1 2974
220a90c5 2975 $record = new object();
2976 $record->id = SITEID;
f33e1ed4 2977 $record->{$this->name} = $data;
220a90c5 2978 $record->timemodified = time();
b2bf016e 2979 // update $SITE
2980 $SITE->{$this->name} = $data;
f33e1ed4 2981 return ($DB->update_record('course', $record) ? '' : get_string('dbupdatefailed', 'error'));
6e4dc10f 2982 }
6e4dc10f 2983}
2984
220a90c5 2985/**
2986 * Special text editor for site description.
db26acd4 2987 *
2988 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
220a90c5 2989 */
6e4dc10f 2990class admin_setting_special_frontpagedesc extends admin_setting {
9baf6825 2991/**
2992 * Calls parent::__construct with specific arguments
2993 */
73fa96d5 2994 public function __construct() {
2995 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
ff5fe311 2996 editors_head_setup();
6e4dc10f 2997 }
eef868d1 2998
db26acd4 2999 /**
3000 * Return the current setting
3001 * @return string The current setting
3002 */
73fa96d5 3003 public function get_setting() {
b2bf016e 3004 $site = get_site();
3005 return $site->{$this->name};
c626c2f4 3006 }
eef868d1 3007
db26acd4 3008 /**
3009 * Save the new setting
3010 *
db26acd4 3011 * @param string $data The new value to save
3012 * @return string empty or error message
3013 */
73fa96d5 3014 public function write_setting($data) {
b2bf016e 3015 global $DB, $SITE;
c626c2f4 3016 $record = new object();
220a90c5 3017 $record->id = SITEID;
f33e1ed4 3018 $record->{$this->name} = $data;
c626c2f4 3019 $record->timemodified = time();
b2bf016e 3020 $SITE->{$this->name} = $data;
7719860b 3021 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 3022 }
3023
db26acd4 3024 /**
3025 * Returns XHTML for the field plus wrapping div
3026 *
db26acd4 3027 * @param string $data The current value
3028 * @param string $query
3029 * @return string The XHTML output
3030 */
73fa96d5 3031 public function output_html($data, $query='') {
cb6c02c4 3032 global $CFG;
6e4dc10f 3033
220a90c5 3034 $CFG->adminusehtmleditor = can_use_html_editor();
8c37106d 3035 $return = '<div class="form-htmlarea">'.print_textarea($CFG->adminusehtmleditor, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
220a90c5 3036
587c7040 3037 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
220a90c5 3038 }
3039}
6e4dc10f 3040
db26acd4 3041/**
3042 * Special font selector for use in admin section
3043 *
3044 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 3045 */
6e4dc10f 3046class admin_setting_special_editorfontlist extends admin_setting {
3047
9baf6825 3048/**
3049 * Construct method, calls parent::__construct with specific args
3050 */
73fa96d5 3051 public function __construct() {
6e4dc10f 3052 global $CFG;
3053 $name = 'editorfontlist';
3054 $visiblename = get_string('editorfontlist', 'admin');
3055 $description = get_string('configeditorfontlist', 'admin');
6e4dc10f 3056 $defaults = array('k0' => 'Trebuchet',
9baf6825 3057 'v0' => 'Trebuchet MS,Verdana,Arial,Helvetica,sans-serif',
3058 'k1' => 'Arial',
3059 'v1' => 'arial,helvetica,sans-serif',
3060 'k2' => 'Courier New',
3061 'v2' => 'courier new,courier,monospace',
3062 'k3' => 'Georgia',
3063 'v3' => 'georgia,times new roman,times,serif',
3064 'k4' => 'Tahoma',
3065 'v4' => 'tahoma,arial,helvetica,sans-serif',
3066 'k5' => 'Times New Roman',
3067 'v5' => 'times new roman,times,serif',
3068 'k6' => 'Verdana',
3069 'v6' => 'verdana,arial,helvetica,sans-serif',
3070 'k7' => 'Impact',
3071 'v7' => 'impact',
3072 'k8' => 'Wingdings',
3073 'v8' => 'wingdings');
73fa96d5 3074 parent::__construct($name, $visiblename, $description, $defaults);
6e4dc10f 3075 }
eef868d1 3076
db26acd4 3077 /**
3078 * Return the current setting
0c079f19 3079 *
db26acd4 3080 * @return array Array of the current setting(s)
3081 */
73fa96d5 3082 public function get_setting() {
cc73de71 3083 global $CFG;
220a90c5 3084 $result = $this->config_read($this->name);
3085 if (is_null($result)) {
cc73de71 3086 return NULL;
3087 }
220a90c5 3088 $i = 0;
3089 $currentsetting = array();
3090 $items = explode(';', $result);
3091 foreach ($items as $item) {
9baf6825 3092 $item = explode(':', $item);
3093 $currentsetting['k'.$i] = $item[0];
3094 $currentsetting['v'.$i] = $item[1];
3095 $i++;
220a90c5 3096 }
3097 return $currentsetting;
6e4dc10f 3098 }
eef868d1 3099
db26acd4 3100 /**
3101 * Save the new setting(s)
3102 *
3103 * @todo Add vartype handling to ensure $data is an array
3104 * @param array $data Array containing the new settings
3105 * @return bool
3106 */
73fa96d5 3107 public function write_setting($data) {
eef868d1 3108
9baf6825 3109 // there miiight be an easier way to do this :)
3110 // if this is changed, make sure the $defaults array above is modified so that this
3111 // function processes it correctly
eef868d1 3112
6e4dc10f 3113 $keys = array();
3114 $values = array();
eef868d1 3115
6e4dc10f 3116 foreach ($data as $key => $value) {
3117 if (substr($key,0,1) == 'k') {
3118 $keys[substr($key,1)] = $value;
3119 } elseif (substr($key,0,1) == 'v') {
3120 $values[substr($key,1)] = $value;
3121 }
3122 }
eef868d1 3123
220a90c5 3124 $result = array();
6e4dc10f 3125 for ($i = 0; $i < count($keys); $i++) {
3126 if (($keys[$i] !== '') && ($values[$i] !== '')) {
220a90c5 3127 $result[] = clean_param($keys[$i],PARAM_NOTAGS).':'.clean_param($values[$i], PARAM_NOTAGS);
6e4dc10f 3128 }
3129 }
eef868d1 3130
220a90c5 3131 return ($this->config_write($this->name, implode(';', $result)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 3132 }
eef868d1 3133
db26acd4 3134 /**
3135 * Returns XHTML for the options
3136 *
3137 * @todo Add vartype handling to ensure that $data is an array
3138 * @param array $data An array of values to set
3139 * @param string $query
3140 * @return string XHTML
3141 */
73fa96d5 3142 public function output_html($data, $query='') {
220a90c5 3143 $fullname = $this->get_full_name();
1beed35f 3144 $return = '<div class="form-group">';
220a90c5 3145 for ($i = 0; $i < count($data) / 2; $i++) {
3146 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="'.$data['k'.$i].'" />';
6e4dc10f 3147 $return .= '&nbsp;&nbsp;';
220a90c5 3148 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="'.$data['v'.$i].'" /><br />';
6e4dc10f 3149 }
220a90c5 3150 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="" />';
6e4dc10f 3151 $return .= '&nbsp;&nbsp;';
220a90c5 3152 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="" /><br />';
3153 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.($i + 1).']" value="" />';
6e4dc10f 3154 $return .= '&nbsp;&nbsp;';
220a90c5 3155 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.($i + 1).']" value="" />';
1beed35f 3156 $return .= '</div>';
6153cf58 3157
587c7040 3158 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 3159 }
eef868d1 3160
6e4dc10f 3161}
db26acd4 3162/**
3163 * Special settings for emoticons
3164 *
3165 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db26acd4 3166 */
93c61c18 3167class admin_setting_emoticons extends admin_setting {
3168
9baf6825 3169/**
3170 * Calls parent::__construct with specific args
3171 */
73fa96d5 3172 public function __construct() {
93c61c18 3173 global $CFG;
3174 $name = 'emoticons';
3175 $visiblename = get_string('emoticons', 'admin');
3176 $description = get_string('configemoticons', 'admin');
3177 $defaults = array('k0' => ':-)',
9baf6825 3178 'v0' => 'smiley',
3179 'k1' => ':)',
3180 'v1' => 'smiley',
3181 'k2' => ':-D',
3182 'v2' => 'biggrin',
3183 'k3' => ';-)',
3184 'v3' => 'wink',
3185 'k4' => ':-/',
3186 'v4' => 'mixed',
3187 'k5' => 'V-.',
3188 'v5' => 'thoughtful',
3189 'k6' => ':-P',
3190 'v6' => 'tongueout',
3191 'k7' => 'B-)',
3192 'v7' => 'cool',
3193 'k8' => '^-)',
3194 'v8' => 'approve',
3195 'k9' => '8-)',
3196 'v9' => 'wideeyes',
3197 'k10' => ':o)',
3198 'v10' => 'clown',
3199 'k11' => ':-(',
3200 'v11' => 'sad',
3201 'k12' => ':(',
3202 'v12' => 'sad',
3203 'k13' => '8-.',
3204 'v13' => 'shy',
3205 'k14' => ':-I',
3206 'v14' => 'blush',
3207 'k15' => ':-X',
3208 'v15' => 'kiss',
3209 'k16' => '8-o',
3210 'v16' => 'surprise',
3211 'k17' => 'P-|',
3212 'v17' => 'blackeye',
3213 'k18' => '8-[',
3214 'v18' => 'angry',
3215 'k19' => 'xx-P',
3216 'v19' => 'dead',
3217 'k20' => '|-.',
3218 'v20' => 'sleepy',
3219 'k21' => '}-]',
3220 'v21' => 'evil',
3221 'k22' => '(h)',
3222 'v22' => 'heart',
3223 'k23' => '(heart)',
3224 'v23' => 'heart',
3225 'k24' => '(y)',
3226 'v24' => 'yes',
3227 'k25' => '(n)',
3228 'v25' => 'no',
3229 'k26' => '(martin)',
3230 'v26' => 'martin',
3231 'k27' => '( )',
3232 'v27' => 'egg');
73fa96d5 3233 parent::__construct($name, $visiblename, $description, $defaults);
93c61c18 3234 }
0c079f19 3235
db26acd4 3236 /**
3237 * Return the current setting(s)
3238 *
db26acd4 3239 * @return array Current settings array
3240 */
73fa96d5 3241 public function get_setting() {
93c61c18 3242 global $CFG;
220a90c5 3243 $result = $this->config_read($this->name);
3244 if (is_null($result)) {
93c61c18 3245 return NULL;
3246 }
220a90c5 3247 $i = 0;
3248 $currentsetting = array();
3249 $items = explode('{;}', $result);
3250 foreach ($items as $item) {
9baf6825 3251 $item = explode('{:}', $item);
3252 $currentsetting['k'.$i] = $item[0];
3253 $currentsetting['v'.$i] = $item[1];
3254 $i++;
220a90c5 3255 }
3256 return $currentsetting;
93c61c18 3257 }
3258
db26acd4 3259 /**
3260 * Save selected settings
3261 *
3262 * @param array $data Array of settings to save
3263 * @return bool
3264 */
73fa96d5 3265 public function write_setting($data) {
93c61c18 3266
9baf6825 3267 // there miiight be an easier way to do this :)
3268 // if this is changed, make sure the $defaults array above is modified so that this
3269 // function processes it correctly
93c61c18 3270
3271 $keys = array();
3272 $values = array();
3273
3274 foreach ($data as $key => $value) {
3275 if (substr($key,0,1) == 'k') {
3276 $keys[substr($key,1)] = $value;
3277 } elseif (substr($key,0,1) == 'v') {
3278 $values[substr($key,1)] = $value;
3279 }
3280 }
3281
220a90c5 3282 $result = array();
93c61c18 3283 for ($i = 0; $i < count($keys); $i++) {
3284 if (($keys[$i] !== '') && ($values[$i] !== '')) {
220a90c5 3285 $result[] = clean_param($keys[$i],PARAM_NOTAGS).'{:}'.clean_param($values[$i], PARAM_NOTAGS);
93c61c18 3286 }
3287 }
3288
220a90c5 3289 return ($this->config_write($this->name, implode('{;}', $result)) ? '' : get_string('errorsetting', 'admin').$this->visiblename.'<br />');
93c61c18 3290 }
3291
db26acd4 3292 /**
3293 * Return XHTML field(s) for options
3294 *
3295 * @param array $data Array of options to set in HTML
3296 * @return string XHTML string for the fields and wrapping div(s)
3297 */
73fa96d5 3298 public function output_html($data, $query='') {
220a90c5 3299 $fullname = $this->get_full_name();
93c61c18 3300 $return = '<div class="form-group">';
220a90c5 3301 for ($i = 0; $i < count($data) / 2; $i++) {
3302 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="'.$data['k'.$i].'" />';
93c61c18 3303 $return .= '&nbsp;&nbsp;';
220a90c5 3304 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="'.$data['v'.$i].'" /><br />';
93c61c18 3305 }
220a90c5 3306 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="" />';
93c61c18 3307 $return .= '&nbsp;&nbsp;';
220a90c5 3308 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="