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