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