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