javascript: MDL-16695 print_footer('empty'); I had the wrong type of quotes.
[moodle.git] / lib / upgradelib.php
CommitLineData
96db14f9 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
db9d4a3d 17
18/**
8580535b 19 * Various upgrade/install related functions and classes.
db9d4a3d 20 *
96db14f9 21 * @package moodlecore
22 * @subpackage upgrade
23 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
db9d4a3d 25 */
26
72fb21b6 27/** UPGRADE_LOG_NORMAL = 0 */
db9d4a3d 28define('UPGRADE_LOG_NORMAL', 0);
72fb21b6 29/** UPGRADE_LOG_NOTICE = 1 */
db9d4a3d 30define('UPGRADE_LOG_NOTICE', 1);
72fb21b6 31/** UPGRADE_LOG_ERROR = 2 */
795a08ad 32define('UPGRADE_LOG_ERROR', 2);
33
34/**
35 * Exception indicating unknown error during upgrade.
72fb21b6 36 *
37 * @package moodlecore
38 * @subpackage upgrade
39 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
795a08ad 41 */
42class upgrade_exception extends moodle_exception {
43 function __construct($plugin, $version) {
44 global $CFG;
45 $a = (object)array('plugin'=>$plugin, 'version'=>$version);
46 parent::__construct('upgradeerror', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a);
47 }
48}
49
50/**
51 * Exception indicating downgrade error during upgrade.
72fb21b6 52 *
53 * @package moodlecore
54 * @subpackage upgrade
55 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
56 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
795a08ad 57 */
58class downgrade_exception extends moodle_exception {
59 function __construct($plugin, $oldversion, $newversion) {
60 global $CFG;
61 $plugin = is_null($plugin) ? 'moodle' : $plugin;
62 $a = (object)array('plugin'=>$plugin, 'oldversion'=>$oldversion, 'newversion'=>$newversion);
63 parent::__construct('cannotdowngrade', 'debug', "$CFG->wwwroot/$CFG->admin/index.php", $a);
64 }
65}
66
72fb21b6 67/**
68 * @package moodlecore
69 * @subpackage upgrade
70 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
71 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
72 */
795a08ad 73class upgrade_requires_exception extends moodle_exception {
74 function __construct($plugin, $pluginversion, $currentmoodle, $requiremoodle) {
75 global $CFG;
76 $a = new object();
77 $a->pluginname = $plugin;
78 $a->pluginversion = $pluginversion;
79 $a->currentmoodle = $currentmoodle;
80 $a->requiremoodle = $currentmoodle;
81 parent::__construct('pluginrequirementsnotmet', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a);
82 }
83}
84
72fb21b6 85/**
86 * @package moodlecore
87 * @subpackage upgrade
88 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
89 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
90 */
795a08ad 91class plugin_defective_exception extends moodle_exception {
92 function __construct($plugin, $details) {
93 global $CFG;
94 parent::__construct('detectedbrokenplugin', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $plugin, $details);
95 }
96}
db9d4a3d 97
98/**
99 * Insert or update log display entry. Entry may already exist.
100 * $module, $action must be unique
101 *
72fb21b6 102 * @global object
db9d4a3d 103 * @param string $module
104 * @param string $action
105 * @param string $mtable
106 * @param string $field
107 * @return void
108 *
109 */
110function update_log_display_entry($module, $action, $mtable, $field) {
111 global $DB;
112
113 if ($type = $DB->get_record('log_display', array('module'=>$module, 'action'=>$action))) {
114 $type->mtable = $mtable;
115 $type->field = $field;
116 $DB->update_record('log_display', $type);
117
118 } else {
119 $type = new object();
120 $type->module = $module;
121 $type->action = $action;
122 $type->mtable = $mtable;
123 $type->field = $field;
124
125 $DB->insert_record('log_display', $type, false);
126 }
127}
128
129/**
130 * Upgrade savepoint, marks end of each upgrade block.
131 * It stores new main version, resets upgrade timeout
132 * and abort upgrade if user cancels page loading.
133 *
134 * Please do not make large upgrade blocks with lots of operations,
135 * for example when adding tables keep only one table operation per block.
136 *
72fb21b6 137 * @global object
db9d4a3d 138 * @param bool $result false if upgrade step failed, true if completed
139 * @param string or float $version main version
140 * @param bool $allowabort allow user to abort script execution here
141 * @return void
142 */
143function upgrade_main_savepoint($result, $version, $allowabort=true) {
144 global $CFG;
145
795a08ad 146 if (!$result) {
147 throw new upgrade_exception('moodle core', $version);
148 }
149
150 if ($CFG->version >= $version) {
151 // something really wrong is going on in main upgrade script
152 throw new downgrade_exception(null, $CFG->version, $version);
db9d4a3d 153 }
154
795a08ad 155 set_config('version', $version);
156 upgrade_log(UPGRADE_LOG_NORMAL, null, 'Upgrade savepoint reached');
157
db9d4a3d 158 // reset upgrade timeout to default
159 upgrade_set_timeout();
160
161 // this is a safe place to stop upgrades if user aborts page loading
162 if ($allowabort and connection_aborted()) {
163 die;
164 }
165}
166
167/**
168 * Module upgrade savepoint, marks end of module upgrade blocks
169 * It stores module version, resets upgrade timeout
170 * and abort upgrade if user cancels page loading.
171 *
72fb21b6 172 * @global object
db9d4a3d 173 * @param bool $result false if upgrade step failed, true if completed
174 * @param string or float $version main version
175 * @param string $modname name of module
176 * @param bool $allowabort allow user to abort script execution here
177 * @return void
178 */
179function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) {
180 global $DB;
181
795a08ad 182 if (!$result) {
183 throw new upgrade_exception("mod/$modname", $version);
184 }
185
db9d4a3d 186 if (!$module = $DB->get_record('modules', array('name'=>$modname))) {
187 print_error('modulenotexist', 'debug', '', $modname);
188 }
189
795a08ad 190 if ($module->version >= $version) {
191 // something really wrong is going on in upgrade script
192 throw new downgrade_exception("mod/$modname", $module->version, $version);
db9d4a3d 193 }
795a08ad 194 $module->version = $version;
195 $DB->update_record('modules', $module);
196 upgrade_log(UPGRADE_LOG_NORMAL, "mod/$modname", 'Upgrade savepoint reached');
db9d4a3d 197
198 // reset upgrade timeout to default
199 upgrade_set_timeout();
200
201 // this is a safe place to stop upgrades if user aborts page loading
202 if ($allowabort and connection_aborted()) {
203 die;
204 }
205}
206
207/**
208 * Blocks upgrade savepoint, marks end of blocks upgrade blocks
209 * It stores block version, resets upgrade timeout
210 * and abort upgrade if user cancels page loading.
211 *
72fb21b6 212 * @global object
db9d4a3d 213 * @param bool $result false if upgrade step failed, true if completed
214 * @param string or float $version main version
215 * @param string $blockname name of block
216 * @param bool $allowabort allow user to abort script execution here
217 * @return void
218 */
795a08ad 219function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) {
db9d4a3d 220 global $DB;
221
795a08ad 222 if (!$result) {
223 throw new upgrade_exception("blocks/$blockname", $version);
224 }
225
db9d4a3d 226 if (!$block = $DB->get_record('block', array('name'=>$blockname))) {
227 print_error('blocknotexist', 'debug', '', $blockname);
228 }
229
795a08ad 230 if ($block->version >= $version) {
231 // something really wrong is going on in upgrade script
232 throw new downgrade_exception("blocks/$blockname", $block->version, $version);
db9d4a3d 233 }
795a08ad 234 $block->version = $version;
235 $DB->update_record('block', $block);
236 upgrade_log(UPGRADE_LOG_NORMAL, "blocks/$blockname", 'Upgrade savepoint reached');
db9d4a3d 237
238 // reset upgrade timeout to default
239 upgrade_set_timeout();
240
241 // this is a safe place to stop upgrades if user aborts page loading
242 if ($allowabort and connection_aborted()) {
243 die;
244 }
245}
246
247/**
248 * Plugins upgrade savepoint, marks end of blocks upgrade blocks
249 * It stores plugin version, resets upgrade timeout
250 * and abort upgrade if user cancels page loading.
251 *
252 * @param bool $result false if upgrade step failed, true if completed
253 * @param string or float $version main version
254 * @param string $type name of plugin
255 * @param string $dir location of plugin
256 * @param bool $allowabort allow user to abort script execution here
257 * @return void
258 */
259function upgrade_plugin_savepoint($result, $version, $type, $dir, $allowabort=true) {
795a08ad 260 if (!$result) {
261 throw new upgrade_exception("$type/$dir", $version);
db9d4a3d 262 }
263
ba04999c 264 /// TODO: Check that $type is a correct type - based on get_plugin_types()
265 /// TODO: Check that $dir (that perhaps should be named $name) is an existing plugin
266
795a08ad 267 $fullname = $type.'_'.$dir;
268 $component = $type.'/'.$dir;
269
270 $installedversion = get_config($fullname, 'version');
271 if ($installedversion >= $version) {
272 // Something really wrong is going on in the upgrade script
273 throw new downgrade_exception($component, $installedversion, $version);
274 }
275 set_config('version', $version, $fullname);
276 upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached');
277
db9d4a3d 278 // Reset upgrade timeout to default
279 upgrade_set_timeout();
280
281 // This is a safe place to stop upgrades if user aborts page loading
282 if ($allowabort and connection_aborted()) {
283 die;
284 }
285}
286
287
288/**
289 * Upgrade plugins
290 *
72fb21b6 291 * @global object
292 * @global object
db9d4a3d 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
296 */
194cdca8 297function upgrade_plugins($type, $dir, $startcallback, $endcallback, $verbose) {
db9d4a3d 298 global $CFG, $DB;
299
300/// special cases
301 if ($type === 'mod') {
194cdca8 302 return upgrade_plugins_modules($startcallback, $endcallback, $verbose);
795a08ad 303 } else if ($type === 'block') {
194cdca8 304 return upgrade_plugins_blocks($startcallback, $endcallback, $verbose);
db9d4a3d 305 }
306
307 $plugs = get_list_of_plugins($dir);
db9d4a3d 308
309 foreach ($plugs as $plug) {
310
795a08ad 311 $fullplug = $CFG->dirroot.'/'.$dir.'/'.$plug;
312 $component = $type.'/'.$plug; // standardised plugin name
db9d4a3d 313
795a08ad 314 if (!is_readable($fullplug.'/version.php')) {
315 continue;
db9d4a3d 316 }
317
795a08ad 318 $plugin = new object();
319 require($fullplug.'/version.php'); // defines $plugin with version etc
db9d4a3d 320
795a08ad 321 if (empty($plugin->version)) {
322 throw new plugin_defective_exception($component, 'Missing version value in version.php');
db9d4a3d 323 }
324
795a08ad 325 $plugin->name = $plug; // The name MUST match the directory
326 $plugin->fullname = $type.'_'.$plug; // The name MUST match the directory
327
328
db9d4a3d 329 if (!empty($plugin->requires)) {
330 if ($plugin->requires > $CFG->version) {
795a08ad 331 throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires);
db9d4a3d 332 }
333 }
334
db9d4a3d 335 $installedversion = get_config($plugin->fullname, 'version');
336
795a08ad 337 if (empty($installedversion)) { // new installation
194cdca8 338 $startcallback($component, true, $verbose);
db9d4a3d 339
795a08ad 340 /// Install tables if defined
341 if (file_exists($fullplug.'/db/install.xml')) {
342 $DB->get_manager()->install_from_xmldb_file($fullplug.'/db/install.xml');
db9d4a3d 343 }
795a08ad 344 /// execute post install file
345 if (file_exists($fullplug.'/db/install.php')) {
346 require_once($fullplug.'/db/install.php');
347 $post_install_function = 'xmldb_'.$plugin->fullname.'_install';;
348 $post_install_function();
349 }
350
351 /// store version
352 upgrade_plugin_savepoint(true, $plugin->version, $type, $plug, false);
353
354 /// Install various components
355 update_capabilities($component);
356 events_update_definition($component);
357 message_update_providers($component);
358
194cdca8 359 $endcallback($component, true, $verbose);
795a08ad 360
361 } else if ($installedversion < $plugin->version) { // upgrade
362 /// Run the upgrade function for the plugin.
194cdca8 363 $startcallback($component, false, $verbose);
795a08ad 364
365 if (is_readable($fullplug.'/db/upgrade.php')) {
366 require_once($fullplug.'/db/upgrade.php'); // defines upgrading function
367
368 $newupgrade_function = 'xmldb_'.$plugin->fullname.'_upgrade';
369 $result = $newupgrade_function($installedversion);
370 } else {
371 $result = true;
372 }
373
374 $installedversion = get_config($plugin->fullname, 'version');
375 if ($installedversion < $plugin->version) {
376 // store version if not already there
377 upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false);
378 }
379
380 /// Upgrade various components
381 update_capabilities($component);
382 events_update_definition($component);
383 message_update_providers($component);
384
194cdca8 385 $endcallback($component, false, $verbose);
795a08ad 386
387 } else if ($installedversion > $plugin->version) {
388 throw new downgrade_exception($component, $installedversion, $plugin->version);
db9d4a3d 389 }
390 }
db9d4a3d 391}
392
393/**
394 * Find and check all modules and load them up or upgrade them if necessary
72fb21b6 395 *
396 * @global object
397 * @global object
db9d4a3d 398 */
194cdca8 399function upgrade_plugins_modules($startcallback, $endcallback, $verbose) {
db9d4a3d 400 global $CFG, $DB;
401
795a08ad 402 $mods = get_list_of_plugins('mod');
db9d4a3d 403
404 foreach ($mods as $mod) {
405
406 if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it
407 continue;
408 }
409
795a08ad 410 $fullmod = $CFG->dirroot.'/mod/'.$mod;
411 $component = 'mod/'.$mod;
db9d4a3d 412
795a08ad 413 if (!is_readable($fullmod.'/version.php')) {
414 throw new plugin_defective_exception($component, 'Missing version.php');
db9d4a3d 415 }
416
795a08ad 417 $module = new object();
418 require($fullmod .'/version.php'); // defines $module with version etc
db9d4a3d 419
795a08ad 420 if (empty($module->version)) {
c1fe2368 421 if (isset($module->version)) {
422 // Version is empty but is set - it means its value is 0 or ''. Let us skip such module.
423 // This is inteded for developers so they can work on the early stages of the module.
424 continue;
425 }
795a08ad 426 throw new plugin_defective_exception($component, 'Missing version value in version.php');
db9d4a3d 427 }
428
429 if (!empty($module->requires)) {
430 if ($module->requires > $CFG->version) {
795a08ad 431 throw new upgrade_requires_exception($component, $module->version, $CFG->version, $module->requires);
db9d4a3d 432 }
433 }
434
435 $module->name = $mod; // The name MUST match the directory
436
795a08ad 437 $currmodule = $DB->get_record('modules', array('name'=>$module->name));
db9d4a3d 438
795a08ad 439 if (empty($currmodule->version)) {
194cdca8 440 $startcallback($component, true, $verbose);
db9d4a3d 441
795a08ad 442 /// Execute install.xml (XMLDB) - must be present in all modules
443 $DB->get_manager()->install_from_xmldb_file($fullmod.'/db/install.xml');
db9d4a3d 444
445 /// Post installation hook - optional
446 if (file_exists("$fullmod/db/install.php")) {
447 require_once("$fullmod/db/install.php");
448 $post_install_function = 'xmldb_'.$module->name.'_install';;
449 $post_install_function();
450 }
451
452 /// Continue with the installation, roles and other stuff
453 $module->id = $DB->insert_record('modules', $module);
454
795a08ad 455 /// Install various components
456 update_capabilities($component);
457 events_update_definition($component);
458 message_update_providers($component);
459
194cdca8 460 $endcallback($component, true, $verbose);
db9d4a3d 461
795a08ad 462 } else if ($currmodule->version < $module->version) {
463 /// If versions say that we need to upgrade but no upgrade files are available, notify and continue
194cdca8 464 $startcallback($component, false, $verbose);
795a08ad 465
466 if (is_readable($fullmod.'/db/upgrade.php')) {
467 require_once($fullmod.'/db/upgrade.php'); // defines new upgrading function
468 $newupgrade_function = 'xmldb_'.$module->name.'_upgrade';
469 $result = $newupgrade_function($currmodule->version, $module);
470 } else {
471 $result = true;
472 }
473
474 $currmodule = $DB->get_record('modules', array('name'=>$module->name));
475 if ($currmodule->version < $module->version) {
476 // store version if not already there
477 upgrade_mod_savepoint($result, $module->version, $mod, false);
478 }
479
480 /// Upgrade various components
481 update_capabilities($component);
482 events_update_definition($component);
483 message_update_providers($component);
484
485 remove_dir($CFG->dataroot.'/cache', true); // flush cache
486
194cdca8 487 $endcallback($component, false, $verbose);
795a08ad 488
489 } else if ($currmodule->version > $module->version) {
490 throw new downgrade_exception($component, $currmodule->version, $module->version);
491 }
492 }
493}
db9d4a3d 494
db9d4a3d 495
795a08ad 496/**
497 * This function finds all available blocks and install them
498 * into blocks table or do all the upgrade process if newer.
72fb21b6 499 *
500 * @global object
501 * @global object
795a08ad 502 */
194cdca8 503function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) {
795a08ad 504 global $CFG, $DB;
505
795a08ad 506 require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
507
508 $blocktitles = array(); // we do not want duplicate titles
509
510 //Is this a first install
511 $first_install = null;
512
513 $blocks = get_list_of_plugins('blocks');
514
515 foreach ($blocks as $blockname) {
516
517 if (is_null($first_install)) {
518 $first_install = ($DB->count_records('block') == 0);
519 }
520
521 if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it
522 continue;
db9d4a3d 523 }
524
795a08ad 525 $fullblock = $CFG->dirroot.'/blocks/'.$blockname;
526 $component = 'block/'.$blockname;
db9d4a3d 527
795a08ad 528 if (!is_readable($fullblock.'/block_'.$blockname.'.php')) {
529 throw new plugin_defective_exception('block/'.$blockname, 'Missing main block class file.');
db9d4a3d 530 }
795a08ad 531 require_once($fullblock.'/block_'.$blockname.'.php');
db9d4a3d 532
795a08ad 533 $classname = 'block_'.$blockname;
534
535 if (!class_exists($classname)) {
536 throw new plugin_defective_exception($component, 'Can not load main class.');
537 }
538
539 $blockobj = new $classname; // This is what we 'll be testing
540 $blocktitle = $blockobj->get_title();
541
542 // OK, it's as we all hoped. For further tests, the object will do them itself.
543 if (!$blockobj->_self_test()) {
544 throw new plugin_defective_exception($component, 'Self test failed.');
545 }
546
547 $block = new object(); // This may be used to update the db below
548 $block->name = $blockname; // The name MUST match the directory
549 $block->version = $blockobj->get_version();
550 $block->cron = !empty($blockobj->cron) ? $blockobj->cron : 0;
551 $block->multiple = $blockobj->instance_allow_multiple() ? 1 : 0;
552
553 if (empty($block->version)) {
554 throw new plugin_defective_exception($component, 'Missing block version.');
555 }
556
557 $currblock = $DB->get_record('block', array('name'=>$block->name));
558
559 if (empty($currblock->version)) { // block not installed yet, so install it
560 // If it allows multiples, start with it enabled
561
562 $conflictblock = array_search($blocktitle, $blocktitles);
563 if ($conflictblock !== false) {
564 // Duplicate block titles are not allowed, they confuse people
565 // AND PHP's associative arrays ;)
566 throw new plugin_defective_exception($component, get_string('blocknameconflict', '', (object)array('name'=>$block->name, 'conflict'=>$conflictblock)));
567 }
194cdca8 568 $startcallback($component, true, $verbose);
795a08ad 569
570 if (file_exists($fullblock.'/db/install.xml')) {
571 $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml');
572 }
573 $block->id = $DB->insert_record('block', $block);
574
575 if (file_exists($fullblock.'/db/install.php')) {
576 require_once($fullblock.'/db/install.php');
577 $post_install_function = 'xmldb_block_'.$blockname.'_install';;
578 $post_install_function();
579 }
580
581 $blocktitles[$block->name] = $blocktitle;
582
583 // Install various components
584 update_capabilities($component);
585 events_update_definition($component);
586 message_update_providers($component);
587
194cdca8 588 $endcallback($component, true, $verbose);
795a08ad 589
590 } else if ($currblock->version < $block->version) {
194cdca8 591 $startcallback($component, false, $verbose);
795a08ad 592
593 if (is_readable($fullblock.'/db/upgrade.php')) {
594 require_once($fullblock.'/db/upgrade.php'); // defines new upgrading function
595 $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade';
596 $result = $newupgrade_function($currblock->version, $block);
597 } else {
598 $result = true;
599 }
600
601 $currblock = $DB->get_record('block', array('name'=>$block->name));
602 if ($currblock->version < $block->version) {
603 // store version if not already there
604 upgrade_block_savepoint($result, $block->version, $block->name, false);
605 }
606
607 if ($currblock->cron != $block->cron) {
608 // update cron flag if needed
609 $currblock->cron = $block->cron;
610 $DB->update_record('block', $currblock);
611 }
612
613 // Upgrade various componebts
614 events_update_definition($component);
615 update_capabilities($component);
616 message_update_providers($component);
617
194cdca8 618 $endcallback($component, false, $verbose);
795a08ad 619
620 } else if ($currblock->version > $block->version) {
621 throw new downgrade_exception($component, $currblock->version, $block->version);
622 }
623 }
624
625
626 // Finally, if we are in the first_install of BLOCKS setup frontpage and admin page blocks
627 if ($first_install) {
795a08ad 628 //Iterate over each course - there should be only site course here now
629 if ($courses = $DB->get_records('course')) {
630 foreach ($courses as $course) {
9d1d606e 631 blocks_add_default_course_blocks($course);
db9d4a3d 632 }
633 }
795a08ad 634
9d1d606e 635 blocks_add_default_system_blocks();
795a08ad 636 }
637}
638
639/**
640 * This function checks to see whether local database customisations are up-to-date
641 * by comparing $CFG->local_version to the variable $local_version defined in
642 * local/version.php. If not, it looks for a function called 'xmldb_local_upgrade'
643 * in a file called 'local/db/upgrade.php', and if it's there calls it with the
644 * appropiate $oldversion parameter. Then it updates $CFG->local_version.
645 *
72fb21b6 646 * @global object
647 * @global object
795a08ad 648 */
649function upgrade_local_db($startcallback, $endcallback) {
650 global $CFG, $DB;
651
652 // if we don't have code version, just return false
653 if (!file_exists($CFG->dirroot.'/local/version.php')) {
654 return;
655 }
656
657 $local_version = null;
658 require($CFG->dirroot.'/local/version.php'); // Get code versions
659
660 if (empty($CFG->local_version)) { // install
661 $startcallback('local', true);
662
663 if (file_exists($CFG->dirroot.'/local/db/install.php')) {
664 require_once($CFG->dirroot.'/local/db/install.php');
665 xmldb_local_install();
666 }
667 set_config('local_version', $local_version);
668
669 /// Install various components
670 events_update_definition('local');
671 update_capabilities('local');
672 message_update_providers('local');
673
674 $endcallback('local', true);
675
676 } else if ($local_version > $CFG->local_version) { // upgrade!
677 $startcallback('local', false);
678
679 if (file_exists($CFG->dirroot.'/local/db/upgrade.php')) {
680 require_once($CFG->dirroot.'/local/db/upgrade.php');
681 xmldb_local_upgrade($CFG->local_version);
682 }
683 set_config('local_version', $local_version);
684
685 /// Upgrade various components
686 events_update_definition('local');
687 update_capabilities('local');
688 message_update_providers('local');
689
690 $endcallback('local', false);
691
692 } else if ($local_version < $CFG->local_version) {
693 throw new downgrade_exception('local', $CFG->local_version, $local_version);
db9d4a3d 694 }
695}
696
697
72fb21b6 698/**
699 * upgrade logging functions
700 *
701 * @global object
702 */
db9d4a3d 703
795a08ad 704function upgrade_handle_exception($ex, $plugin=null) {
705 global $CFG;
706
707 if ($ex instanceof moodle_exception) {
708 $details = get_string($ex->errorcode, $ex->module, $ex->a)."<br />debugging:".$ex->debuginfo;
709 } else {
710 $details = get_string('generalexceptionmessage', 'error', $ex->getMessage());
711 }
712 $info = "Exception: ".get_class($ex);
713 $backtrace = $ex->getTrace();
714 $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex));
715 array_unshift($backtrace, $place);
716
717 /// first log upgrade error
718 upgrade_log(UPGRADE_LOG_ERROR, $plugin, $info, $details, $backtrace);
719
720 // always turn on debugging - admins need to know what is going on
721 $CFG->debug = DEBUG_DEVELOPER;
722
723 // now print the exception info as usually
724 if ($ex instanceof moodle_exception) {
725 _print_normal_error($ex->errorcode, $ex->module, $ex->a, $ex->link, $backtrace, $ex->debuginfo);
726 } else {
727 _print_normal_error('generalexceptionmessage', 'error', $ex->getMessage(), '', $backtrace);
728 }
729
730 die; // not reached
db9d4a3d 731}
732
733/**
734 * Adds log entry into upgrade_log table
735 *
72fb21b6 736 * @global object
737 * @global object
738 * @global object
db9d4a3d 739 * @param int $type UPGRADE_LOG_NORMAL, UPGRADE_LOG_NOTICE or UPGRADE_LOG_ERROR
740 * @param string $plugin plugin or null if main
741 * @param string $info short description text of log entry
742 * @param string $details long problem description
743 * @param string $backtrace string used for errors only
744 * @return void
745 */
746function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) {
747 global $DB, $USER, $CFG;
748
795a08ad 749 $plugin = ($plugin==='moodle') ? null : $plugin;
750
b4154c2d 751 $backtrace = print_backtrace($backtrace, true, true);
db9d4a3d 752
753 $version = null;
754
755 //first try to find out current version number
795a08ad 756 if (empty($plugin) or $plugin === 'moodle') {
db9d4a3d 757 //main
758 $version = $CFG->version;
759
795a08ad 760 } else if ($plugin === 'local') {
761 //customisation
762 $version = $CFG->local_version;
763
764 } else if (strpos($plugin, 'mod/') === 0) {
db9d4a3d 765 try {
766 $modname = substr($plugin, strlen('mod/'));
767 $version = $DB->get_field('modules', 'version', array('name'=>$modname));
795a08ad 768 $version = ($version === false) ? null : $version;
db9d4a3d 769 } catch (Exception $ignored) {
770 }
771
795a08ad 772 } else if (strpos($plugin, 'block/') === 0) {
db9d4a3d 773 try {
795a08ad 774 $blockname = substr($plugin, strlen('block/'));
db9d4a3d 775 if ($block = $DB->get_record('block', array('name'=>$blockname))) {
776 $version = $block->version;
777 }
778 } catch (Exception $ignored) {
779 }
795a08ad 780
781 } else {
782 $pluginversion = get_config(str_replace('/', '_', $plugin), 'version');
783 if (!empty($pluginversion)) {
784 $version = $pluginversion;
785 }
db9d4a3d 786 }
787
788 $log = new object();
789 $log->type = $type;
790 $log->plugin = $plugin;
791 $log->version = $version;
792 $log->info = $info;
793 $log->details = $details;
794 $log->backtrace = $backtrace;
795 $log->userid = $USER->id;
796 $log->timemodified = time();
db9d4a3d 797 try {
798 $DB->insert_record('upgrade_log', $log);
799 } catch (Exception $ignored) {
795a08ad 800 // possible during install or 2.0 upgrade
db9d4a3d 801 }
802}
803
804/**
805 * Marks start of upgrade, blocks any other access to site.
806 * The upgrade is finished at the end of script or after timeout.
72fb21b6 807 *
808 * @global object
809 * @global object
810 * @global object
db9d4a3d 811 */
812function upgrade_started($preinstall=false) {
c13a5e71 813 global $CFG, $DB, $PAGE;
db9d4a3d 814
815 static $started = false;
816
817 if ($preinstall) {
818 ignore_user_abort(true);
819 upgrade_setup_debug(true);
820
821 } else if ($started) {
822 upgrade_set_timeout(120);
823
824 } else {
c13a5e71 825 if (!CLI_SCRIPT and !$PAGE->headerprinted) {
db9d4a3d 826 $strupgrade = get_string('upgradingversion', 'admin');
827
9ace5094 828 print_header($strupgrade.' - Moodle '.$CFG->target_release, $strupgrade,
db9d4a3d 829 build_navigation(array(array('name' => $strupgrade, 'link' => null, 'type' => 'misc'))), '',
830 upgrade_get_javascript(), false, '&nbsp;', '&nbsp;');
831 }
832
833 ignore_user_abort(true);
834 register_shutdown_function('upgrade_finished_handler');
835 upgrade_setup_debug(true);
836 set_config('upgraderunning', time()+300);
837 $started = true;
838 }
839}
840
841/**
842 * Internal function - executed if upgrade interruped.
843 */
844function upgrade_finished_handler() {
845 upgrade_finished();
846}
847
848/**
849 * Indicates upgrade is finished.
850 *
851 * This function may be called repeatedly.
72fb21b6 852 *
853 * @global object
854 * @global object
db9d4a3d 855 */
856function upgrade_finished($continueurl=null) {
857 global $CFG, $DB;
858
859 if (!empty($CFG->upgraderunning)) {
860 unset_config('upgraderunning');
861 upgrade_setup_debug(false);
862 ignore_user_abort(false);
863 if ($continueurl) {
864 print_continue($continueurl);
9ace5094 865 print_footer('upgrade');
db9d4a3d 866 die;
867 }
868 }
869}
870
72fb21b6 871/**
872 * @global object
873 * @global object
874 */
db9d4a3d 875function upgrade_setup_debug($starting) {
876 global $CFG, $DB;
877
878 static $originaldebug = null;
879
880 if ($starting) {
881 if ($originaldebug === null) {
882 $originaldebug = $DB->get_debug();
883 }
884 if (!empty($CFG->upgradeshowsql)) {
885 $DB->set_debug(true);
886 }
887 } else {
888 $DB->set_debug($originaldebug);
889 }
890}
891
72fb21b6 892/**
893 * @global object
894 */
90509582 895function print_upgrade_reload($url) {
896 global $CFG;
897
898 echo "<br />";
899 echo '<div class="continuebutton">';
900 echo '<a href="'.$url.'" title="'.get_string('reload').'" ><img src="'.$CFG->pixpath.'/i/reload.gif" alt="" /> '.get_string('reload').'</a>';
901 echo '</div><br />';
902}
903
db9d4a3d 904function print_upgrade_separator() {
905 if (!CLI_SCRIPT) {
906 echo '<hr />';
907 }
908}
909
795a08ad 910/**
911 * Default start upgrade callback
912 * @param string $plugin
913 * @param bool $installation true if installation, false menas upgrade
914 */
194cdca8 915function print_upgrade_part_start($plugin, $installation, $verbose) {
795a08ad 916 if (empty($plugin) or $plugin == 'moodle') {
917 upgrade_started($installation); // does not store upgrade running flag yet
194cdca8 918 if ($verbose) {
919 print_heading(get_string('coresystem'));
920 }
795a08ad 921 } else {
922 upgrade_started();
194cdca8 923 if ($verbose) {
924 print_heading($plugin);
925 }
795a08ad 926 }
927 if ($installation) {
928 if (empty($plugin) or $plugin == 'moodle') {
929 // no need to log - log table not yet there ;-)
930 } else {
931 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin installation');
932 }
933 } else {
934 if (empty($plugin) or $plugin == 'moodle') {
935 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting core upgrade');
936 } else {
937 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin upgrade');
938 }
939 }
940}
941
942/**
943 * Default end upgrade callback
944 * @param string $plugin
945 * @param bool $installation true if installation, false menas upgrade
946 */
194cdca8 947function print_upgrade_part_end($plugin, $installation, $verbose) {
795a08ad 948 upgrade_started();
949 if ($installation) {
950 if (empty($plugin) or $plugin == 'moodle') {
951 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core installed');
952 } else {
953 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin installed');
954 }
955 } else {
956 if (empty($plugin) or $plugin == 'moodle') {
957 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core upgraded');
958 } else {
959 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin upgraded');
960 }
961 }
194cdca8 962 if ($verbose) {
963 notify(get_string('success'), 'notifysuccess');
964 print_upgrade_separator();
96db14f9 965 }
966}
967
72fb21b6 968/**
969 * @global object
970 */
db9d4a3d 971function upgrade_get_javascript() {
972 global $CFG;
2d6b0b04 973 return '<script type="text/javascript" src="'.$CFG->wwwroot.'/lib/scroll_to_page_end.js"></script>';
db9d4a3d 974}
975
976
977/**
978 * Try to upgrade the given language pack (or current language)
72fb21b6 979 * @global object
db9d4a3d 980 */
981function upgrade_language_pack($lang='') {
982 global $CFG;
983
984 if (empty($lang)) {
985 $lang = current_language();
986 }
987
988 if ($lang == 'en_utf8') {
989 return true; // Nothing to do
990 }
991
551fe0e5 992 upgrade_started(false);
993 print_heading(get_string('langimport', 'admin').': '.$lang);
db9d4a3d 994
995 @mkdir ($CFG->dataroot.'/temp/'); //make it in case it's a fresh install, it might not be there
996 @mkdir ($CFG->dataroot.'/lang/');
997
998 require_once($CFG->libdir.'/componentlib.class.php');
999
1000 if ($cd = new component_installer('http://download.moodle.org', 'lang16', $lang.'.zip', 'languages.md5', 'lang')) {
1001 $status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED)
1002
1003 if ($status == COMPONENT_INSTALLED) {
db9d4a3d 1004 @unlink($CFG->dataroot.'/cache/languages');
551fe0e5 1005 if ($parentlang = get_parent_language($lang)) {
1006 if ($cd = new component_installer('http://download.moodle.org', 'lang16', $parentlang.'.zip', 'languages.md5', 'lang')) {
1007 $cd->install();
1008 }
1009 }
1010 notify(get_string('success'), 'notifysuccess');
db9d4a3d 1011 }
1012 }
1013
551fe0e5 1014 print_upgrade_separator();
db9d4a3d 1015}
8580535b 1016
1017/**
1018 * Install core moodle tables and initialize
1019 * @param float $version target version
1020 * @param bool $verbose
1021 * @return void, may throw exception
1022 */
1023function install_core($version, $verbose) {
1024 global $CFG, $DB;
1025
1026 try {
c3d0e149 1027 set_time_limit(600);
194cdca8 1028 print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag
8580535b 1029
1030 $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml");
1031 upgrade_started(); // we want the flag to be stored in config table ;-)
1032
1033 // set all core default records and default settings
1034 require_once("$CFG->libdir/db/install.php");
1035 xmldb_main_install();
1036
1037 // store version
1038 upgrade_main_savepoint(true, $version, false);
1039
1040 // Continue with the instalation
1041 events_update_definition('moodle');
1042 message_update_providers('moodle');
1043 message_update_providers('message');
1044
1045 // Write default settings unconditionlly
1046 admin_apply_default_settings(NULL, true);
1047
194cdca8 1048 print_upgrade_part_end(null, true, $verbose);
8580535b 1049 } catch (exception $ex) {
1050 upgrade_handle_exception($ex);
1051 }
1052}
1053
1054/**
1055 * Upgrade moodle core
1056 * @param float $version target version
1057 * @param bool $verbose
1058 * @return void, may throw exception
1059 */
1060function upgrade_core($version, $verbose) {
1061 global $CFG;
1062
3316fe24 1063 require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades
3316fe24 1064
8580535b 1065 try {
1066 // Upgrade current language pack if we can
1067 if (empty($CFG->skiplangupgrade)) {
1068 upgrade_language_pack(false);
1069 }
1070
194cdca8 1071 print_upgrade_part_start('moodle', false, $verbose);
8580535b 1072
1073 $result = xmldb_main_upgrade($CFG->version);
1074 if ($version > $CFG->version) {
1075 // store version if not already there
1076 upgrade_main_savepoint($result, $version, false);
1077 }
1078
1079 // perform all other component upgrade routines
1080 update_capabilities('moodle');
1081 events_update_definition('moodle');
1082 message_update_providers('moodle');
1083 message_update_providers('message');
1084
1085 remove_dir($CFG->dataroot . '/cache', true); // flush cache
1086
194cdca8 1087 print_upgrade_part_end('moodle', false, $verbose);
8580535b 1088 } catch (Exception $ex) {
1089 upgrade_handle_exception($ex);
1090 }
1091}
1092
1093/**
1094 * Upgrade/install other parts of moodle
1095 * @param bool $verbose
1096 * @return void, may throw exception
1097 */
1098function upgrade_noncore($verbose) {
1099 global $CFG;
1100
8580535b 1101 // upgrade all plugins types
1102 try {
1103 $plugintypes = get_plugin_types();
1104 foreach ($plugintypes as $type=>$location) {
194cdca8 1105 upgrade_plugins($type, $location, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose);
8580535b 1106 }
1107 } catch (Exception $ex) {
1108 upgrade_handle_exception($ex);
1109 }
1110
1111 // Check for changes to RPC functions
1112 if ($CFG->mnet_dispatcher_mode != 'off') {
1113 try {
1114 // this needs a full rewrite, sorry to mention that :-(
1115 // we have to make it part of standard WS framework
1116 require_once("$CFG->dirroot/$CFG->admin/mnet/adminlib.php");
1117 upgrade_RPC_functions(); // Return here afterwards
1118 } catch (Exception $ex) {
1119 upgrade_handle_exception($ex);
1120 }
1121 }
1122
1123 // Check for local database customisations
1124 try {
1125 require_once("$CFG->dirroot/lib/locallib.php");
194cdca8 1126 upgrade_local_db('print_upgrade_part_start', 'print_upgrade_part_end', $verbose);
8580535b 1127 } catch (Exception $ex) {
1128 upgrade_handle_exception($ex);
1129 }
1130}
3316fe24 1131
1132/**
1133 * Checks if the main tables have been installed yet or not.
1134 * @return bool
1135 */
1136function core_tables_exist() {
1137 global $DB;
1138
1139 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
1140 return false;
1141
1142 } else { // Check for missing main tables
1143 $mtables = array('config', 'course', 'groupings'); // some tables used in 1.9 and 2.0, preferable something from the start and end of install.xml
1144 foreach ($mtables as $mtable) {
1145 if (!in_array($mtable, $tables)) {
1146 return false;
1147 }
1148 }
1149 return true;
1150 }
ba04999c 1151}