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