[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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/>. 17 18 /** 19 * Various upgrade/install related functions and classes. 20 * 21 * @package core 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 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** UPGRADE_LOG_NORMAL = 0 */ 30 define('UPGRADE_LOG_NORMAL', 0); 31 /** UPGRADE_LOG_NOTICE = 1 */ 32 define('UPGRADE_LOG_NOTICE', 1); 33 /** UPGRADE_LOG_ERROR = 2 */ 34 define('UPGRADE_LOG_ERROR', 2); 35 36 /** 37 * Exception indicating unknown error during upgrade. 38 * 39 * @package core 40 * @subpackage upgrade 41 * @copyright 2009 Petr Skoda {@link http://skodak.org} 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class upgrade_exception extends moodle_exception { 45 function __construct($plugin, $version, $debuginfo=NULL) { 46 global $CFG; 47 $a = (object)array('plugin'=>$plugin, 'version'=>$version); 48 parent::__construct('upgradeerror', 'admin', "$CFG->wwwroot/$CFG->admin/index.php", $a, $debuginfo); 49 } 50 } 51 52 /** 53 * Exception indicating downgrade error during upgrade. 54 * 55 * @package core 56 * @subpackage upgrade 57 * @copyright 2009 Petr Skoda {@link http://skodak.org} 58 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 59 */ 60 class downgrade_exception extends moodle_exception { 61 function __construct($plugin, $oldversion, $newversion) { 62 global $CFG; 63 $plugin = is_null($plugin) ? 'moodle' : $plugin; 64 $a = (object)array('plugin'=>$plugin, 'oldversion'=>$oldversion, 'newversion'=>$newversion); 65 parent::__construct('cannotdowngrade', 'debug', "$CFG->wwwroot/$CFG->admin/index.php", $a); 66 } 67 } 68 69 /** 70 * @package core 71 * @subpackage upgrade 72 * @copyright 2009 Petr Skoda {@link http://skodak.org} 73 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 74 */ 75 class upgrade_requires_exception extends moodle_exception { 76 function __construct($plugin, $pluginversion, $currentmoodle, $requiremoodle) { 77 global $CFG; 78 $a = new stdClass(); 79 $a->pluginname = $plugin; 80 $a->pluginversion = $pluginversion; 81 $a->currentmoodle = $currentmoodle; 82 $a->requiremoodle = $requiremoodle; 83 parent::__construct('pluginrequirementsnotmet', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a); 84 } 85 } 86 87 /** 88 * @package core 89 * @subpackage upgrade 90 * @copyright 2009 Petr Skoda {@link http://skodak.org} 91 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 92 */ 93 class plugin_defective_exception extends moodle_exception { 94 function __construct($plugin, $details) { 95 global $CFG; 96 parent::__construct('detectedbrokenplugin', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $plugin, $details); 97 } 98 } 99 100 /** 101 * Misplaced plugin exception. 102 * 103 * Note: this should be used only from the upgrade/admin code. 104 * 105 * @package core 106 * @subpackage upgrade 107 * @copyright 2009 Petr Skoda {@link http://skodak.org} 108 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 109 */ 110 class plugin_misplaced_exception extends moodle_exception { 111 /** 112 * Constructor. 113 * @param string $component the component from version.php 114 * @param string $expected expected directory, null means calculate 115 * @param string $current plugin directory path 116 */ 117 public function __construct($component, $expected, $current) { 118 global $CFG; 119 if (empty($expected)) { 120 list($type, $plugin) = core_component::normalize_component($component); 121 $plugintypes = core_component::get_plugin_types(); 122 if (isset($plugintypes[$type])) { 123 $expected = $plugintypes[$type] . '/' . $plugin; 124 } 125 } 126 if (strpos($expected, '$CFG->dirroot') !== 0) { 127 $expected = str_replace($CFG->dirroot, '$CFG->dirroot', $expected); 128 } 129 if (strpos($current, '$CFG->dirroot') !== 0) { 130 $current = str_replace($CFG->dirroot, '$CFG->dirroot', $current); 131 } 132 $a = new stdClass(); 133 $a->component = $component; 134 $a->expected = $expected; 135 $a->current = $current; 136 parent::__construct('detectedmisplacedplugin', 'core_plugin', "$CFG->wwwroot/$CFG->admin/index.php", $a); 137 } 138 } 139 140 /** 141 * Sets maximum expected time needed for upgrade task. 142 * Please always make sure that upgrade will not run longer! 143 * 144 * The script may be automatically aborted if upgrade times out. 145 * 146 * @category upgrade 147 * @param int $max_execution_time in seconds (can not be less than 60 s) 148 */ 149 function upgrade_set_timeout($max_execution_time=300) { 150 global $CFG; 151 152 if (!isset($CFG->upgraderunning) or $CFG->upgraderunning < time()) { 153 $upgraderunning = get_config(null, 'upgraderunning'); 154 } else { 155 $upgraderunning = $CFG->upgraderunning; 156 } 157 158 if (!$upgraderunning) { 159 if (CLI_SCRIPT) { 160 // never stop CLI upgrades 161 $upgraderunning = 0; 162 } else { 163 // web upgrade not running or aborted 164 print_error('upgradetimedout', 'admin', "$CFG->wwwroot/$CFG->admin/"); 165 } 166 } 167 168 if ($max_execution_time < 60) { 169 // protection against 0 here 170 $max_execution_time = 60; 171 } 172 173 $expected_end = time() + $max_execution_time; 174 175 if ($expected_end < $upgraderunning + 10 and $expected_end > $upgraderunning - 10) { 176 // no need to store new end, it is nearly the same ;-) 177 return; 178 } 179 180 if (CLI_SCRIPT) { 181 // there is no point in timing out of CLI scripts, admins can stop them if necessary 182 core_php_time_limit::raise(); 183 } else { 184 core_php_time_limit::raise($max_execution_time); 185 } 186 set_config('upgraderunning', $expected_end); // keep upgrade locked until this time 187 } 188 189 /** 190 * Upgrade savepoint, marks end of each upgrade block. 191 * It stores new main version, resets upgrade timeout 192 * and abort upgrade if user cancels page loading. 193 * 194 * Please do not make large upgrade blocks with lots of operations, 195 * for example when adding tables keep only one table operation per block. 196 * 197 * @category upgrade 198 * @param bool $result false if upgrade step failed, true if completed 199 * @param string or float $version main version 200 * @param bool $allowabort allow user to abort script execution here 201 * @return void 202 */ 203 function upgrade_main_savepoint($result, $version, $allowabort=true) { 204 global $CFG; 205 206 //sanity check to avoid confusion with upgrade_mod_savepoint usage. 207 if (!is_bool($allowabort)) { 208 $errormessage = 'Parameter type mismatch. Are you mixing up upgrade_main_savepoint() and upgrade_mod_savepoint()?'; 209 throw new coding_exception($errormessage); 210 } 211 212 if (!$result) { 213 throw new upgrade_exception(null, $version); 214 } 215 216 if ($CFG->version >= $version) { 217 // something really wrong is going on in main upgrade script 218 throw new downgrade_exception(null, $CFG->version, $version); 219 } 220 221 set_config('version', $version); 222 upgrade_log(UPGRADE_LOG_NORMAL, null, 'Upgrade savepoint reached'); 223 224 // reset upgrade timeout to default 225 upgrade_set_timeout(); 226 227 // this is a safe place to stop upgrades if user aborts page loading 228 if ($allowabort and connection_aborted()) { 229 die; 230 } 231 } 232 233 /** 234 * Module upgrade savepoint, marks end of module upgrade blocks 235 * It stores module version, resets upgrade timeout 236 * and abort upgrade if user cancels page loading. 237 * 238 * @category upgrade 239 * @param bool $result false if upgrade step failed, true if completed 240 * @param string or float $version main version 241 * @param string $modname name of module 242 * @param bool $allowabort allow user to abort script execution here 243 * @return void 244 */ 245 function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { 246 global $DB; 247 248 $component = 'mod_'.$modname; 249 250 if (!$result) { 251 throw new upgrade_exception($component, $version); 252 } 253 254 $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); 255 256 if (!$module = $DB->get_record('modules', array('name'=>$modname))) { 257 print_error('modulenotexist', 'debug', '', $modname); 258 } 259 260 if ($dbversion >= $version) { 261 // something really wrong is going on in upgrade script 262 throw new downgrade_exception($component, $dbversion, $version); 263 } 264 set_config('version', $version, $component); 265 266 upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); 267 268 // reset upgrade timeout to default 269 upgrade_set_timeout(); 270 271 // this is a safe place to stop upgrades if user aborts page loading 272 if ($allowabort and connection_aborted()) { 273 die; 274 } 275 } 276 277 /** 278 * Blocks upgrade savepoint, marks end of blocks upgrade blocks 279 * It stores block version, resets upgrade timeout 280 * and abort upgrade if user cancels page loading. 281 * 282 * @category upgrade 283 * @param bool $result false if upgrade step failed, true if completed 284 * @param string or float $version main version 285 * @param string $blockname name of block 286 * @param bool $allowabort allow user to abort script execution here 287 * @return void 288 */ 289 function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) { 290 global $DB; 291 292 $component = 'block_'.$blockname; 293 294 if (!$result) { 295 throw new upgrade_exception($component, $version); 296 } 297 298 $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); 299 300 if (!$block = $DB->get_record('block', array('name'=>$blockname))) { 301 print_error('blocknotexist', 'debug', '', $blockname); 302 } 303 304 if ($dbversion >= $version) { 305 // something really wrong is going on in upgrade script 306 throw new downgrade_exception($component, $dbversion, $version); 307 } 308 set_config('version', $version, $component); 309 310 upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); 311 312 // reset upgrade timeout to default 313 upgrade_set_timeout(); 314 315 // this is a safe place to stop upgrades if user aborts page loading 316 if ($allowabort and connection_aborted()) { 317 die; 318 } 319 } 320 321 /** 322 * Plugins upgrade savepoint, marks end of blocks upgrade blocks 323 * It stores plugin version, resets upgrade timeout 324 * and abort upgrade if user cancels page loading. 325 * 326 * @category upgrade 327 * @param bool $result false if upgrade step failed, true if completed 328 * @param string or float $version main version 329 * @param string $type name of plugin 330 * @param string $dir location of plugin 331 * @param bool $allowabort allow user to abort script execution here 332 * @return void 333 */ 334 function upgrade_plugin_savepoint($result, $version, $type, $plugin, $allowabort=true) { 335 global $DB; 336 337 $component = $type.'_'.$plugin; 338 339 if (!$result) { 340 throw new upgrade_exception($component, $version); 341 } 342 343 $dbversion = $DB->get_field('config_plugins', 'value', array('plugin'=>$component, 'name'=>'version')); 344 345 if ($dbversion >= $version) { 346 // Something really wrong is going on in the upgrade script 347 throw new downgrade_exception($component, $dbversion, $version); 348 } 349 set_config('version', $version, $component); 350 upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); 351 352 // Reset upgrade timeout to default 353 upgrade_set_timeout(); 354 355 // This is a safe place to stop upgrades if user aborts page loading 356 if ($allowabort and connection_aborted()) { 357 die; 358 } 359 } 360 361 /** 362 * Detect if there are leftovers in PHP source files. 363 * 364 * During main version upgrades administrators MUST move away 365 * old PHP source files and start from scratch (or better 366 * use git). 367 * 368 * @return bool true means borked upgrade, false means previous PHP files were properly removed 369 */ 370 function upgrade_stale_php_files_present() { 371 global $CFG; 372 373 $someexamplesofremovedfiles = array( 374 // Removed in 3.1. 375 '/lib/classes/log/sql_internal_reader.php', 376 '/lib/zend/', 377 '/mod/forum/pix/icon.gif', 378 '/tag/templates/tagname.mustache', 379 // Removed in 3.0. 380 '/mod/lti/grade.php', 381 '/tag/coursetagslib.php', 382 // Removed in 2.9. 383 '/lib/timezone.txt', 384 // Removed in 2.8. 385 '/course/delete_category_form.php', 386 // Removed in 2.7. 387 '/admin/tool/qeupgradehelper/version.php', 388 // Removed in 2.6. 389 '/admin/block.php', 390 '/admin/oacleanup.php', 391 // Removed in 2.5. 392 '/backup/lib.php', 393 '/backup/bb/README.txt', 394 '/lib/excel/test.php', 395 // Removed in 2.4. 396 '/admin/tool/unittest/simpletestlib.php', 397 // Removed in 2.3. 398 '/lib/minify/builder/', 399 // Removed in 2.2. 400 '/lib/yui/3.4.1pr1/', 401 // Removed in 2.2. 402 '/search/cron_php5.php', 403 '/course/report/log/indexlive.php', 404 '/admin/report/backups/index.php', 405 '/admin/generator.php', 406 // Removed in 2.1. 407 '/lib/yui/2.8.0r4/', 408 // Removed in 2.0. 409 '/blocks/admin/block_admin.php', 410 '/blocks/admin_tree/block_admin_tree.php', 411 ); 412 413 foreach ($someexamplesofremovedfiles as $file) { 414 if (file_exists($CFG->dirroot.$file)) { 415 return true; 416 } 417 } 418 419 return false; 420 } 421 422 /** 423 * Upgrade plugins 424 * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype') 425 * return void 426 */ 427 function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { 428 global $CFG, $DB; 429 430 /// special cases 431 if ($type === 'mod') { 432 return upgrade_plugins_modules($startcallback, $endcallback, $verbose); 433 } else if ($type === 'block') { 434 return upgrade_plugins_blocks($startcallback, $endcallback, $verbose); 435 } 436 437 $plugs = core_component::get_plugin_list($type); 438 439 foreach ($plugs as $plug=>$fullplug) { 440 // Reset time so that it works when installing a large number of plugins 441 core_php_time_limit::raise(600); 442 $component = clean_param($type.'_'.$plug, PARAM_COMPONENT); // standardised plugin name 443 444 // check plugin dir is valid name 445 if (empty($component)) { 446 throw new plugin_defective_exception($type.'_'.$plug, 'Invalid plugin directory name.'); 447 } 448 449 if (!is_readable($fullplug.'/version.php')) { 450 continue; 451 } 452 453 $plugin = new stdClass(); 454 $plugin->version = null; 455 $module = $plugin; // Prevent some notices when plugin placed in wrong directory. 456 require ($fullplug.'/version.php'); // defines $plugin with version etc 457 unset($module); 458 459 if (empty($plugin->version)) { 460 throw new plugin_defective_exception($component, 'Missing $plugin->version number in version.php.'); 461 } 462 463 if (empty($plugin->component)) { 464 throw new plugin_defective_exception($component, 'Missing $plugin->component declaration in version.php.'); 465 } 466 467 if ($plugin->component !== $component) { 468 throw new plugin_misplaced_exception($plugin->component, null, $fullplug); 469 } 470 471 $plugin->name = $plug; 472 $plugin->fullname = $component; 473 474 if (!empty($plugin->requires)) { 475 if ($plugin->requires > $CFG->version) { 476 throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); 477 } else if ($plugin->requires < 2010000000) { 478 throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); 479 } 480 } 481 482 // try to recover from interrupted install.php if needed 483 if (file_exists($fullplug.'/db/install.php')) { 484 if (get_config($plugin->fullname, 'installrunning')) { 485 require_once ($fullplug.'/db/install.php'); 486 $recover_install_function = 'xmldb_'.$plugin->fullname.'_install_recovery'; 487 if (function_exists($recover_install_function)) { 488 $startcallback($component, true, $verbose); 489 $recover_install_function(); 490 unset_config('installrunning', $plugin->fullname); 491 update_capabilities($component); 492 log_update_descriptions($component); 493 external_update_descriptions($component); 494 events_update_definition($component); 495 \core\task\manager::reset_scheduled_tasks_for_component($component); 496 message_update_providers($component); 497 \core\message\inbound\manager::update_handlers_for_component($component); 498 if ($type === 'message') { 499 message_update_processors($plug); 500 } 501 upgrade_plugin_mnet_functions($component); 502 core_tag_area::reset_definitions_for_component($component); 503 $endcallback($component, true, $verbose); 504 } 505 } 506 } 507 508 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 509 if (empty($installedversion)) { // new installation 510 $startcallback($component, true, $verbose); 511 512 /// Install tables if defined 513 if (file_exists($fullplug.'/db/install.xml')) { 514 $DB->get_manager()->install_from_xmldb_file($fullplug.'/db/install.xml'); 515 } 516 517 /// store version 518 upgrade_plugin_savepoint(true, $plugin->version, $type, $plug, false); 519 520 /// execute post install file 521 if (file_exists($fullplug.'/db/install.php')) { 522 require_once ($fullplug.'/db/install.php'); 523 set_config('installrunning', 1, $plugin->fullname); 524 $post_install_function = 'xmldb_'.$plugin->fullname.'_install'; 525 $post_install_function(); 526 unset_config('installrunning', $plugin->fullname); 527 } 528 529 /// Install various components 530 update_capabilities($component); 531 log_update_descriptions($component); 532 external_update_descriptions($component); 533 events_update_definition($component); 534 \core\task\manager::reset_scheduled_tasks_for_component($component); 535 message_update_providers($component); 536 \core\message\inbound\manager::update_handlers_for_component($component); 537 if ($type === 'message') { 538 message_update_processors($plug); 539 } 540 upgrade_plugin_mnet_functions($component); 541 core_tag_area::reset_definitions_for_component($component); 542 $endcallback($component, true, $verbose); 543 544 } else if ($installedversion < $plugin->version) { // upgrade 545 /// Run the upgrade function for the plugin. 546 $startcallback($component, false, $verbose); 547 548 if (is_readable($fullplug.'/db/upgrade.php')) { 549 require_once ($fullplug.'/db/upgrade.php'); // defines upgrading function 550 551 $newupgrade_function = 'xmldb_'.$plugin->fullname.'_upgrade'; 552 $result = $newupgrade_function($installedversion); 553 } else { 554 $result = true; 555 } 556 557 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 558 if ($installedversion < $plugin->version) { 559 // store version if not already there 560 upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false); 561 } 562 563 /// Upgrade various components 564 update_capabilities($component); 565 log_update_descriptions($component); 566 external_update_descriptions($component); 567 events_update_definition($component); 568 \core\task\manager::reset_scheduled_tasks_for_component($component); 569 message_update_providers($component); 570 \core\message\inbound\manager::update_handlers_for_component($component); 571 if ($type === 'message') { 572 // Ugly hack! 573 message_update_processors($plug); 574 } 575 upgrade_plugin_mnet_functions($component); 576 core_tag_area::reset_definitions_for_component($component); 577 $endcallback($component, false, $verbose); 578 579 } else if ($installedversion > $plugin->version) { 580 throw new downgrade_exception($component, $installedversion, $plugin->version); 581 } 582 } 583 } 584 585 /** 586 * Find and check all modules and load them up or upgrade them if necessary 587 * 588 * @global object 589 * @global object 590 */ 591 function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { 592 global $CFG, $DB; 593 594 $mods = core_component::get_plugin_list('mod'); 595 596 foreach ($mods as $mod=>$fullmod) { 597 598 if ($mod === 'NEWMODULE') { // Someone has unzipped the template, ignore it 599 continue; 600 } 601 602 $component = clean_param('mod_'.$mod, PARAM_COMPONENT); 603 604 // check module dir is valid name 605 if (empty($component)) { 606 throw new plugin_defective_exception('mod_'.$mod, 'Invalid plugin directory name.'); 607 } 608 609 if (!is_readable($fullmod.'/version.php')) { 610 throw new plugin_defective_exception($component, 'Missing version.php'); 611 } 612 613 $module = new stdClass(); 614 $plugin = new stdClass(); 615 $plugin->version = null; 616 require ($fullmod .'/version.php'); // Defines $plugin with version etc. 617 618 // Check if the legacy $module syntax is still used. 619 if (!is_object($module) or (count((array)$module) > 0)) { 620 throw new plugin_defective_exception($component, 'Unsupported $module syntax detected in version.php'); 621 } 622 623 // Prepare the record for the {modules} table. 624 $module = clone($plugin); 625 unset($module->version); 626 unset($module->component); 627 unset($module->dependencies); 628 unset($module->release); 629 630 if (empty($plugin->version)) { 631 throw new plugin_defective_exception($component, 'Missing $plugin->version number in version.php.'); 632 } 633 634 if (empty($plugin->component)) { 635 throw new plugin_defective_exception($component, 'Missing $plugin->component declaration in version.php.'); 636 } 637 638 if ($plugin->component !== $component) { 639 throw new plugin_misplaced_exception($plugin->component, null, $fullmod); 640 } 641 642 if (!empty($plugin->requires)) { 643 if ($plugin->requires > $CFG->version) { 644 throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); 645 } else if ($plugin->requires < 2010000000) { 646 throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); 647 } 648 } 649 650 if (empty($module->cron)) { 651 $module->cron = 0; 652 } 653 654 // all modules must have en lang pack 655 if (!is_readable("$fullmod/lang/en/$mod.php")) { 656 throw new plugin_defective_exception($component, 'Missing mandatory en language pack.'); 657 } 658 659 $module->name = $mod; // The name MUST match the directory 660 661 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 662 663 if (file_exists($fullmod.'/db/install.php')) { 664 if (get_config($module->name, 'installrunning')) { 665 require_once ($fullmod.'/db/install.php'); 666 $recover_install_function = 'xmldb_'.$module->name.'_install_recovery'; 667 if (function_exists($recover_install_function)) { 668 $startcallback($component, true, $verbose); 669 $recover_install_function(); 670 unset_config('installrunning', $module->name); 671 // Install various components too 672 update_capabilities($component); 673 log_update_descriptions($component); 674 external_update_descriptions($component); 675 events_update_definition($component); 676 \core\task\manager::reset_scheduled_tasks_for_component($component); 677 message_update_providers($component); 678 \core\message\inbound\manager::update_handlers_for_component($component); 679 upgrade_plugin_mnet_functions($component); 680 core_tag_area::reset_definitions_for_component($component); 681 $endcallback($component, true, $verbose); 682 } 683 } 684 } 685 686 if (empty($installedversion)) { 687 $startcallback($component, true, $verbose); 688 689 /// Execute install.xml (XMLDB) - must be present in all modules 690 $DB->get_manager()->install_from_xmldb_file($fullmod.'/db/install.xml'); 691 692 /// Add record into modules table - may be needed in install.php already 693 $module->id = $DB->insert_record('modules', $module); 694 upgrade_mod_savepoint(true, $plugin->version, $module->name, false); 695 696 /// Post installation hook - optional 697 if (file_exists("$fullmod/db/install.php")) { 698 require_once("$fullmod/db/install.php"); 699 // Set installation running flag, we need to recover after exception or error 700 set_config('installrunning', 1, $module->name); 701 $post_install_function = 'xmldb_'.$module->name.'_install'; 702 $post_install_function(); 703 unset_config('installrunning', $module->name); 704 } 705 706 /// Install various components 707 update_capabilities($component); 708 log_update_descriptions($component); 709 external_update_descriptions($component); 710 events_update_definition($component); 711 \core\task\manager::reset_scheduled_tasks_for_component($component); 712 message_update_providers($component); 713 \core\message\inbound\manager::update_handlers_for_component($component); 714 upgrade_plugin_mnet_functions($component); 715 core_tag_area::reset_definitions_for_component($component); 716 717 $endcallback($component, true, $verbose); 718 719 } else if ($installedversion < $plugin->version) { 720 /// If versions say that we need to upgrade but no upgrade files are available, notify and continue 721 $startcallback($component, false, $verbose); 722 723 if (is_readable($fullmod.'/db/upgrade.php')) { 724 require_once ($fullmod.'/db/upgrade.php'); // defines new upgrading function 725 $newupgrade_function = 'xmldb_'.$module->name.'_upgrade'; 726 $result = $newupgrade_function($installedversion, $module); 727 } else { 728 $result = true; 729 } 730 731 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 732 $currmodule = $DB->get_record('modules', array('name'=>$module->name)); 733 if ($installedversion < $plugin->version) { 734 // store version if not already there 735 upgrade_mod_savepoint($result, $plugin->version, $mod, false); 736 } 737 738 // update cron flag if needed 739 if ($currmodule->cron != $module->cron) { 740 $DB->set_field('modules', 'cron', $module->cron, array('name' => $module->name)); 741 } 742 743 // Upgrade various components 744 update_capabilities($component); 745 log_update_descriptions($component); 746 external_update_descriptions($component); 747 events_update_definition($component); 748 \core\task\manager::reset_scheduled_tasks_for_component($component); 749 message_update_providers($component); 750 \core\message\inbound\manager::update_handlers_for_component($component); 751 upgrade_plugin_mnet_functions($component); 752 core_tag_area::reset_definitions_for_component($component); 753 754 $endcallback($component, false, $verbose); 755 756 } else if ($installedversion > $plugin->version) { 757 throw new downgrade_exception($component, $installedversion, $plugin->version); 758 } 759 } 760 } 761 762 763 /** 764 * This function finds all available blocks and install them 765 * into blocks table or do all the upgrade process if newer. 766 * 767 * @global object 768 * @global object 769 */ 770 function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { 771 global $CFG, $DB; 772 773 require_once($CFG->dirroot.'/blocks/moodleblock.class.php'); 774 775 $blocktitles = array(); // we do not want duplicate titles 776 777 //Is this a first install 778 $first_install = null; 779 780 $blocks = core_component::get_plugin_list('block'); 781 782 foreach ($blocks as $blockname=>$fullblock) { 783 784 if (is_null($first_install)) { 785 $first_install = ($DB->count_records('block_instances') == 0); 786 } 787 788 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it 789 continue; 790 } 791 792 $component = clean_param('block_'.$blockname, PARAM_COMPONENT); 793 794 // check block dir is valid name 795 if (empty($component)) { 796 throw new plugin_defective_exception('block_'.$blockname, 'Invalid plugin directory name.'); 797 } 798 799 if (!is_readable($fullblock.'/version.php')) { 800 throw new plugin_defective_exception('block/'.$blockname, 'Missing version.php file.'); 801 } 802 $plugin = new stdClass(); 803 $plugin->version = null; 804 $plugin->cron = 0; 805 $module = $plugin; // Prevent some notices when module placed in wrong directory. 806 include ($fullblock.'/version.php'); 807 unset($module); 808 $block = clone($plugin); 809 unset($block->version); 810 unset($block->component); 811 unset($block->dependencies); 812 unset($block->release); 813 814 if (empty($plugin->version)) { 815 throw new plugin_defective_exception($component, 'Missing block version number in version.php.'); 816 } 817 818 if (empty($plugin->component)) { 819 throw new plugin_defective_exception($component, 'Missing $plugin->component declaration in version.php.'); 820 } 821 822 if ($plugin->component !== $component) { 823 throw new plugin_misplaced_exception($plugin->component, null, $fullblock); 824 } 825 826 if (!empty($plugin->requires)) { 827 if ($plugin->requires > $CFG->version) { 828 throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); 829 } else if ($plugin->requires < 2010000000) { 830 throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); 831 } 832 } 833 834 if (!is_readable($fullblock.'/block_'.$blockname.'.php')) { 835 throw new plugin_defective_exception('block/'.$blockname, 'Missing main block class file.'); 836 } 837 include_once($fullblock.'/block_'.$blockname.'.php'); 838 839 $classname = 'block_'.$blockname; 840 841 if (!class_exists($classname)) { 842 throw new plugin_defective_exception($component, 'Can not load main class.'); 843 } 844 845 $blockobj = new $classname; // This is what we'll be testing 846 $blocktitle = $blockobj->get_title(); 847 848 // OK, it's as we all hoped. For further tests, the object will do them itself. 849 if (!$blockobj->_self_test()) { 850 throw new plugin_defective_exception($component, 'Self test failed.'); 851 } 852 853 $block->name = $blockname; // The name MUST match the directory 854 855 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 856 857 if (file_exists($fullblock.'/db/install.php')) { 858 if (get_config('block_'.$blockname, 'installrunning')) { 859 require_once ($fullblock.'/db/install.php'); 860 $recover_install_function = 'xmldb_block_'.$blockname.'_install_recovery'; 861 if (function_exists($recover_install_function)) { 862 $startcallback($component, true, $verbose); 863 $recover_install_function(); 864 unset_config('installrunning', 'block_'.$blockname); 865 // Install various components 866 update_capabilities($component); 867 log_update_descriptions($component); 868 external_update_descriptions($component); 869 events_update_definition($component); 870 \core\task\manager::reset_scheduled_tasks_for_component($component); 871 message_update_providers($component); 872 \core\message\inbound\manager::update_handlers_for_component($component); 873 upgrade_plugin_mnet_functions($component); 874 core_tag_area::reset_definitions_for_component($component); 875 $endcallback($component, true, $verbose); 876 } 877 } 878 } 879 880 if (empty($installedversion)) { // block not installed yet, so install it 881 $conflictblock = array_search($blocktitle, $blocktitles); 882 if ($conflictblock !== false) { 883 // Duplicate block titles are not allowed, they confuse people 884 // AND PHP's associative arrays ;) 885 throw new plugin_defective_exception($component, get_string('blocknameconflict', 'error', (object)array('name'=>$block->name, 'conflict'=>$conflictblock))); 886 } 887 $startcallback($component, true, $verbose); 888 889 if (file_exists($fullblock.'/db/install.xml')) { 890 $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml'); 891 } 892 $block->id = $DB->insert_record('block', $block); 893 upgrade_block_savepoint(true, $plugin->version, $block->name, false); 894 895 if (file_exists($fullblock.'/db/install.php')) { 896 require_once ($fullblock.'/db/install.php'); 897 // Set installation running flag, we need to recover after exception or error 898 set_config('installrunning', 1, 'block_'.$blockname); 899 $post_install_function = 'xmldb_block_'.$blockname.'_install'; 900 $post_install_function(); 901 unset_config('installrunning', 'block_'.$blockname); 902 } 903 904 $blocktitles[$block->name] = $blocktitle; 905 906 // Install various components 907 update_capabilities($component); 908 log_update_descriptions($component); 909 external_update_descriptions($component); 910 events_update_definition($component); 911 \core\task\manager::reset_scheduled_tasks_for_component($component); 912 message_update_providers($component); 913 \core\message\inbound\manager::update_handlers_for_component($component); 914 core_tag_area::reset_definitions_for_component($component); 915 upgrade_plugin_mnet_functions($component); 916 917 $endcallback($component, true, $verbose); 918 919 } else if ($installedversion < $plugin->version) { 920 $startcallback($component, false, $verbose); 921 922 if (is_readable($fullblock.'/db/upgrade.php')) { 923 require_once ($fullblock.'/db/upgrade.php'); // defines new upgrading function 924 $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade'; 925 $result = $newupgrade_function($installedversion, $block); 926 } else { 927 $result = true; 928 } 929 930 $installedversion = $DB->get_field('config_plugins', 'value', array('name'=>'version', 'plugin'=>$component)); // No caching! 931 $currblock = $DB->get_record('block', array('name'=>$block->name)); 932 if ($installedversion < $plugin->version) { 933 // store version if not already there 934 upgrade_block_savepoint($result, $plugin->version, $block->name, false); 935 } 936 937 if ($currblock->cron != $block->cron) { 938 // update cron flag if needed 939 $DB->set_field('block', 'cron', $block->cron, array('id' => $currblock->id)); 940 } 941 942 // Upgrade various components 943 update_capabilities($component); 944 log_update_descriptions($component); 945 external_update_descriptions($component); 946 events_update_definition($component); 947 \core\task\manager::reset_scheduled_tasks_for_component($component); 948 message_update_providers($component); 949 \core\message\inbound\manager::update_handlers_for_component($component); 950 upgrade_plugin_mnet_functions($component); 951 core_tag_area::reset_definitions_for_component($component); 952 953 $endcallback($component, false, $verbose); 954 955 } else if ($installedversion > $plugin->version) { 956 throw new downgrade_exception($component, $installedversion, $plugin->version); 957 } 958 } 959 960 961 // Finally, if we are in the first_install of BLOCKS setup frontpage and admin page blocks 962 if ($first_install) { 963 //Iterate over each course - there should be only site course here now 964 if ($courses = $DB->get_records('course')) { 965 foreach ($courses as $course) { 966 blocks_add_default_course_blocks($course); 967 } 968 } 969 970 blocks_add_default_system_blocks(); 971 } 972 } 973 974 975 /** 976 * Log_display description function used during install and upgrade. 977 * 978 * @param string $component name of component (moodle, mod_assignment, etc.) 979 * @return void 980 */ 981 function log_update_descriptions($component) { 982 global $DB; 983 984 $defpath = core_component::get_component_directory($component).'/db/log.php'; 985 986 if (!file_exists($defpath)) { 987 $DB->delete_records('log_display', array('component'=>$component)); 988 return; 989 } 990 991 // load new info 992 $logs = array(); 993 include($defpath); 994 $newlogs = array(); 995 foreach ($logs as $log) { 996 $newlogs[$log['module'].'-'.$log['action']] = $log; // kind of unique name 997 } 998 unset($logs); 999 $logs = $newlogs; 1000 1001 $fields = array('module', 'action', 'mtable', 'field'); 1002 // update all log fist 1003 $dblogs = $DB->get_records('log_display', array('component'=>$component)); 1004 foreach ($dblogs as $dblog) { 1005 $name = $dblog->module.'-'.$dblog->action; 1006 1007 if (empty($logs[$name])) { 1008 $DB->delete_records('log_display', array('id'=>$dblog->id)); 1009 continue; 1010 } 1011 1012 $log = $logs[$name]; 1013 unset($logs[$name]); 1014 1015 $update = false; 1016 foreach ($fields as $field) { 1017 if ($dblog->$field != $log[$field]) { 1018 $dblog->$field = $log[$field]; 1019 $update = true; 1020 } 1021 } 1022 if ($update) { 1023 $DB->update_record('log_display', $dblog); 1024 } 1025 } 1026 foreach ($logs as $log) { 1027 $dblog = (object)$log; 1028 $dblog->component = $component; 1029 $DB->insert_record('log_display', $dblog); 1030 } 1031 } 1032 1033 /** 1034 * Web service discovery function used during install and upgrade. 1035 * @param string $component name of component (moodle, mod_assignment, etc.) 1036 * @return void 1037 */ 1038 function external_update_descriptions($component) { 1039 global $DB, $CFG; 1040 1041 $defpath = core_component::get_component_directory($component).'/db/services.php'; 1042 1043 if (!file_exists($defpath)) { 1044 require_once($CFG->dirroot.'/lib/externallib.php'); 1045 external_delete_descriptions($component); 1046 return; 1047 } 1048 1049 // load new info 1050 $functions = array(); 1051 $services = array(); 1052 include($defpath); 1053 1054 // update all function fist 1055 $dbfunctions = $DB->get_records('external_functions', array('component'=>$component)); 1056 foreach ($dbfunctions as $dbfunction) { 1057 if (empty($functions[$dbfunction->name])) { 1058 $DB->delete_records('external_functions', array('id'=>$dbfunction->id)); 1059 // do not delete functions from external_services_functions, beacuse 1060 // we want to notify admins when functions used in custom services disappear 1061 1062 //TODO: this looks wrong, we have to delete it eventually (skodak) 1063 continue; 1064 } 1065 1066 $function = $functions[$dbfunction->name]; 1067 unset($functions[$dbfunction->name]); 1068 $function['classpath'] = empty($function['classpath']) ? null : $function['classpath']; 1069 1070 $update = false; 1071 if ($dbfunction->classname != $function['classname']) { 1072 $dbfunction->classname = $function['classname']; 1073 $update = true; 1074 } 1075 if ($dbfunction->methodname != $function['methodname']) { 1076 $dbfunction->methodname = $function['methodname']; 1077 $update = true; 1078 } 1079 if ($dbfunction->classpath != $function['classpath']) { 1080 $dbfunction->classpath = $function['classpath']; 1081 $update = true; 1082 } 1083 $functioncapabilities = array_key_exists('capabilities', $function)?$function['capabilities']:''; 1084 if ($dbfunction->capabilities != $functioncapabilities) { 1085 $dbfunction->capabilities = $functioncapabilities; 1086 $update = true; 1087 } 1088 1089 if (isset($function['services']) and is_array($function['services'])) { 1090 sort($function['services']); 1091 $functionservices = implode(',', $function['services']); 1092 } else { 1093 // Force null values in the DB. 1094 $functionservices = null; 1095 } 1096 1097 if ($dbfunction->services != $functionservices) { 1098 // Now, we need to check if services were removed, in that case we need to remove the function from them. 1099 $servicesremoved = array_diff(explode(",", $dbfunction->services), explode(",", $functionservices)); 1100 foreach ($servicesremoved as $removedshortname) { 1101 if ($externalserviceid = $DB->get_field('external_services', 'id', array("shortname" => $removedshortname))) { 1102 $DB->delete_records('external_services_functions', array('functionname' => $dbfunction->name, 1103 'externalserviceid' => $externalserviceid)); 1104 } 1105 } 1106 1107 $dbfunction->services = $functionservices; 1108 $update = true; 1109 } 1110 if ($update) { 1111 $DB->update_record('external_functions', $dbfunction); 1112 } 1113 } 1114 foreach ($functions as $fname => $function) { 1115 $dbfunction = new stdClass(); 1116 $dbfunction->name = $fname; 1117 $dbfunction->classname = $function['classname']; 1118 $dbfunction->methodname = $function['methodname']; 1119 $dbfunction->classpath = empty($function['classpath']) ? null : $function['classpath']; 1120 $dbfunction->component = $component; 1121 $dbfunction->capabilities = array_key_exists('capabilities', $function)?$function['capabilities']:''; 1122 1123 if (isset($function['services']) and is_array($function['services'])) { 1124 sort($function['services']); 1125 $dbfunction->services = implode(',', $function['services']); 1126 } else { 1127 // Force null values in the DB. 1128 $dbfunction->services = null; 1129 } 1130 1131 $dbfunction->id = $DB->insert_record('external_functions', $dbfunction); 1132 } 1133 unset($functions); 1134 1135 // now deal with services 1136 $dbservices = $DB->get_records('external_services', array('component'=>$component)); 1137 foreach ($dbservices as $dbservice) { 1138 if (empty($services[$dbservice->name])) { 1139 $DB->delete_records('external_tokens', array('externalserviceid'=>$dbservice->id)); 1140 $DB->delete_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); 1141 $DB->delete_records('external_services_users', array('externalserviceid'=>$dbservice->id)); 1142 $DB->delete_records('external_services', array('id'=>$dbservice->id)); 1143 continue; 1144 } 1145 $service = $services[$dbservice->name]; 1146 unset($services[$dbservice->name]); 1147 $service['enabled'] = empty($service['enabled']) ? 0 : $service['enabled']; 1148 $service['requiredcapability'] = empty($service['requiredcapability']) ? null : $service['requiredcapability']; 1149 $service['restrictedusers'] = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; 1150 $service['downloadfiles'] = !isset($service['downloadfiles']) ? 0 : $service['downloadfiles']; 1151 $service['uploadfiles'] = !isset($service['uploadfiles']) ? 0 : $service['uploadfiles']; 1152 $service['shortname'] = !isset($service['shortname']) ? null : $service['shortname']; 1153 1154 $update = false; 1155 if ($dbservice->requiredcapability != $service['requiredcapability']) { 1156 $dbservice->requiredcapability = $service['requiredcapability']; 1157 $update = true; 1158 } 1159 if ($dbservice->restrictedusers != $service['restrictedusers']) { 1160 $dbservice->restrictedusers = $service['restrictedusers']; 1161 $update = true; 1162 } 1163 if ($dbservice->downloadfiles != $service['downloadfiles']) { 1164 $dbservice->downloadfiles = $service['downloadfiles']; 1165 $update = true; 1166 } 1167 if ($dbservice->uploadfiles != $service['uploadfiles']) { 1168 $dbservice->uploadfiles = $service['uploadfiles']; 1169 $update = true; 1170 } 1171 //if shortname is not a PARAM_ALPHANUMEXT, fail (tested here for service update and creation) 1172 if (isset($service['shortname']) and 1173 (clean_param($service['shortname'], PARAM_ALPHANUMEXT) != $service['shortname'])) { 1174 throw new moodle_exception('installserviceshortnameerror', 'webservice', '', $service['shortname']); 1175 } 1176 if ($dbservice->shortname != $service['shortname']) { 1177 //check that shortname is unique 1178 if (isset($service['shortname'])) { //we currently accepts multiple shortname == null 1179 $existingservice = $DB->get_record('external_services', 1180 array('shortname' => $service['shortname'])); 1181 if (!empty($existingservice)) { 1182 throw new moodle_exception('installexistingserviceshortnameerror', 'webservice', '', $service['shortname']); 1183 } 1184 } 1185 $dbservice->shortname = $service['shortname']; 1186 $update = true; 1187 } 1188 if ($update) { 1189 $DB->update_record('external_services', $dbservice); 1190 } 1191 1192 $functions = $DB->get_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); 1193 foreach ($functions as $function) { 1194 $key = array_search($function->functionname, $service['functions']); 1195 if ($key === false) { 1196 $DB->delete_records('external_services_functions', array('id'=>$function->id)); 1197 } else { 1198 unset($service['functions'][$key]); 1199 } 1200 } 1201 foreach ($service['functions'] as $fname) { 1202 $newf = new stdClass(); 1203 $newf->externalserviceid = $dbservice->id; 1204 $newf->functionname = $fname; 1205 $DB->insert_record('external_services_functions', $newf); 1206 } 1207 unset($functions); 1208 } 1209 foreach ($services as $name => $service) { 1210 //check that shortname is unique 1211 if (isset($service['shortname'])) { //we currently accepts multiple shortname == null 1212 $existingservice = $DB->get_record('external_services', 1213 array('shortname' => $service['shortname'])); 1214 if (!empty($existingservice)) { 1215 throw new moodle_exception('installserviceshortnameerror', 'webservice'); 1216 } 1217 } 1218 1219 $dbservice = new stdClass(); 1220 $dbservice->name = $name; 1221 $dbservice->enabled = empty($service['enabled']) ? 0 : $service['enabled']; 1222 $dbservice->requiredcapability = empty($service['requiredcapability']) ? null : $service['requiredcapability']; 1223 $dbservice->restrictedusers = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; 1224 $dbservice->downloadfiles = !isset($service['downloadfiles']) ? 0 : $service['downloadfiles']; 1225 $dbservice->uploadfiles = !isset($service['uploadfiles']) ? 0 : $service['uploadfiles']; 1226 $dbservice->shortname = !isset($service['shortname']) ? null : $service['shortname']; 1227 $dbservice->component = $component; 1228 $dbservice->timecreated = time(); 1229 $dbservice->id = $DB->insert_record('external_services', $dbservice); 1230 foreach ($service['functions'] as $fname) { 1231 $newf = new stdClass(); 1232 $newf->externalserviceid = $dbservice->id; 1233 $newf->functionname = $fname; 1234 $DB->insert_record('external_services_functions', $newf); 1235 } 1236 } 1237 } 1238 1239 /** 1240 * Allow plugins and subsystems to add external functions to other plugins or built-in services. 1241 * This function is executed just after all the plugins have been updated. 1242 */ 1243 function external_update_services() { 1244 global $DB; 1245 1246 // Look for external functions that want to be added in existing services. 1247 $functions = $DB->get_records_select('external_functions', 'services IS NOT NULL'); 1248 1249 $servicescache = array(); 1250 foreach ($functions as $function) { 1251 // Prevent edge cases. 1252 if (empty($function->services)) { 1253 continue; 1254 } 1255 $services = explode(',', $function->services); 1256 1257 foreach ($services as $serviceshortname) { 1258 // Get the service id by shortname. 1259 if (!empty($servicescache[$serviceshortname])) { 1260 $serviceid = $servicescache[$serviceshortname]; 1261 } else if ($service = $DB->get_record('external_services', array('shortname' => $serviceshortname))) { 1262 // If the component is empty, it means that is not a built-in service. 1263 // We don't allow functions to inject themselves in services created by an user in Moodle. 1264 if (empty($service->component)) { 1265 continue; 1266 } 1267 $serviceid = $service->id; 1268 $servicescache[$serviceshortname] = $serviceid; 1269 } else { 1270 // Service not found. 1271 continue; 1272 } 1273 // Finally add the function to the service. 1274 $newf = new stdClass(); 1275 $newf->externalserviceid = $serviceid; 1276 $newf->functionname = $function->name; 1277 1278 if (!$DB->record_exists('external_services_functions', (array)$newf)) { 1279 $DB->insert_record('external_services_functions', $newf); 1280 } 1281 } 1282 } 1283 } 1284 1285 /** 1286 * upgrade logging functions 1287 */ 1288 function upgrade_handle_exception($ex, $plugin = null) { 1289 global $CFG; 1290 1291 // rollback everything, we need to log all upgrade problems 1292 abort_all_db_transactions(); 1293 1294 $info = get_exception_info($ex); 1295 1296 // First log upgrade error 1297 upgrade_log(UPGRADE_LOG_ERROR, $plugin, 'Exception: ' . get_class($ex), $info->message, $info->backtrace); 1298 1299 // Always turn on debugging - admins need to know what is going on 1300 set_debugging(DEBUG_DEVELOPER, true); 1301 1302 default_exception_handler($ex, true, $plugin); 1303 } 1304 1305 /** 1306 * Adds log entry into upgrade_log table 1307 * 1308 * @param int $type UPGRADE_LOG_NORMAL, UPGRADE_LOG_NOTICE or UPGRADE_LOG_ERROR 1309 * @param string $plugin frankenstyle component name 1310 * @param string $info short description text of log entry 1311 * @param string $details long problem description 1312 * @param string $backtrace string used for errors only 1313 * @return void 1314 */ 1315 function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { 1316 global $DB, $USER, $CFG; 1317 1318 if (empty($plugin)) { 1319 $plugin = 'core'; 1320 } 1321 1322 list($plugintype, $pluginname) = core_component::normalize_component($plugin); 1323 $component = is_null($pluginname) ? $plugintype : $plugintype . '_' . $pluginname; 1324 1325 $backtrace = format_backtrace($backtrace, true); 1326 1327 $currentversion = null; 1328 $targetversion = null; 1329 1330 //first try to find out current version number 1331 if ($plugintype === 'core') { 1332 //main 1333 $currentversion = $CFG->version; 1334 1335 $version = null; 1336 include("$CFG->dirroot/version.php"); 1337 $targetversion = $version; 1338 1339 } else { 1340 $pluginversion = get_config($component, 'version'); 1341 if (!empty($pluginversion)) { 1342 $currentversion = $pluginversion; 1343 } 1344 $cd = core_component::get_component_directory($component); 1345 if (file_exists("$cd/version.php")) { 1346 $plugin = new stdClass(); 1347 $plugin->version = null; 1348 $module = $plugin; 1349 include("$cd/version.php"); 1350 $targetversion = $plugin->version; 1351 } 1352 } 1353 1354 $log = new stdClass(); 1355 $log->type = $type; 1356 $log->plugin = $component; 1357 $log->version = $currentversion; 1358 $log->targetversion = $targetversion; 1359 $log->info = $info; 1360 $log->details = $details; 1361 $log->backtrace = $backtrace; 1362 $log->userid = $USER->id; 1363 $log->timemodified = time(); 1364 try { 1365 $DB->insert_record('upgrade_log', $log); 1366 } catch (Exception $ignored) { 1367 // possible during install or 2.0 upgrade 1368 } 1369 } 1370 1371 /** 1372 * Marks start of upgrade, blocks any other access to site. 1373 * The upgrade is finished at the end of script or after timeout. 1374 * 1375 * @global object 1376 * @global object 1377 * @global object 1378 */ 1379 function upgrade_started($preinstall=false) { 1380 global $CFG, $DB, $PAGE, $OUTPUT; 1381 1382 static $started = false; 1383 1384 if ($preinstall) { 1385 ignore_user_abort(true); 1386 upgrade_setup_debug(true); 1387 1388 } else if ($started) { 1389 upgrade_set_timeout(120); 1390 1391 } else { 1392 if (!CLI_SCRIPT and !$PAGE->headerprinted) { 1393 $strupgrade = get_string('upgradingversion', 'admin'); 1394 $PAGE->set_pagelayout('maintenance'); 1395 upgrade_init_javascript(); 1396 $PAGE->set_title($strupgrade.' - Moodle '.$CFG->target_release); 1397 $PAGE->set_heading($strupgrade); 1398 $PAGE->navbar->add($strupgrade); 1399 $PAGE->set_cacheable(false); 1400 echo $OUTPUT->header(); 1401 } 1402 1403 ignore_user_abort(true); 1404 core_shutdown_manager::register_function('upgrade_finished_handler'); 1405 upgrade_setup_debug(true); 1406 set_config('upgraderunning', time()+300); 1407 $started = true; 1408 } 1409 } 1410 1411 /** 1412 * Internal function - executed if upgrade interrupted. 1413 */ 1414 function upgrade_finished_handler() { 1415 upgrade_finished(); 1416 } 1417 1418 /** 1419 * Indicates upgrade is finished. 1420 * 1421 * This function may be called repeatedly. 1422 * 1423 * @global object 1424 * @global object 1425 */ 1426 function upgrade_finished($continueurl=null) { 1427 global $CFG, $DB, $OUTPUT; 1428 1429 if (!empty($CFG->upgraderunning)) { 1430 unset_config('upgraderunning'); 1431 // We have to forcefully purge the caches using the writer here. 1432 // This has to be done after we unset the config var. If someone hits the site while this is set they will 1433 // cause the config values to propogate to the caches. 1434 // Caches are purged after the last step in an upgrade but there is several code routines that exceute between 1435 // then and now that leaving a window for things to fall out of sync. 1436 cache_helper::purge_all(true); 1437 upgrade_setup_debug(false); 1438 ignore_user_abort(false); 1439 if ($continueurl) { 1440 echo $OUTPUT->continue_button($continueurl); 1441 echo $OUTPUT->footer(); 1442 die; 1443 } 1444 } 1445 } 1446 1447 /** 1448 * @global object 1449 * @global object 1450 */ 1451 function upgrade_setup_debug($starting) { 1452 global $CFG, $DB; 1453 1454 static $originaldebug = null; 1455 1456 if ($starting) { 1457 if ($originaldebug === null) { 1458 $originaldebug = $DB->get_debug(); 1459 } 1460 if (!empty($CFG->upgradeshowsql)) { 1461 $DB->set_debug(true); 1462 } 1463 } else { 1464 $DB->set_debug($originaldebug); 1465 } 1466 } 1467 1468 function print_upgrade_separator() { 1469 if (!CLI_SCRIPT) { 1470 echo '<hr />'; 1471 } 1472 } 1473 1474 /** 1475 * Default start upgrade callback 1476 * @param string $plugin 1477 * @param bool $installation true if installation, false means upgrade 1478 */ 1479 function print_upgrade_part_start($plugin, $installation, $verbose) { 1480 global $OUTPUT; 1481 if (empty($plugin) or $plugin == 'moodle') { 1482 upgrade_started($installation); // does not store upgrade running flag yet 1483 if ($verbose) { 1484 echo $OUTPUT->heading(get_string('coresystem')); 1485 } 1486 } else { 1487 upgrade_started(); 1488 if ($verbose) { 1489 echo $OUTPUT->heading($plugin); 1490 } 1491 } 1492 if ($installation) { 1493 if (empty($plugin) or $plugin == 'moodle') { 1494 // no need to log - log table not yet there ;-) 1495 } else { 1496 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin installation'); 1497 } 1498 } else { 1499 if (empty($plugin) or $plugin == 'moodle') { 1500 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting core upgrade'); 1501 } else { 1502 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin upgrade'); 1503 } 1504 } 1505 } 1506 1507 /** 1508 * Default end upgrade callback 1509 * @param string $plugin 1510 * @param bool $installation true if installation, false means upgrade 1511 */ 1512 function print_upgrade_part_end($plugin, $installation, $verbose) { 1513 global $OUTPUT; 1514 upgrade_started(); 1515 if ($installation) { 1516 if (empty($plugin) or $plugin == 'moodle') { 1517 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core installed'); 1518 } else { 1519 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin installed'); 1520 } 1521 } else { 1522 if (empty($plugin) or $plugin == 'moodle') { 1523 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core upgraded'); 1524 } else { 1525 upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin upgraded'); 1526 } 1527 } 1528 if ($verbose) { 1529 $notification = new \core\output\notification(get_string('success'), \core\output\notification::NOTIFY_SUCCESS); 1530 $notification->set_show_closebutton(false); 1531 echo $OUTPUT->render($notification); 1532 print_upgrade_separator(); 1533 } 1534 } 1535 1536 /** 1537 * Sets up JS code required for all upgrade scripts. 1538 * @global object 1539 */ 1540 function upgrade_init_javascript() { 1541 global $PAGE; 1542 // scroll to the end of each upgrade page so that ppl see either error or continue button, 1543 // no need to scroll continuously any more, it is enough to jump to end once the footer is printed ;-) 1544 $js = "window.scrollTo(0, 5000000);"; 1545 $PAGE->requires->js_init_code($js); 1546 } 1547 1548 /** 1549 * Try to upgrade the given language pack (or current language) 1550 * 1551 * @param string $lang the code of the language to update, defaults to the current language 1552 */ 1553 function upgrade_language_pack($lang = null) { 1554 global $CFG; 1555 1556 if (!empty($CFG->skiplangupgrade)) { 1557 return; 1558 } 1559 1560 if (!file_exists("$CFG->dirroot/$CFG->admin/tool/langimport/lib.php")) { 1561 // weird, somebody uninstalled the import utility 1562 return; 1563 } 1564 1565 if (!$lang) { 1566 $lang = current_language(); 1567 } 1568 1569 if (!get_string_manager()->translation_exists($lang)) { 1570 return; 1571 } 1572 1573 get_string_manager()->reset_caches(); 1574 1575 if ($lang === 'en') { 1576 return; // Nothing to do 1577 } 1578 1579 upgrade_started(false); 1580 1581 require_once("$CFG->dirroot/$CFG->admin/tool/langimport/lib.php"); 1582 tool_langimport_preupgrade_update($lang); 1583 1584 get_string_manager()->reset_caches(); 1585 1586 print_upgrade_separator(); 1587 } 1588 1589 /** 1590 * Install core moodle tables and initialize 1591 * @param float $version target version 1592 * @param bool $verbose 1593 * @return void, may throw exception 1594 */ 1595 function install_core($version, $verbose) { 1596 global $CFG, $DB; 1597 1598 // We can not call purge_all_caches() yet, make sure the temp and cache dirs exist and are empty. 1599 remove_dir($CFG->cachedir.'', true); 1600 make_cache_directory('', true); 1601 1602 remove_dir($CFG->localcachedir.'', true); 1603 make_localcache_directory('', true); 1604 1605 remove_dir($CFG->tempdir.'', true); 1606 make_temp_directory('', true); 1607 1608 remove_dir($CFG->dataroot.'/muc', true); 1609 make_writable_directory($CFG->dataroot.'/muc', true); 1610 1611 try { 1612 core_php_time_limit::raise(600); 1613 print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag 1614 1615 $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml"); 1616 upgrade_started(); // we want the flag to be stored in config table ;-) 1617 1618 // set all core default records and default settings 1619 require_once("$CFG->libdir/db/install.php"); 1620 xmldb_main_install(); // installs the capabilities too 1621 1622 // store version 1623 upgrade_main_savepoint(true, $version, false); 1624 1625 // Continue with the installation 1626 log_update_descriptions('moodle'); 1627 external_update_descriptions('moodle'); 1628 events_update_definition('moodle'); 1629 \core\task\manager::reset_scheduled_tasks_for_component('moodle'); 1630 message_update_providers('moodle'); 1631 \core\message\inbound\manager::update_handlers_for_component('moodle'); 1632 core_tag_area::reset_definitions_for_component('moodle'); 1633 1634 // Write default settings unconditionally 1635 admin_apply_default_settings(NULL, true); 1636 1637 print_upgrade_part_end(null, true, $verbose); 1638 1639 // Purge all caches. They're disabled but this ensures that we don't have any persistent data just in case something 1640 // during installation didn't use APIs. 1641 cache_helper::purge_all(); 1642 } catch (exception $ex) { 1643 upgrade_handle_exception($ex); 1644 } catch (Throwable $ex) { 1645 // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5). 1646 upgrade_handle_exception($ex); 1647 } 1648 } 1649 1650 /** 1651 * Upgrade moodle core 1652 * @param float $version target version 1653 * @param bool $verbose 1654 * @return void, may throw exception 1655 */ 1656 function upgrade_core($version, $verbose) { 1657 global $CFG, $SITE, $DB, $COURSE; 1658 1659 raise_memory_limit(MEMORY_EXTRA); 1660 1661 require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades 1662 1663 try { 1664 // Reset caches before any output. 1665 cache_helper::purge_all(true); 1666 purge_all_caches(); 1667 1668 // Upgrade current language pack if we can 1669 upgrade_language_pack(); 1670 1671 print_upgrade_part_start('moodle', false, $verbose); 1672 1673 // Pre-upgrade scripts for local hack workarounds. 1674 $preupgradefile = "$CFG->dirroot/local/preupgrade.php"; 1675 if (file_exists($preupgradefile)) { 1676 core_php_time_limit::raise(); 1677 require($preupgradefile); 1678 // Reset upgrade timeout to default. 1679 upgrade_set_timeout(); 1680 } 1681 1682 $result = xmldb_main_upgrade($CFG->version); 1683 if ($version > $CFG->version) { 1684 // store version if not already there 1685 upgrade_main_savepoint($result, $version, false); 1686 } 1687 1688 // In case structure of 'course' table has been changed and we forgot to update $SITE, re-read it from db. 1689 $SITE = $DB->get_record('course', array('id' => $SITE->id)); 1690 $COURSE = clone($SITE); 1691 1692 // perform all other component upgrade routines 1693 update_capabilities('moodle'); 1694 log_update_descriptions('moodle'); 1695 external_update_descriptions('moodle'); 1696 events_update_definition('moodle'); 1697 \core\task\manager::reset_scheduled_tasks_for_component('moodle'); 1698 message_update_providers('moodle'); 1699 \core\message\inbound\manager::update_handlers_for_component('moodle'); 1700 core_tag_area::reset_definitions_for_component('moodle'); 1701 // Update core definitions. 1702 cache_helper::update_definitions(true); 1703 1704 // Purge caches again, just to be sure we arn't holding onto old stuff now. 1705 cache_helper::purge_all(true); 1706 purge_all_caches(); 1707 1708 // Clean up contexts - more and more stuff depends on existence of paths and contexts 1709 context_helper::cleanup_instances(); 1710 context_helper::create_instances(null, false); 1711 context_helper::build_all_paths(false); 1712 $syscontext = context_system::instance(); 1713 $syscontext->mark_dirty(); 1714 1715 print_upgrade_part_end('moodle', false, $verbose); 1716 } catch (Exception $ex) { 1717 upgrade_handle_exception($ex); 1718 } catch (Throwable $ex) { 1719 // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5). 1720 upgrade_handle_exception($ex); 1721 } 1722 } 1723 1724 /** 1725 * Upgrade/install other parts of moodle 1726 * @param bool $verbose 1727 * @return void, may throw exception 1728 */ 1729 function upgrade_noncore($verbose) { 1730 global $CFG; 1731 1732 raise_memory_limit(MEMORY_EXTRA); 1733 1734 // upgrade all plugins types 1735 try { 1736 // Reset caches before any output. 1737 cache_helper::purge_all(true); 1738 purge_all_caches(); 1739 1740 $plugintypes = core_component::get_plugin_types(); 1741 foreach ($plugintypes as $type=>$location) { 1742 upgrade_plugins($type, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose); 1743 } 1744 // Upgrade services. 1745 // This function gives plugins and subsystems a chance to add functions to existing built-in services. 1746 external_update_services(); 1747 1748 // Update cache definitions. Involves scanning each plugin for any changes. 1749 cache_helper::update_definitions(); 1750 // Mark the site as upgraded. 1751 set_config('allversionshash', core_component::get_all_versions_hash()); 1752 1753 // Purge caches again, just to be sure we arn't holding onto old stuff now. 1754 cache_helper::purge_all(true); 1755 purge_all_caches(); 1756 1757 } catch (Exception $ex) { 1758 upgrade_handle_exception($ex); 1759 } catch (Throwable $ex) { 1760 // Engine errors in PHP7 throw exceptions of type Throwable (this "catch" will be ignored in PHP5). 1761 upgrade_handle_exception($ex); 1762 } 1763 } 1764 1765 /** 1766 * Checks if the main tables have been installed yet or not. 1767 * 1768 * Note: we can not use caches here because they might be stale, 1769 * use with care! 1770 * 1771 * @return bool 1772 */ 1773 function core_tables_exist() { 1774 global $DB; 1775 1776 if (!$tables = $DB->get_tables(false) ) { // No tables yet at all. 1777 return false; 1778 1779 } else { // Check for missing main tables 1780 $mtables = array('config', 'course', 'groupings'); // some tables used in 1.9 and 2.0, preferable something from the start and end of install.xml 1781 foreach ($mtables as $mtable) { 1782 if (!in_array($mtable, $tables)) { 1783 return false; 1784 } 1785 } 1786 return true; 1787 } 1788 } 1789 1790 /** 1791 * upgrades the mnet rpc definitions for the given component. 1792 * this method doesn't return status, an exception will be thrown in the case of an error 1793 * 1794 * @param string $component the plugin to upgrade, eg auth_mnet 1795 */ 1796 function upgrade_plugin_mnet_functions($component) { 1797 global $DB, $CFG; 1798 1799 list($type, $plugin) = core_component::normalize_component($component); 1800 $path = core_component::get_plugin_directory($type, $plugin); 1801 1802 $publishes = array(); 1803 $subscribes = array(); 1804 if (file_exists($path . '/db/mnet.php')) { 1805 require_once($path . '/db/mnet.php'); // $publishes comes from this file 1806 } 1807 if (empty($publishes)) { 1808 $publishes = array(); // still need this to be able to disable stuff later 1809 } 1810 if (empty($subscribes)) { 1811 $subscribes = array(); // still need this to be able to disable stuff later 1812 } 1813 1814 static $servicecache = array(); 1815 1816 // rekey an array based on the rpc method for easy lookups later 1817 $publishmethodservices = array(); 1818 $subscribemethodservices = array(); 1819 foreach($publishes as $servicename => $service) { 1820 if (is_array($service['methods'])) { 1821 foreach($service['methods'] as $methodname) { 1822 $service['servicename'] = $servicename; 1823 $publishmethodservices[$methodname][] = $service; 1824 } 1825 } 1826 } 1827 1828 // Disable functions that don't exist (any more) in the source 1829 // Should these be deleted? What about their permissions records? 1830 foreach ($DB->get_records('mnet_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { 1831 if (!array_key_exists($rpc->functionname, $publishmethodservices) && $rpc->enabled) { 1832 $DB->set_field('mnet_rpc', 'enabled', 0, array('id' => $rpc->id)); 1833 } else if (array_key_exists($rpc->functionname, $publishmethodservices) && !$rpc->enabled) { 1834 $DB->set_field('mnet_rpc', 'enabled', 1, array('id' => $rpc->id)); 1835 } 1836 } 1837 1838 // reflect all the services we're publishing and save them 1839 static $cachedclasses = array(); // to store reflection information in 1840 foreach ($publishes as $service => $data) { 1841 $f = $data['filename']; 1842 $c = $data['classname']; 1843 foreach ($data['methods'] as $method) { 1844 $dataobject = new stdClass(); 1845 $dataobject->plugintype = $type; 1846 $dataobject->pluginname = $plugin; 1847 $dataobject->enabled = 1; 1848 $dataobject->classname = $c; 1849 $dataobject->filename = $f; 1850 1851 if (is_string($method)) { 1852 $dataobject->functionname = $method; 1853 1854 } else if (is_array($method)) { // wants to override file or class 1855 $dataobject->functionname = $method['method']; 1856 $dataobject->classname = $method['classname']; 1857 $dataobject->filename = $method['filename']; 1858 } 1859 $dataobject->xmlrpcpath = $type.'/'.$plugin.'/'.$dataobject->filename.'/'.$method; 1860 $dataobject->static = false; 1861 1862 require_once($path . '/' . $dataobject->filename); 1863 $functionreflect = null; // slightly different ways to get this depending on whether it's a class method or a function 1864 if (!empty($dataobject->classname)) { 1865 if (!class_exists($dataobject->classname)) { 1866 throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); 1867 } 1868 $key = $dataobject->filename . '|' . $dataobject->classname; 1869 if (!array_key_exists($key, $cachedclasses)) { // look to see if we've already got a reflection object 1870 try { 1871 $cachedclasses[$key] = new ReflectionClass($dataobject->classname); 1872 } catch (ReflectionException $e) { // catch these and rethrow them to something more helpful 1873 throw new moodle_exception('installreflectionclasserror', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname, 'error' => $e->getMessage())); 1874 } 1875 } 1876 $r =& $cachedclasses[$key]; 1877 if (!$r->hasMethod($dataobject->functionname)) { 1878 throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); 1879 } 1880 $functionreflect = $r->getMethod($dataobject->functionname); 1881 $dataobject->static = (int)$functionreflect->isStatic(); 1882 } else { 1883 if (!function_exists($dataobject->functionname)) { 1884 throw new moodle_exception('installnosuchfunction', 'mnet', '', (object)array('method' => $dataobject->functionname, 'file' => $dataobject->filename)); 1885 } 1886 try { 1887 $functionreflect = new ReflectionFunction($dataobject->functionname); 1888 } catch (ReflectionException $e) { // catch these and rethrow them to something more helpful 1889 throw new moodle_exception('installreflectionfunctionerror', 'mnet', '', (object)array('method' => $dataobject->functionname, '' => $dataobject->filename, 'error' => $e->getMessage())); 1890 } 1891 } 1892 $dataobject->profile = serialize(admin_mnet_method_profile($functionreflect)); 1893 $dataobject->help = admin_mnet_method_get_help($functionreflect); 1894 1895 if ($record_exists = $DB->get_record('mnet_rpc', array('xmlrpcpath'=>$dataobject->xmlrpcpath))) { 1896 $dataobject->id = $record_exists->id; 1897 $dataobject->enabled = $record_exists->enabled; 1898 $DB->update_record('mnet_rpc', $dataobject); 1899 } else { 1900 $dataobject->id = $DB->insert_record('mnet_rpc', $dataobject, true); 1901 } 1902 1903 // TODO this API versioning must be reworked, here the recently processed method 1904 // sets the service API which may not be correct 1905 foreach ($publishmethodservices[$dataobject->functionname] as $service) { 1906 if ($serviceobj = $DB->get_record('mnet_service', array('name'=>$service['servicename']))) { 1907 $serviceobj->apiversion = $service['apiversion']; 1908 $DB->update_record('mnet_service', $serviceobj); 1909 } else { 1910 $serviceobj = new stdClass(); 1911 $serviceobj->name = $service['servicename']; 1912 $serviceobj->description = empty($service['description']) ? '' : $service['description']; 1913 $serviceobj->apiversion = $service['apiversion']; 1914 $serviceobj->offer = 1; 1915 $serviceobj->id = $DB->insert_record('mnet_service', $serviceobj); 1916 } 1917 $servicecache[$service['servicename']] = $serviceobj; 1918 if (!$DB->record_exists('mnet_service2rpc', array('rpcid'=>$dataobject->id, 'serviceid'=>$serviceobj->id))) { 1919 $obj = new stdClass(); 1920 $obj->rpcid = $dataobject->id; 1921 $obj->serviceid = $serviceobj->id; 1922 $DB->insert_record('mnet_service2rpc', $obj, true); 1923 } 1924 } 1925 } 1926 } 1927 // finished with methods we publish, now do subscribable methods 1928 foreach($subscribes as $service => $methods) { 1929 if (!array_key_exists($service, $servicecache)) { 1930 if (!$serviceobj = $DB->get_record('mnet_service', array('name' => $service))) { 1931 debugging("TODO: skipping unknown service $service - somebody needs to fix MDL-21993"); 1932 continue; 1933 } 1934 $servicecache[$service] = $serviceobj; 1935 } else { 1936 $serviceobj = $servicecache[$service]; 1937 } 1938 foreach ($methods as $method => $xmlrpcpath) { 1939 if (!$rpcid = $DB->get_field('mnet_remote_rpc', 'id', array('xmlrpcpath'=>$xmlrpcpath))) { 1940 $remoterpc = (object)array( 1941 'functionname' => $method, 1942 'xmlrpcpath' => $xmlrpcpath, 1943 'plugintype' => $type, 1944 'pluginname' => $plugin, 1945 'enabled' => 1, 1946 ); 1947 $rpcid = $remoterpc->id = $DB->insert_record('mnet_remote_rpc', $remoterpc, true); 1948 } 1949 if (!$DB->record_exists('mnet_remote_service2rpc', array('rpcid'=>$rpcid, 'serviceid'=>$serviceobj->id))) { 1950 $obj = new stdClass(); 1951 $obj->rpcid = $rpcid; 1952 $obj->serviceid = $serviceobj->id; 1953 $DB->insert_record('mnet_remote_service2rpc', $obj, true); 1954 } 1955 $subscribemethodservices[$method][] = $service; 1956 } 1957 } 1958 1959 foreach ($DB->get_records('mnet_remote_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { 1960 if (!array_key_exists($rpc->functionname, $subscribemethodservices) && $rpc->enabled) { 1961 $DB->set_field('mnet_remote_rpc', 'enabled', 0, array('id' => $rpc->id)); 1962 } else if (array_key_exists($rpc->functionname, $subscribemethodservices) && !$rpc->enabled) { 1963 $DB->set_field('mnet_remote_rpc', 'enabled', 1, array('id' => $rpc->id)); 1964 } 1965 } 1966 1967 return true; 1968 } 1969 1970 /** 1971 * Given some sort of reflection function/method object, return a profile array, ready to be serialized and stored 1972 * 1973 * @param ReflectionFunctionAbstract $function reflection function/method object from which to extract information 1974 * 1975 * @return array associative array with function/method information 1976 */ 1977 function admin_mnet_method_profile(ReflectionFunctionAbstract $function) { 1978 $commentlines = admin_mnet_method_get_docblock($function); 1979 $getkey = function($key) use ($commentlines) { 1980 return array_values(array_filter($commentlines, function($line) use ($key) { 1981 return $line[0] == $key; 1982 })); 1983 }; 1984 $returnline = $getkey('@return'); 1985 return array ( 1986 'parameters' => array_map(function($line) { 1987 return array( 1988 'name' => trim($line[2], " \t\n\r\0\x0B$"), 1989 'type' => $line[1], 1990 'description' => $line[3] 1991 ); 1992 }, $getkey('@param')), 1993 1994 'return' => array( 1995 'type' => !empty($returnline[0][1]) ? $returnline[0][1] : 'void', 1996 'description' => !empty($returnline[0][2]) ? $returnline[0][2] : '' 1997 ) 1998 ); 1999 } 2000 2001 /** 2002 * Given some sort of reflection function/method object, return an array of docblock lines, where each line is an array of 2003 * keywords/descriptions 2004 * 2005 * @param ReflectionFunctionAbstract $function reflection function/method object from which to extract information 2006 * 2007 * @return array docblock converted in to an array 2008 */ 2009 function admin_mnet_method_get_docblock(ReflectionFunctionAbstract $function) { 2010 return array_map(function($line) { 2011 $text = trim($line, " \t\n\r\0\x0B*/"); 2012 if (strpos($text, '@param') === 0) { 2013 return preg_split('/\s+/', $text, 4); 2014 } 2015 2016 if (strpos($text, '@return') === 0) { 2017 return preg_split('/\s+/', $text, 3); 2018 } 2019 2020 return array($text); 2021 }, explode("\n", $function->getDocComment())); 2022 } 2023 2024 /** 2025 * Given some sort of reflection function/method object, return just the help text 2026 * 2027 * @param ReflectionFunctionAbstract $function reflection function/method object from which to extract information 2028 * 2029 * @return string docblock help text 2030 */ 2031 function admin_mnet_method_get_help(ReflectionFunctionAbstract $function) { 2032 $helplines = array_map(function($line) { 2033 return implode(' ', $line); 2034 }, array_values(array_filter(admin_mnet_method_get_docblock($function), function($line) { 2035 return strpos($line[0], '@') !== 0 && !empty($line[0]); 2036 }))); 2037 2038 return implode("\n", $helplines); 2039 } 2040 2041 /** 2042 * Detect draft file areas with missing root directory records and add them. 2043 */ 2044 function upgrade_fix_missing_root_folders_draft() { 2045 global $DB; 2046 2047 $transaction = $DB->start_delegated_transaction(); 2048 2049 $sql = "SELECT contextid, itemid, MAX(timecreated) AS timecreated, MAX(timemodified) AS timemodified 2050 FROM {files} 2051 WHERE (component = 'user' AND filearea = 'draft') 2052 GROUP BY contextid, itemid 2053 HAVING MAX(CASE WHEN filename = '.' AND filepath = '/' THEN 1 ELSE 0 END) = 0"; 2054 2055 $rs = $DB->get_recordset_sql($sql); 2056 $defaults = array('component' => 'user', 2057 'filearea' => 'draft', 2058 'filepath' => '/', 2059 'filename' => '.', 2060 'userid' => 0, // Don't rely on any particular user for these system records. 2061 'filesize' => 0, 2062 'contenthash' => sha1('')); 2063 foreach ($rs as $r) { 2064 $r->pathnamehash = sha1("/$r->contextid/user/draft/$r->itemid/."); 2065 $DB->insert_record('files', (array)$r + $defaults); 2066 } 2067 $rs->close(); 2068 $transaction->allow_commit(); 2069 } 2070 2071 /** 2072 * This function verifies that the database is not using an unsupported storage engine. 2073 * 2074 * @param environment_results $result object to update, if relevant 2075 * @return environment_results|null updated results object, or null if the storage engine is supported 2076 */ 2077 function check_database_storage_engine(environment_results $result) { 2078 global $DB; 2079 2080 // Check if MySQL is the DB family (this will also be the same for MariaDB). 2081 if ($DB->get_dbfamily() == 'mysql') { 2082 // Get the database engine we will either be using to install the tables, or what we are currently using. 2083 $engine = $DB->get_dbengine(); 2084 // Check if MyISAM is the storage engine that will be used, if so, do not proceed and display an error. 2085 if ($engine == 'MyISAM') { 2086 $result->setInfo('unsupported_db_storage_engine'); 2087 $result->setStatus(false); 2088 return $result; 2089 } 2090 } 2091 2092 return null; 2093 } 2094 2095 /** 2096 * Method used to check the usage of slasharguments config and display a warning message. 2097 * 2098 * @param environment_results $result object to update, if relevant. 2099 * @return environment_results|null updated results or null if slasharguments is disabled. 2100 */ 2101 function check_slasharguments(environment_results $result){ 2102 global $CFG; 2103 2104 if (!during_initial_install() && empty($CFG->slasharguments)) { 2105 $result->setInfo('slasharguments'); 2106 $result->setStatus(false); 2107 return $result; 2108 } 2109 2110 return null; 2111 } 2112 2113 /** 2114 * This function verifies if the database has tables using innoDB Antelope row format. 2115 * 2116 * @param environment_results $result 2117 * @return environment_results|null updated results object, or null if no Antelope table has been found. 2118 */ 2119 function check_database_tables_row_format(environment_results $result) { 2120 global $DB; 2121 2122 if ($DB->get_dbfamily() == 'mysql') { 2123 $generator = $DB->get_manager()->generator; 2124 2125 foreach ($DB->get_tables(false) as $table) { 2126 $columns = $DB->get_columns($table, false); 2127 $size = $generator->guess_antelope_row_size($columns); 2128 $format = $DB->get_row_format($table); 2129 2130 if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) { 2131 continue; 2132 } 2133 2134 if ($format === 'Compact' or $format === 'Redundant') { 2135 $result->setInfo('unsupported_db_table_row_format'); 2136 $result->setStatus(false); 2137 return $result; 2138 } 2139 } 2140 } 2141 2142 return null; 2143 } 2144 2145 /** 2146 * Upgrade the minmaxgrade setting. 2147 * 2148 * This step should only be run for sites running 2.8 or later. Sites using 2.7 will be fine 2149 * using the new default system setting $CFG->grade_minmaxtouse. 2150 * 2151 * @return void 2152 */ 2153 function upgrade_minmaxgrade() { 2154 global $CFG, $DB; 2155 2156 // 2 is a copy of GRADE_MIN_MAX_FROM_GRADE_GRADE. 2157 $settingvalue = 2; 2158 2159 // Set the course setting when: 2160 // - The system setting does not exist yet. 2161 // - The system seeting is not set to what we'd set the course setting. 2162 $setcoursesetting = !isset($CFG->grade_minmaxtouse) || $CFG->grade_minmaxtouse != $settingvalue; 2163 2164 // Identify the courses that have inconsistencies grade_item vs grade_grade. 2165 $sql = "SELECT DISTINCT(gi.courseid) 2166 FROM {grade_grades} gg 2167 JOIN {grade_items} gi 2168 ON gg.itemid = gi.id 2169 WHERE gi.itemtype NOT IN (?, ?) 2170 AND (gg.rawgrademax != gi.grademax OR gg.rawgrademin != gi.grademin)"; 2171 2172 $rs = $DB->get_recordset_sql($sql, array('course', 'category')); 2173 foreach ($rs as $record) { 2174 // Flag the course to show a notice in the gradebook. 2175 set_config('show_min_max_grades_changed_' . $record->courseid, 1); 2176 2177 // Set the appropriate course setting so that grades displayed are not changed. 2178 $configname = 'minmaxtouse'; 2179 if ($setcoursesetting && 2180 !$DB->record_exists('grade_settings', array('courseid' => $record->courseid, 'name' => $configname))) { 2181 // Do not set the setting when the course already defines it. 2182 $data = new stdClass(); 2183 $data->courseid = $record->courseid; 2184 $data->name = $configname; 2185 $data->value = $settingvalue; 2186 $DB->insert_record('grade_settings', $data); 2187 } 2188 2189 // Mark the grades to be regraded. 2190 $DB->set_field('grade_items', 'needsupdate', 1, array('courseid' => $record->courseid)); 2191 } 2192 $rs->close(); 2193 } 2194 2195 2196 /** 2197 * Assert the upgrade key is provided, if it is defined. 2198 * 2199 * The upgrade key can be defined in the main config.php as $CFG->upgradekey. If 2200 * it is defined there, then its value must be provided every time the site is 2201 * being upgraded, regardless the administrator is logged in or not. 2202 * 2203 * This is supposed to be used at certain places in /admin/index.php only. 2204 * 2205 * @param string|null $upgradekeyhash the SHA-1 of the value provided by the user 2206 */ 2207 function check_upgrade_key($upgradekeyhash) { 2208 global $CFG, $PAGE; 2209 2210 if (isset($CFG->config_php_settings['upgradekey'])) { 2211 if ($upgradekeyhash === null or $upgradekeyhash !== sha1($CFG->config_php_settings['upgradekey'])) { 2212 if (!$PAGE->headerprinted) { 2213 $output = $PAGE->get_renderer('core', 'admin'); 2214 echo $output->upgradekey_form_page(new moodle_url('/admin/index.php', array('cache' => 0))); 2215 die(); 2216 } else { 2217 // This should not happen. 2218 die('Upgrade locked'); 2219 } 2220 } 2221 } 2222 } 2223 2224 /** 2225 * Helper procedure/macro for installing remote plugins at admin/index.php 2226 * 2227 * Does not return, always redirects or exits. 2228 * 2229 * @param array $installable list of \core\update\remote_info 2230 * @param bool $confirmed false: display the validation screen, true: proceed installation 2231 * @param string $heading validation screen heading 2232 * @param moodle_url|string|null $continue URL to proceed with installation at the validation screen 2233 * @param moodle_url|string|null $return URL to go back on cancelling at the validation screen 2234 */ 2235 function upgrade_install_plugins(array $installable, $confirmed, $heading='', $continue=null, $return=null) { 2236 global $CFG, $PAGE; 2237 2238 if (empty($return)) { 2239 $return = $PAGE->url; 2240 } 2241 2242 if (!empty($CFG->disableupdateautodeploy)) { 2243 redirect($return); 2244 } 2245 2246 if (empty($installable)) { 2247 redirect($return); 2248 } 2249 2250 $pluginman = core_plugin_manager::instance(); 2251 2252 if ($confirmed) { 2253 // Installation confirmed at the validation results page. 2254 if (!$pluginman->install_plugins($installable, true, true)) { 2255 throw new moodle_exception('install_plugins_failed', 'core_plugin', $return); 2256 } 2257 2258 // Always redirect to admin/index.php to perform the database upgrade. 2259 // Do not throw away the existing $PAGE->url parameters such as 2260 // confirmupgrade or confirmrelease if $PAGE->url is a superset of the 2261 // URL we must go to. 2262 $mustgoto = new moodle_url('/admin/index.php', array('cache' => 0, 'confirmplugincheck' => 0)); 2263 if ($mustgoto->compare($PAGE->url, URL_MATCH_PARAMS)) { 2264 redirect($PAGE->url); 2265 } else { 2266 redirect($mustgoto); 2267 } 2268 2269 } else { 2270 $output = $PAGE->get_renderer('core', 'admin'); 2271 echo $output->header(); 2272 if ($heading) { 2273 echo $output->heading($heading, 3); 2274 } 2275 echo html_writer::start_tag('pre', array('class' => 'plugin-install-console')); 2276 $validated = $pluginman->install_plugins($installable, false, false); 2277 echo html_writer::end_tag('pre'); 2278 if ($validated) { 2279 echo $output->plugins_management_confirm_buttons($continue, $return); 2280 } else { 2281 echo $output->plugins_management_confirm_buttons(null, $return); 2282 } 2283 echo $output->footer(); 2284 die(); 2285 } 2286 } 2287 /** 2288 * Method used to check the installed unoconv version. 2289 * 2290 * @param environment_results $result object to update, if relevant. 2291 * @return environment_results|null updated results or null if unoconv path is not executable. 2292 */ 2293 function check_unoconv_version(environment_results $result) { 2294 global $CFG; 2295 2296 if (!during_initial_install() && !empty($CFG->pathtounoconv) && file_is_executable(trim($CFG->pathtounoconv))) { 2297 $currentversion = 0; 2298 $supportedversion = 0.7; 2299 $unoconvbin = \escapeshellarg($CFG->pathtounoconv); 2300 $command = "$unoconvbin --version"; 2301 exec($command, $output); 2302 2303 // If the command execution returned some output, then get the unoconv version. 2304 if ($output) { 2305 foreach ($output as $response) { 2306 if (preg_match('/unoconv (\\d+\\.\\d+)/', $response, $matches)) { 2307 $currentversion = (float)$matches[1]; 2308 } 2309 } 2310 } 2311 2312 if ($currentversion < $supportedversion) { 2313 $result->setInfo('unoconv version not supported'); 2314 $result->setStatus(false); 2315 return $result; 2316 } 2317 } 2318 return null; 2319 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |