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