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