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