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