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