moodle_page: MDL-12212 reimplement user_is_editing, deprecate isediting
[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 */
fb073f60 2164 function __construct($name, $visiblename, $description, $defaultsetting, $capability) {
2165 $this->capability = $capability;
2166 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
2167 }
2168
2169 function load_choices() {
2170 if (is_array($this->choices)) {
2171 return true;
2172 }
4413941f 2173 $users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM),
fb073f60 2174 $this->capability, 'u.id,u.username,u.firstname,u.lastname', 'u.lastname,u.firstname');
2175 $this->choices = array(
4413941f 2176 '$@NONE@$' => get_string('nobody'),
fb073f60 2177 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4413941f 2178 );
2179 foreach ($users as $user) {
fb073f60 2180 $this->choices[$user->username] = fullname($user);
4413941f 2181 }
fb073f60 2182 return true;
4413941f 2183 }
2184
73fa96d5 2185 public function get_defaultsetting() {
4413941f 2186 $this->load_choices();
cd3acbf2 2187 $defaultsetting = parent::get_defaultsetting();
2188 if (empty($defaultsetting)) {
4413941f 2189 return array('$@NONE@$');
cd3acbf2 2190 } else if (array_key_exists($defaultsetting, $this->choices)) {
2191 return $defaultsetting;
4413941f 2192 } else {
2193 return '';
2194 }
2195 }
2196
73fa96d5 2197 public function get_setting() {
4413941f 2198 $result = parent::get_setting();
2199 if (empty($result)) {
2200 $result = array('$@NONE@$');
2201 }
2202 return $result;
2203 }
2204
73fa96d5 2205 public function write_setting($data) {
4413941f 2206 // If all is selected, remove any explicit options.
2207 if (in_array('$@ALL@$', $data)) {
2208 $data = array('$@ALL@$');
2209 }
2210 // None never needs to be writted to the DB.
2211 if (in_array('$@NONE@$', $data)) {
2212 unset($data[array_search('$@NONE@$', $data)]);
2213 }
2214 return parent::write_setting($data);
2215 }
2216}
2217
220a90c5 2218/**
2219 * Special checkbox for calendar - resets SESSION vars.
2220 */
6e4dc10f 2221class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
73fa96d5 2222 public function __construct() {
2223 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
2224 get_string('helpadminseesall', 'admin'), '0');
6e4dc10f 2225 }
2226
73fa96d5 2227 public function write_setting($data) {
6e4dc10f 2228 global $SESSION;
2229 unset($SESSION->cal_courses_shown);
220a90c5 2230 return parent::write_setting($data);
6e4dc10f 2231 }
2232}
2233
392e7363 2234/**
2235 * Special select for settings that are altered in setup.php and can not be altered on the fly
2236 */
2237class admin_setting_special_selectsetup extends admin_setting_configselect {
73fa96d5 2238 public function get_setting() {
392e7363 2239 // read directly from db!
2240 return get_config(NULL, $this->name);
2241 }
2242
73fa96d5 2243 public function write_setting($data) {
392e7363 2244 global $CFG;
2245 // do not change active CFG setting!
2246 $current = $CFG->{$this->name};
2247 $result = parent::write_setting($data);
2248 $CFG->{$this->name} = $current;
2249 return $result;
2250 }
2251}
2252
220a90c5 2253/**
2254 * Special select for frontpage - stores data in course table
2255 */
6e4dc10f 2256class admin_setting_sitesetselect extends admin_setting_configselect {
73fa96d5 2257 public function get_setting() {
b2bf016e 2258 $site = get_site();
2259 return $site->{$this->name};
6e4dc10f 2260 }
eef868d1 2261
73fa96d5 2262 public function write_setting($data) {
b2bf016e 2263 global $DB, $SITE;
6e4dc10f 2264 if (!in_array($data, array_keys($this->choices))) {
220a90c5 2265 return get_string('errorsetting', 'admin');
6e4dc10f 2266 }
2267 $record = new stdClass();
220a90c5 2268 $record->id = SITEID;
2269 $temp = $this->name;
2270 $record->$temp = $data;
6e4dc10f 2271 $record->timemodified = time();
b2bf016e 2272 // update $SITE
2273 $SITE->{$this->name} = $data;
f33e1ed4 2274 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2275 }
6e4dc10f 2276}
2277
220a90c5 2278/**
2279 * Special select - lists on the frontpage - hacky
2280 */
2281class admin_setting_courselist_frontpage extends admin_setting {
73fa96d5 2282 public $choices;
6e4dc10f 2283
73fa96d5 2284 public function __construct($loggedin) {
6e4dc10f 2285 global $CFG;
220a90c5 2286 require_once($CFG->dirroot.'/course/lib.php');
2287 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
2288 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
2289 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
2290 $defaults = array(FRONTPAGECOURSELIST);
73fa96d5 2291 parent::__construct($name, $visiblename, $description, $defaults);
6e4dc10f 2292 }
eef868d1 2293
73fa96d5 2294 public function load_choices() {
c7da4357 2295 global $DB;
220a90c5 2296 if (is_array($this->choices)) {
2297 return true;
2298 }
2299 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
2300 FRONTPAGECOURSELIST => get_string('frontpagecourselist'),
2301 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
2302 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
2303 'none' => get_string('none'));
c7da4357 2304 if ($this->name == 'frontpage' and $DB->count_records('course') > FRONTPAGECOURSELIMIT) {
220a90c5 2305 unset($this->choices[FRONTPAGECOURSELIST]);
2306 }
2307 return true;
2308 }
73fa96d5 2309 public function get_setting() {
220a90c5 2310 $result = $this->config_read($this->name);
2311 if (is_null($result)) {
2312 return NULL;
2313 }
2314 if ($result === '') {
2315 return array();
2316 }
2317 return explode(',', $result);
6e4dc10f 2318 }
eef868d1 2319
73fa96d5 2320 public function write_setting($data) {
220a90c5 2321 if (!is_array($data)) {
2322 return '';
6e4dc10f 2323 }
220a90c5 2324 $this->load_choices();
2325 $save = array();
6e4dc10f 2326 foreach($data as $datum) {
220a90c5 2327 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
2328 continue;
6e4dc10f 2329 }
220a90c5 2330 $save[$datum] = $datum; // no duplicates
6e4dc10f 2331 }
220a90c5 2332 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2333 }
eef868d1 2334
73fa96d5 2335 public function output_html($data, $query='') {
220a90c5 2336 $this->load_choices();
2337 $currentsetting = array();
2338 foreach ($data as $key) {
2339 if ($key != 'none' and array_key_exists($key, $this->choices)) {
2340 $currentsetting[] = $key; // already selected first
6e4dc10f 2341 }
2342 }
220a90c5 2343
0a7e84c3 2344 $return = '<div class="form-group">';
6e4dc10f 2345 for ($i = 0; $i < count($this->choices) - 1; $i++) {
220a90c5 2346 if (!array_key_exists($i, $currentsetting)) {
2347 $currentsetting[$i] = 'none'; //none
2348 }
2349 $return .='<select class="form-select" id="'.$this->get_id().$i.'" name="'.$this->get_full_name().'[]">';
6e4dc10f 2350 foreach ($this->choices as $key => $value) {
220a90c5 2351 $return .= '<option value="'.$key.'"'.("$key" == $currentsetting[$i] ? ' selected="selected"' : '').'>'.$value.'</option>';
6e4dc10f 2352 }
2353 $return .= '</select>';
2354 if ($i !== count($this->choices) - 2) {
975211bb 2355 $return .= '<br />';
6e4dc10f 2356 }
2357 }
0a7e84c3 2358 $return .= '</div>';
2359
587c7040 2360 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 2361 }
2362}
2363
220a90c5 2364/**
2365 * Special checkbox for frontpage - stores data in course table
2366 */
6e4dc10f 2367class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
73fa96d5 2368 public function get_setting() {
b2bf016e 2369 $site = get_site();
2370 return $site->{$this->name};
6e4dc10f 2371 }
eef868d1 2372
73fa96d5 2373 public function write_setting($data) {
b2bf016e 2374 global $DB, $SITE;
220a90c5 2375 $record = new object();
2376 $record->id = SITEID;
2377 $record->{$this->name} = ($data == '1' ? 1 : 0);
2378 $record->timemodified = time();
b2bf016e 2379 // update $SITE
2380 $SITE->{$this->name} = $data;
f33e1ed4 2381 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2382 }
6e4dc10f 2383}
2384
220a90c5 2385/**
2386 * Special text for frontpage - stores data in course table.
2387 * Empty string means not set here. Manual setting is required.
2388 */
6e4dc10f 2389class admin_setting_sitesettext extends admin_setting_configtext {
73fa96d5 2390 public function get_setting() {
b2bf016e 2391 $site = get_site();
a7747679 2392 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
6e4dc10f 2393 }
90cfbd0a 2394
73fa96d5 2395 public function validate($data) {
294ce987 2396 $cleaned = clean_param($data, PARAM_MULTILANG);
e33fbf87 2397 if ($cleaned === '') {
2398 return get_string('required');
2399 }
2400 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
2401 return true;
2402 } else {
2403 return get_string('validateerror', 'admin');
b89639f9 2404 }
b89639f9 2405 }
2406
73fa96d5 2407 public function write_setting($data) {
b2bf016e 2408 global $DB, $SITE;
b89639f9 2409 $data = trim($data);
c5d2d0dd 2410 $validated = $this->validate($data);
e33fbf87 2411 if ($validated !== true) {
2412 return $validated;
90cfbd0a 2413 }
eef868d1 2414
220a90c5 2415 $record = new object();
2416 $record->id = SITEID;
f33e1ed4 2417 $record->{$this->name} = $data;
220a90c5 2418 $record->timemodified = time();
b2bf016e 2419 // update $SITE
2420 $SITE->{$this->name} = $data;
f33e1ed4 2421 return ($DB->update_record('course', $record) ? '' : get_string('dbupdatefailed', 'error'));
6e4dc10f 2422 }
6e4dc10f 2423}
2424
220a90c5 2425/**
2426 * Special text editor for site description.
2427 */
6e4dc10f 2428class admin_setting_special_frontpagedesc extends admin_setting {
73fa96d5 2429 public function __construct() {
2430 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), NULL);
6e4dc10f 2431 }
eef868d1 2432
73fa96d5 2433 public function get_setting() {
b2bf016e 2434 $site = get_site();
2435 return $site->{$this->name};
c626c2f4 2436 }
eef868d1 2437
73fa96d5 2438 public function write_setting($data) {
b2bf016e 2439 global $DB, $SITE;
c626c2f4 2440 $record = new object();
220a90c5 2441 $record->id = SITEID;
f33e1ed4 2442 $record->{$this->name} = $data;
c626c2f4 2443 $record->timemodified = time();
b2bf016e 2444 $SITE->{$this->name} = $data;
7719860b 2445 return ($DB->update_record('course', $record) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2446 }
2447
73fa96d5 2448 public function output_html($data, $query='') {
cb6c02c4 2449 global $CFG;
6e4dc10f 2450
220a90c5 2451 $CFG->adminusehtmleditor = can_use_html_editor();
8c37106d 2452 $return = '<div class="form-htmlarea">'.print_textarea($CFG->adminusehtmleditor, 15, 60, 0, 0, $this->get_full_name(), $data, 0, true, 'summary') .'</div>';
220a90c5 2453
587c7040 2454 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
220a90c5 2455 }
2456}
6e4dc10f 2457
2458class admin_setting_special_editorfontlist extends admin_setting {
2459
73fa96d5 2460 public $items;
6e4dc10f 2461
73fa96d5 2462 public function __construct() {
6e4dc10f 2463 global $CFG;
2464 $name = 'editorfontlist';
2465 $visiblename = get_string('editorfontlist', 'admin');
2466 $description = get_string('configeditorfontlist', 'admin');
6e4dc10f 2467 $defaults = array('k0' => 'Trebuchet',
2468 'v0' => 'Trebuchet MS,Verdana,Arial,Helvetica,sans-serif',
2469 'k1' => 'Arial',
2470 'v1' => 'arial,helvetica,sans-serif',
2471 'k2' => 'Courier New',
2472 'v2' => 'courier new,courier,monospace',
2473 'k3' => 'Georgia',
2474 'v3' => 'georgia,times new roman,times,serif',
2475 'k4' => 'Tahoma',
2476 'v4' => 'tahoma,arial,helvetica,sans-serif',
2477 'k5' => 'Times New Roman',
2478 'v5' => 'times new roman,times,serif',
2479 'k6' => 'Verdana',
2480 'v6' => 'verdana,arial,helvetica,sans-serif',
2481 'k7' => 'Impact',
2482 'v7' => 'impact',
2483 'k8' => 'Wingdings',
2484 'v8' => 'wingdings');
73fa96d5 2485 parent::__construct($name, $visiblename, $description, $defaults);
6e4dc10f 2486 }
eef868d1 2487
73fa96d5 2488 public function get_setting() {
cc73de71 2489 global $CFG;
220a90c5 2490 $result = $this->config_read($this->name);
2491 if (is_null($result)) {
cc73de71 2492 return NULL;
2493 }
220a90c5 2494 $i = 0;
2495 $currentsetting = array();
2496 $items = explode(';', $result);
2497 foreach ($items as $item) {
2498 $item = explode(':', $item);
2499 $currentsetting['k'.$i] = $item[0];
2500 $currentsetting['v'.$i] = $item[1];
2501 $i++;
2502 }
2503 return $currentsetting;
6e4dc10f 2504 }
eef868d1 2505
73fa96d5 2506 public function write_setting($data) {
eef868d1 2507
6e4dc10f 2508 // there miiight be an easier way to do this :)
2509 // if this is changed, make sure the $defaults array above is modified so that this
2510 // function processes it correctly
eef868d1 2511
6e4dc10f 2512 $keys = array();
2513 $values = array();
eef868d1 2514
6e4dc10f 2515 foreach ($data as $key => $value) {
2516 if (substr($key,0,1) == 'k') {
2517 $keys[substr($key,1)] = $value;
2518 } elseif (substr($key,0,1) == 'v') {
2519 $values[substr($key,1)] = $value;
2520 }
2521 }
eef868d1 2522
220a90c5 2523 $result = array();
6e4dc10f 2524 for ($i = 0; $i < count($keys); $i++) {
2525 if (($keys[$i] !== '') && ($values[$i] !== '')) {
220a90c5 2526 $result[] = clean_param($keys[$i],PARAM_NOTAGS).':'.clean_param($values[$i], PARAM_NOTAGS);
6e4dc10f 2527 }
2528 }
eef868d1 2529
220a90c5 2530 return ($this->config_write($this->name, implode(';', $result)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2531 }
eef868d1 2532
73fa96d5 2533 public function output_html($data, $query='') {
220a90c5 2534 $fullname = $this->get_full_name();
1beed35f 2535 $return = '<div class="form-group">';
220a90c5 2536 for ($i = 0; $i < count($data) / 2; $i++) {
2537 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="'.$data['k'.$i].'" />';
6e4dc10f 2538 $return .= '&nbsp;&nbsp;';
220a90c5 2539 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="'.$data['v'.$i].'" /><br />';
6e4dc10f 2540 }
220a90c5 2541 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="" />';
6e4dc10f 2542 $return .= '&nbsp;&nbsp;';
220a90c5 2543 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="" /><br />';
2544 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.($i + 1).']" value="" />';
6e4dc10f 2545 $return .= '&nbsp;&nbsp;';
220a90c5 2546 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.($i + 1).']" value="" />';
1beed35f 2547 $return .= '</div>';
6153cf58 2548
587c7040 2549 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 2550 }
eef868d1 2551
6e4dc10f 2552}
2553
93c61c18 2554class admin_setting_emoticons extends admin_setting {
2555
73fa96d5 2556 public $items;
93c61c18 2557
73fa96d5 2558 public function __construct() {
93c61c18 2559 global $CFG;
2560 $name = 'emoticons';
2561 $visiblename = get_string('emoticons', 'admin');
2562 $description = get_string('configemoticons', 'admin');
2563 $defaults = array('k0' => ':-)',
2564 'v0' => 'smiley',
2565 'k1' => ':)',
2566 'v1' => 'smiley',
2567 'k2' => ':-D',
2568 'v2' => 'biggrin',
2569 'k3' => ';-)',
2570 'v3' => 'wink',
2571 'k4' => ':-/',
2572 'v4' => 'mixed',
2573 'k5' => 'V-.',
2574 'v5' => 'thoughtful',
2575 'k6' => ':-P',
2576 'v6' => 'tongueout',
2577 'k7' => 'B-)',
2578 'v7' => 'cool',
2579 'k8' => '^-)',
2580 'v8' => 'approve',
2581 'k9' => '8-)',
2582 'v9' => 'wideeyes',
2583 'k10' => ':o)',
2584 'v10' => 'clown',
2585 'k11' => ':-(',
2586 'v11' => 'sad',
2587 'k12' => ':(',
2588 'v12' => 'sad',
2589 'k13' => '8-.',
2590 'v13' => 'shy',
2591 'k14' => ':-I',
2592 'v14' => 'blush',
2593 'k15' => ':-X',
2594 'v15' => 'kiss',
2595 'k16' => '8-o',
2596 'v16' => 'surprise',
2597 'k17' => 'P-|',
2598 'v17' => 'blackeye',
2599 'k18' => '8-[',
2600 'v18' => 'angry',
2601 'k19' => 'xx-P',
2602 'v19' => 'dead',
2603 'k20' => '|-.',
2604 'v20' => 'sleepy',
2605 'k21' => '}-]',
2606 'v21' => 'evil',
2607 'k22' => '(h)',
2608 'v22' => 'heart',
2609 'k23' => '(heart)',
2610 'v23' => 'heart',
2611 'k24' => '(y)',
2612 'v24' => 'yes',
2613 'k25' => '(n)',
2614 'v25' => 'no',
2615 'k26' => '(martin)',
2616 'v26' => 'martin',
2617 'k27' => '( )',
2618 'v27' => 'egg');
73fa96d5 2619 parent::__construct($name, $visiblename, $description, $defaults);
93c61c18 2620 }
2621
73fa96d5 2622 public function get_setting() {
93c61c18 2623 global $CFG;
220a90c5 2624 $result = $this->config_read($this->name);
2625 if (is_null($result)) {
93c61c18 2626 return NULL;
2627 }
220a90c5 2628 $i = 0;
2629 $currentsetting = array();
2630 $items = explode('{;}', $result);
2631 foreach ($items as $item) {
2632 $item = explode('{:}', $item);
2633 $currentsetting['k'.$i] = $item[0];
2634 $currentsetting['v'.$i] = $item[1];
2635 $i++;
2636 }
2637 return $currentsetting;
93c61c18 2638 }
2639
73fa96d5 2640 public function write_setting($data) {
93c61c18 2641
2642 // there miiight be an easier way to do this :)
2643 // if this is changed, make sure the $defaults array above is modified so that this
2644 // function processes it correctly
2645
2646 $keys = array();
2647 $values = array();
2648
2649 foreach ($data as $key => $value) {
2650 if (substr($key,0,1) == 'k') {
2651 $keys[substr($key,1)] = $value;
2652 } elseif (substr($key,0,1) == 'v') {
2653 $values[substr($key,1)] = $value;
2654 }
2655 }
2656
220a90c5 2657 $result = array();
93c61c18 2658 for ($i = 0; $i < count($keys); $i++) {
2659 if (($keys[$i] !== '') && ($values[$i] !== '')) {
220a90c5 2660 $result[] = clean_param($keys[$i],PARAM_NOTAGS).'{:}'.clean_param($values[$i], PARAM_NOTAGS);
93c61c18 2661 }
2662 }
2663
220a90c5 2664 return ($this->config_write($this->name, implode('{;}', $result)) ? '' : get_string('errorsetting', 'admin').$this->visiblename.'<br />');
93c61c18 2665 }
2666
73fa96d5 2667 public function output_html($data, $query='') {
220a90c5 2668 $fullname = $this->get_full_name();
93c61c18 2669 $return = '<div class="form-group">';
220a90c5 2670 for ($i = 0; $i < count($data) / 2; $i++) {
2671 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="'.$data['k'.$i].'" />';
93c61c18 2672 $return .= '&nbsp;&nbsp;';
220a90c5 2673 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="'.$data['v'.$i].'" /><br />';
93c61c18 2674 }
220a90c5 2675 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.$i.']" value="" />';
93c61c18 2676 $return .= '&nbsp;&nbsp;';
220a90c5 2677 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.$i.']" value="" /><br />';
2678 $return .= '<input type="text" class="form-text" name="'.$fullname.'[k'.($i + 1).']" value="" />';
93c61c18 2679 $return .= '&nbsp;&nbsp;';
220a90c5 2680 $return .= '<input type="text" class="form-text" name="'.$fullname.'[v'.($i + 1).']" value="" />';
93c61c18 2681 $return .= '</div>';
2682
587c7040 2683 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
93c61c18 2684 }
2685
2686}
2687
6e4dc10f 2688class admin_setting_special_editorhidebuttons extends admin_setting {
73fa96d5 2689 public $items;
6e4dc10f 2690
73fa96d5 2691 public function __construct() {
2692 parent::__construct('editorhidebuttons', get_string('editorhidebuttons', 'admin'),
2693 get_string('confeditorhidebuttons', 'admin'), array());
6e4dc10f 2694 // weird array... buttonname => buttonimage (assume proper path appended). if you leave buttomimage blank, text will be printed instead
2695 $this->items = array('fontname' => '',
2696 'fontsize' => '',
2697 'formatblock' => '',
2698 'bold' => 'ed_format_bold.gif',
2699 'italic' => 'ed_format_italic.gif',
2700 'underline' => 'ed_format_underline.gif',
2701 'strikethrough' => 'ed_format_strike.gif',
2702 'subscript' => 'ed_format_sub.gif',
2703 'superscript' => 'ed_format_sup.gif',
2704 'copy' => 'ed_copy.gif',
2705 'cut' => 'ed_cut.gif',
2706 'paste' => 'ed_paste.gif',
2707 'clean' => 'ed_wordclean.gif',
2708 'undo' => 'ed_undo.gif',
2709 'redo' => 'ed_redo.gif',
2710 'justifyleft' => 'ed_align_left.gif',
2711 'justifycenter' => 'ed_align_center.gif',
2712 'justifyright' => 'ed_align_right.gif',
2713 'justifyfull' => 'ed_align_justify.gif',
2714 'lefttoright' => 'ed_left_to_right.gif',
2715 'righttoleft' => 'ed_right_to_left.gif',
2716 'insertorderedlist' => 'ed_list_num.gif',
2717 'insertunorderedlist' => 'ed_list_bullet.gif',
2718 'outdent' => 'ed_indent_less.gif',
2719 'indent' => 'ed_indent_more.gif',
2720 'forecolor' => 'ed_color_fg.gif',
2721 'hilitecolor' => 'ed_color_bg.gif',
2722 'inserthorizontalrule' => 'ed_hr.gif',
2723 'createanchor' => 'ed_anchor.gif',
2724 'createlink' => 'ed_link.gif',
2725 'unlink' => 'ed_unlink.gif',
2726 'insertimage' => 'ed_image.gif',
2727 'inserttable' => 'insert_table.gif',
2728 'insertsmile' => 'em.icon.smile.gif',
2729 'insertchar' => 'icon_ins_char.gif',
2730 'spellcheck' => 'spell-check.gif',
2731 'htmlmode' => 'ed_html.gif',
2732 'popupeditor' => 'fullscreen_maximize.gif',
2733 'search_replace' => 'ed_replace.gif');
2734 }
2735
73fa96d5 2736 public function get_setting() {
220a90c5 2737 $result = $this->config_read($this->name);
2738 if (is_null($result)) {
2739 return NULL;
2740 }
2741 if ($result === '') {
2742 return array();
2743 }
5ac776ac 2744 return explode(' ', $result);
6e4dc10f 2745 }
2746
73fa96d5 2747 public function write_setting($data) {
220a90c5 2748 if (!is_array($data)) {
2749 return ''; // ignore it
2750 }
2751 unset($data['xxxxx']);
6e4dc10f 2752 $result = array();
220a90c5 2753
6e4dc10f 2754 foreach ($data as $key => $value) {
5ac776ac 2755 if (!isset($this->items[$key])) {
220a90c5 2756 return get_string('errorsetting', 'admin');
6e4dc10f 2757 }
2758 if ($value == '1') {
2759 $result[] = $key;
2760 }
2761 }
220a90c5 2762 return ($this->config_write($this->name, implode(' ', $result)) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2763 }
2764
73fa96d5 2765 public function output_html($data, $query='') {
eef868d1 2766
6e4dc10f 2767 global $CFG;
eef868d1 2768
6e4dc10f 2769 // checkboxes with input name="$this->name[$key]" value="1"
2770 // we do 15 fields per column
eef868d1 2771
1beed35f 2772 $return = '<div class="form-group">';
2773 $return .= '<table><tr><td valign="top" align="right">';
220a90c5 2774 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
eef868d1 2775
6e4dc10f 2776 $count = 0;
eef868d1 2777
6e4dc10f 2778 foreach($this->items as $key => $value) {
8ddbd7a6 2779 if ($count % 15 == 0 and $count != 0) {
2780 $return .= '</td><td valign="top" align="right">';
6e4dc10f 2781 }
eef868d1 2782
5ac776ac 2783 $return .= '<label for="'.$this->get_id().$key.'">';
220a90c5 2784 $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;';
2785 $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 2786 $return .= '</label>';
6e4dc10f 2787 $count++;
2788 if ($count % 15 != 0) {
2789 $return .= '<br /><br />';
2790 }
2791 }
eef868d1 2792
2793 $return .= '</td></tr>';
6e4dc10f 2794 $return .= '</table>';
1beed35f 2795 $return .= '</div>';
6e4dc10f 2796
587c7040 2797 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
6e4dc10f 2798 }
6e4dc10f 2799}
2800
220a90c5 2801/**
2802 * Special setting for limiting of the list of available languages.
2803 */
4642650f 2804class admin_setting_langlist extends admin_setting_configtext {
73fa96d5 2805 public function __construct() {
2806 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4642650f 2807 }
2808
73fa96d5 2809 public function write_setting($data) {
4642650f 2810 $return = parent::write_setting($data);
2811 get_list_of_languages(true);//refresh the list
2812 return $return;
2813 }
2814}
2815
220a90c5 2816/**
2817 * Course category selection
2818 */
2819class admin_settings_coursecat_select extends admin_setting_configselect {
73fa96d5 2820 public function __construct($name, $visiblename, $description, $defaultsetting) {
2821 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
220a90c5 2822 }
6153cf58 2823
73fa96d5 2824 public function load_choices() {
220a90c5 2825 global $CFG;
2826 require_once($CFG->dirroot.'/course/lib.php');
2827 if (is_array($this->choices)) {
2828 return true;
2829 }
2830 $this->choices = make_categories_options();
2831 return true;
2832 }
2833}
eef868d1 2834
220a90c5 2835class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
73fa96d5 2836 public function __construct() {
2837 parent::__construct('backup_sche_weekdays', get_string('schedule'), get_string('backupschedulehelp'), array(), NULL);
220a90c5 2838 $this->plugin = 'backup';
6e4dc10f 2839 }
eef868d1 2840
73fa96d5 2841 public function load_choices() {
220a90c5 2842 if (is_array($this->choices)) {
2843 return true;
2844 }
2845 $this->choices = array();
2846 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
2847 foreach ($days as $day) {
2848 $this->choices[$day] = get_string($day, 'calendar');
6e4dc10f 2849 }
220a90c5 2850 return true;
6e4dc10f 2851 }
2852}
2853
220a90c5 2854/**
2855 * Special debug setting
2856 */
ee437bbc 2857class admin_setting_special_debug extends admin_setting_configselect {
73fa96d5 2858 public function __construct() {
2859 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
6e4dc10f 2860 }
2861
73fa96d5 2862 public function load_choices() {
220a90c5 2863 if (is_array($this->choices)) {
2864 return true;
ee437bbc 2865 }
220a90c5 2866 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
2867 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
2868 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
2869 DEBUG_ALL => get_string('debugall', 'admin'),
2870 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
2871 return true;
6e4dc10f 2872 }
6e4dc10f 2873}
2874
2875
2876class admin_setting_special_calendar_weekend extends admin_setting {
73fa96d5 2877 public function __construct() {
6e4dc10f 2878 $name = 'calendar_weekend';
2879 $visiblename = get_string('calendar_weekend', 'admin');
2880 $description = get_string('helpweekenddays', 'admin');
5eaa6aa0 2881 $default = array ('0', '6'); // Saturdays and Sundays
73fa96d5 2882 parent::__construct($name, $visiblename, $description, $default);
6e4dc10f 2883 }
2884
73fa96d5 2885 public function get_setting() {
220a90c5 2886 $result = $this->config_read($this->name);
2887 if (is_null($result)) {
2888 return NULL;
2889 }
2890 if ($result === '') {
2891 return array();
2892 }
2893 $settings = array();
2894 for ($i=0; $i<7; $i++) {
2895 if ($result & (1 << $i)) {
552c374e 2896 $settings[] = $i;
220a90c5 2897 }
2898 }
552c374e 2899 return $settings;
6e4dc10f 2900 }
eef868d1 2901
73fa96d5 2902 public function write_setting($data) {
220a90c5 2903 if (!is_array($data)) {
2904 return '';
2905 }
2906 unset($data['xxxxx']);
4af8d5d3 2907 $result = 0;
220a90c5 2908 foreach($data as $index) {
2909 $result |= 1 << $index;
6e4dc10f 2910 }
220a90c5 2911 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
6e4dc10f 2912 }
eef868d1 2913
73fa96d5 2914 public function output_html($data, $query='') {
4af8d5d3 2915 // The order matters very much because of the implied numeric keys
2916 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
2917 $return = '<table><thead><tr>';
220a90c5 2918 $return .= '<input type="hidden" name="'.$this->get_full_name().'[xxxxx]" value="1" />'; // something must be submitted even if nothing selected
4af8d5d3 2919 foreach($days as $index => $day) {
220a90c5 2920 $return .= '<td><label for="'.$this->get_id().$index.'">'.get_string($day, 'calendar').'</label></td>';
4af8d5d3 2921 }
2922 $return .= '</tr></thead><tbody><tr>';
2923 foreach($days as $index => $day) {
220a90c5 2924 $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 2925 }
2926 $return .= '</tr></tbody></table>';
6153cf58 2927
587c7040 2928 return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query);
eef868d1 2929
6e4dc10f 2930 }
6e4dc10f 2931}
2932
220a90c5 2933
3d5c00b3 2934/**
4e781c7b 2935 * Admin setting that allows a user to pick appropriate roles for something.
5a412dbf 2936 */
4e781c7b 2937class admin_setting_pickroles extends admin_setting_configmulticheckbox {
2938 private $types;
2939
2940 /**
2941 * @param string $name Name of config variable
2942 * @param string $visiblename Display name
2943 * @param string $description Description
2944 * @param array $types Array of capabilities (usually moodle/legacy:something)
2945 * which identify roles that will be enabled by default. Default is the
2946 * student role
2947 */
73fa96d5 2948 public function __construct($name, $visiblename, $description, $types) {
2949 parent::__construct($name, $visiblename, $description, NULL, NULL);
775f811a 2950 $this->types = $types;
5a412dbf 2951 }
2952
73fa96d5 2953 public function load_choices() {
f33e1ed4 2954 global $CFG, $DB;
220a90c5 2955 if (empty($CFG->rolesactive)) {
2956 return false;
2957 }
2958 if (is_array($this->choices)) {
2959 return true;
2960 }
9101efd3 2961 if ($roles = get_all_roles()) {
220a90c5 2962 $this->choices = array();
2963 foreach($roles as $role) {
2964 $this->choices[$role->id] = format_string($role->name);
60f7d402 2965 }
220a90c5 2966 return true;
5a412dbf 2967 } else {
220a90c5 2968 return false;
5a412dbf 2969 }
2970 }
2971
73fa96d5 2972 public function get_defaultsetting() {
220a90c5 2973 global $CFG;
10f19c49 2974
220a90c5 2975 if (empty($CFG->rolesactive)) {
775f811a 2976 return null;
220a90c5 2977 }
2978 $result = array();
4e781c7b 2979 foreach($this->types as $capability) {
2980 if ($caproles = get_roles_with_capability($capability, CAP_ALLOW)) {
2981 foreach ($caproles as $caprole) {
10f19c49 2982 $result[$caprole->id] = 1;
4e781c7b 2983 }
5a412dbf 2984 }
73afaf5b 2985 }
220a90c5 2986 return $result;
5a412dbf 2987 }
220a90c5 2988}
5a412dbf 2989
e2249afe 2990/**
7fb0303d 2991 * Text field with an advanced checkbox, that controls a additional "fix_$name" setting.
e2249afe 2992 */
7fb0303d 2993class admin_setting_text_with_advanced extends admin_setting_configtext {
73fa96d5 2994 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype) {
2995 parent::__construct($name, $visiblename, $description,
e2249afe 2996 $defaultsetting, $paramtype);
2997 }
2998
73fa96d5 2999 public function get_setting() {
e2249afe 3000 $value = parent::get_setting();
3001 $fix = $this->config_read('fix_' . $this->name);
3002 if (is_null($value) or is_null($fix)) {
3003 return NULL;
3004 }
3005 return array('value' => $value, 'fix' => $fix);
3006 }
3007
73fa96d5 3008 public function write_setting($data) {
e2249afe 3009 $error = parent::write_setting($data['value']);
3010 if (!$error) {
3011 if (empty($data['fix'])) {
3012 $ok = $this->config_write('fix_' . $this->name, 0);
3013 } else {
3014 $ok = $this->config_write('fix_' . $this->name, 1);
3015 }
3016 if (!$ok) {
3017 $error = get_string('errorsetting', 'admin');
3018 }
3019 }
3020 return $error;
3021 }
3022
73fa96d5 3023 public function output_html($data, $query='') {
e2249afe 3024 $default = $this->get_defaultsetting();
3025 $defaultinfo = array();
de9c561b 3026 if (isset($default['value'])) {
3027 if ($default['value'] === '') {
3028 $defaultinfo[] = "''";
3029 } else {
3030 $defaultinfo[] = $default['value'];
3031 }
e2249afe 3032 }
3033 if (!empty($default['fix'])) {
3034 $defaultinfo[] = get_string('advanced');
3035 }
3036 $defaultinfo = implode(', ', $defaultinfo);
3037
3038 $fix = !empty($data['fix']);
a6600395 3039 $return = '<div class="form-text defaultsnext">' .
e2249afe 3040 '<input type="text" size="' . $this->size . '" id="' . $this->get_id() .
3041 '" name="' . $this->get_full_name() . '[value]" value="' . s($data['value']) . '" />' .
3042 ' <input type="checkbox" class="form-checkbox" id="' .
3043 $this->get_id() . '_fix" name="' . $this->get_full_name() .
3044 '[fix]" value="1" ' . ($fix ? 'checked="checked"' : '') . ' />' .
3045 ' <label for="' . $this->get_id() . '_fix">' .
3046 get_string('advanced') . '</label></div>';
3047
3048 return format_admin_setting($this, $this->visiblename, $return,
3049 $this->description, true, '', $defaultinfo, $query);
3050 }
3051}
3052
3053/**
7fb0303d 3054 * Dropdown menu with an advanced checkbox, that controls a additional "fix_$name" setting.
e2249afe 3055 */
7fb0303d 3056class admin_setting_combo_with_advanced extends admin_setting_configselect {
73fa96d5 3057 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3058 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
e2249afe 3059 }
3060
73fa96d5 3061 public function get_setting() {
e2249afe 3062 $value = parent::get_setting();
3063 $fix = $this->config_read('fix_' . $this->name);
3064 if (is_null($value) or is_null($fix)) {
3065 return NULL;
3066 }
3067 return array('value' => $value, 'fix' => $fix);
3068 }
3069
73fa96d5 3070 public function write_setting($data) {
e2249afe 3071 $error = parent::write_setting($data['value']);
3072 if (!$error) {
3073 if (empty($data['fix'])) {
3074 $ok = $this->config_write('fix_' . $this->name, 0);
3075 } else {
3076 $ok = $this->config_write('fix_' . $this->name, 1);
3077 }
3078 if (!$ok) {
3079 $error = get_string('errorsetting', 'admin');
3080 }
3081 }
3082 return $error;
3083 }
3084
73fa96d5 3085 public function output_html($data, $query='') {
e2249afe 3086 $default = $this->get_defaultsetting();
3087 $current = $this->get_setting();
3088
3089 list($selecthtml, $warning) = $this->output_select_html($data['value'],
3090 $current['value'], $default['value'], '[value]');
3091 if (!$selecthtml) {
3092 return '';
3093 }
3094
3095 if (!is_null($default) and array_key_exists($default['value'], $this->choices)) {
3096 $defaultinfo = array();
3097 if (isset($this->choices[$default['value']])) {
3098 $defaultinfo[] = $this->choices[$default['value']];
3099 }
3100 if (!empty($default['fix'])) {
3101 $defaultinfo[] = get_string('advanced');
3102 }
3103 $defaultinfo = implode(', ', $defaultinfo);
3104 } else {
3105 $defaultinfo = '';
3106 }
3107
3108 $fix = !empty($data['fix']);
a6600395 3109 $return = '<div class="form-select defaultsnext">' . $selecthtml .
e2249afe 3110 ' <input type="checkbox" class="form-checkbox" id="' .
3111 $this->get_id() . '_fix" name="' . $this->get_full_name() .
3112 '[fix]" value="1" ' . ($fix ? 'checked="checked"' : '') . ' />' .
3113 ' <label for="' . $this->get_id() . '_fix">' .
3114 get_string('advanced') . '</label></div>';
3115
3116 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, $warning, $defaultinfo, $query);
3117 }
3118}
3119
e2249afe 3120/**
7fb0303d 3121 * Specialisation of admin_setting_combo_with_advanced for easy yes/no choices.
e2249afe 3122 */
7fb0303d 3123class admin_setting_yesno_with_advanced extends admin_setting_combo_with_advanced {
73fa96d5 3124 public function __construct($name, $visiblename, $description, $defaultsetting) {
e2249afe 3125 parent::__construct($name, $visiblename, $description,
3126 $defaultsetting, array(get_string('no'), get_string('yes')));
3127 }
3128}
3129
4e781c7b 3130/**
3131 * Graded roles in gradebook
3132 */
3133class admin_setting_special_gradebookroles extends admin_setting_pickroles {
73fa96d5 3134 public function __construct() {
3135 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
3136 get_string('configgradebookroles', 'admin'),
3137 array('moodle/legacy:student'));
4e781c7b 3138 }
3139}
3140
3141
91f9a62c 3142class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
73fa96d5 3143 public function write_setting($data) {
f33e1ed4 3144 global $CFG, $DB;
91f9a62c 3145
3146 $oldvalue = $this->config_read($this->name);
3147 $return = parent::write_setting($data);
3148 $newvalue = $this->config_read($this->name);
3149
3150 if ($oldvalue !== $newvalue) {
3151 // force full regrading
f33e1ed4 3152 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
91f9a62c 3153 }
3154
3155 return $return;
c5d2d0dd 3156 }
91f9a62c 3157}
3158
220a90c5 3159/**
3160 * Which roles to show on course decription page
3161 */
4e781c7b 3162class admin_setting_special_coursemanager extends admin_setting_pickroles {
73fa96d5 3163 public function __construct() {
3164 parent::__construct('coursemanager', get_string('coursemanager', 'admin'),
3165 get_string('configcoursemanager', 'admin'),
3166 array('moodle/legacy:editingteacher'));
220a90c5 3167 }
3168}
5a412dbf 3169
653a8648 3170class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
3171 function admin_setting_special_gradelimiting() {
3172 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
3173 get_string('configunlimitedgrades', 'grades'), '0', '1', '0');
3174 }
3175
3176 function regrade_all() {
3177 global $CFG;
3178 require_once("$CFG->libdir/gradelib.php");
3179 grade_force_site_regrading();
3180 }
3181
3182 function write_setting($data) {
3183 $previous = $this->get_setting();
3184
3185 if ($previous === null) {
3186 if ($data) {
3187 $this->regrade_all();
3188 }
3189 } else {
3190 if ($data != $previous) {
3191 $this->regrade_all();
3192 }
3193 }
3194 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3195 }
3196
3197}
3198
220a90c5 3199/**
3200 * Primary grade export plugin - has state tracking.
3201 */
3202class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
73fa96d5 3203 public function __construct() {
3204 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
3205 get_string('configgradeexport', 'admin'), array(), NULL);
5a412dbf 3206 }
3207
73fa96d5 3208 public function load_choices() {
220a90c5 3209 if (is_array($this->choices)) {
3210 return true;
3211 }
3212 $this->choices = array();
3213
3214 if ($plugins = get_list_of_plugins('grade/export')) {
3215 foreach($plugins as $plugin) {
3216 $this->choices[$plugin] = get_string('modulename', 'gradeexport_'.$plugin);
3217 }
3218 }
3219 return true;
3220 }
5a412dbf 3221}
6e4dc10f 3222
220a90c5 3223/**
3224 * Grade category settings
d42c64ba 3225 */
220a90c5 3226class admin_setting_gradecat_combo extends admin_setting {
d42c64ba 3227
73fa96d5 3228 public $choices;
220a90c5 3229
73fa96d5 3230 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
220a90c5 3231 $this->choices = $choices;
73fa96d5 3232 parent::__construct($name, $visiblename, $description, $defaultsetting);
d42c64ba 3233 }
3234
73fa96d5 3235 public function get_setting() {
d42c64ba 3236 global $CFG;
220a90c5 3237
3238 $value = $this->config_read($this->name);
3239 $flag = $this->config_read($this->name.'_flag');
3240
3241 if (is_null($value) or is_null($flag)) {
3242 return NULL;
d42c64ba 3243 }
220a90c5 3244
3245 $flag = (int)$flag;
3246 $forced = (boolean)(1 & $flag); // first bit
3247 $adv = (boolean)(2 & $flag); // second bit
3248
3249 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
d42c64ba 3250 }
3251
73fa96d5 3252 public function write_setting($data) {
220a90c5 3253 global $CFG;
d42c64ba 3254
220a90c5 3255 $value = $data['value'];
3256 $forced = empty($data['forced']) ? 0 : 1;
3257 $adv = empty($data['adv']) ? 0 : 2;
3258 $flag = ($forced | $adv); //bitwise or
3259
3260 if (!in_array($value, array_keys($this->choices))) {
3261 return 'Error setting ';
3262 }
3263
3264 $oldvalue = $this->config_read($this->name);
3265 $oldflag = (int)$this->config_read($this->name.'_flag');
3266 $oldforced = (1 & $oldflag); // first bit
3267
3268 $result1 = $this->config_write($this->name, $value);
3269 $result2 = $this->config_write($this->name.'_flag', $flag);
3270
3271 // force regrade if needed
3272 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
3273 require_once($CFG->libdir.'/gradelib.php');
3274 grade_category::updated_forced_settings();
3275 }
3276
3277 if ($result1 and $result2) {
3278 return '';
d42c64ba 3279 } else {
220a90c5 3280 return get_string('errorsetting', 'admin');
d42c64ba 3281 }
3282 }
3283
73fa96d5 3284 public function output_html($data, $query='') {
220a90c5 3285 $value = $data['value'];
3286 $forced = !empty($data['forced']);
3287 $adv = !empty($data['adv']);
d42c64ba 3288
b5d4efc6 3289 $default = $this->get_defaultsetting();
3290 if (!is_null($default)) {
3291 $defaultinfo = array();
3292 if (isset($this->choices[$default['value']])) {
3293 $defaultinfo[] = $this->choices[$default['value']];
3294 }
3295 if (!empty($default['forced'])) {
3296 $defaultinfo[] = get_string('force');
3297 }
3298 if (!empty($default['adv'])) {
3299 $defaultinfo[] = get_string('advanced');
3300 }
3301 $defaultinfo = implode(', ', $defaultinfo);
c5d2d0dd 3302
b5d4efc6 3303 } else {
3304 $defaultinfo = NULL;
3305 }
3306
3307
3308 $return = '<div class="form-group">';
3309 $return .= '<select class="form-select" id="'.$this->get_id().'" name="'.$this->get_full_name().'[value]">';
220a90c5 3310 foreach ($this->choices as $key => $val) {
3311 // the string cast is needed because key may be integer - 0 is equal to most strings!
3312 $return .= '<option value="'.$key.'"'.((string)$key==$value ? ' selected="selected"' : '').'>'.$val.'</option>';
3313 }
3314 $return .= '</select>';
587c7040 3315 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'force" name="'.$this->get_full_name().'[forced]" value="1" '.($forced ? 'checked="checked"' : '').' />'
3316 .'<label for="'.$this->get_id().'force">'.get_string('force').'</label>';
3317 $return .= '<input type="checkbox" class="form-checkbox" id="'.$this->get_id().'adv" name="'.$this->get_full_name().'[adv]" value="1" '.($adv ? 'checked="checked"' : '').' />'
3318 .'<label for="'.$this->get_id().'adv">'.get_string('advanced').'</label>';
b5d4efc6 3319 $return .= '</div>';
220a90c5 3320
b5d4efc6 3321 return format_admin_setting($this, $this->visiblename, $return, $this->description, true, '', $defaultinfo, $query);
220a90c5 3322 }
3323}
3324
3325
3326/**
3327 * Selection of grade report in user profiles
3328 */