Commit | Line | Data |
---|---|---|
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 | 28 | define('UPGRADE_LOG_NORMAL', 0); |
72fb21b6 | 29 | /** UPGRADE_LOG_NOTICE = 1 */ |
db9d4a3d | 30 | define('UPGRADE_LOG_NOTICE', 1); |
72fb21b6 | 31 | /** UPGRADE_LOG_ERROR = 2 */ |
795a08ad | 32 | define('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 | */ |
42 | class upgrade_exception extends moodle_exception { | |
43 | function __construct($plugin, $version) { | |
44 | global $CFG; | |
45 | $a = (object)array('plugin'=>$plugin, 'version'=>$version); | |
45440ceb | 46 | parent::__construct('upgradeerror', 'admin', "$CFG->wwwroot/$CFG->admin/index.php", $a); |
795a08ad | 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 | */ |
58 | class 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 | 73 | class 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; | |
17da2e6f | 80 | $a->requiremoodle = $requiremoodle; |
795a08ad | 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 | 91 | class 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 | */ | |
110 | function 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 | */ | |
143 | function upgrade_main_savepoint($result, $version, $allowabort=true) { | |
144 | global $CFG; | |
145 | ||
795a08ad | 146 | if (!$result) { |
520cea92 | 147 | throw new upgrade_exception(null, $version); |
795a08ad | 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 | */ | |
179 | function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { | |
180 | global $DB; | |
181 | ||
795a08ad | 182 | if (!$result) { |
520cea92 | 183 | throw new upgrade_exception("mod_$modname", $version); |
795a08ad | 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 | |
520cea92 | 192 | throw new downgrade_exception("mod_$modname", $module->version, $version); |
db9d4a3d | 193 | } |
795a08ad | 194 | $module->version = $version; |
195 | $DB->update_record('modules', $module); | |
520cea92 | 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 | 219 | function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) { |
db9d4a3d | 220 | global $DB; |
221 | ||
795a08ad | 222 | if (!$result) { |
520cea92 | 223 | throw new upgrade_exception("block_$blockname", $version); |
795a08ad | 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 | |
520cea92 | 232 | throw new downgrade_exception("block_$blockname", $block->version, $version); |
db9d4a3d | 233 | } |
795a08ad | 234 | $block->version = $version; |
235 | $DB->update_record('block', $block); | |
520cea92 | 236 | upgrade_log(UPGRADE_LOG_NORMAL, "block_$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 | */ | |
17da2e6f | 259 | function upgrade_plugin_savepoint($result, $version, $type, $plugin, $allowabort=true) { |
260 | $component = $type.'_'.$plugin; | |
261 | ||
795a08ad | 262 | if (!$result) { |
17da2e6f | 263 | throw new upgrade_exception($component, $version); |
db9d4a3d | 264 | } |
265 | ||
17da2e6f | 266 | $installedversion = get_config($component, 'version'); |
795a08ad | 267 | if ($installedversion >= $version) { |
268 | // Something really wrong is going on in the upgrade script | |
269 | throw new downgrade_exception($component, $installedversion, $version); | |
270 | } | |
17da2e6f | 271 | set_config('version', $version, $component); |
795a08ad | 272 | upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); |
273 | ||
db9d4a3d | 274 | // Reset upgrade timeout to default |
275 | upgrade_set_timeout(); | |
276 | ||
277 | // This is a safe place to stop upgrades if user aborts page loading | |
278 | if ($allowabort and connection_aborted()) { | |
279 | die; | |
280 | } | |
281 | } | |
282 | ||
283 | ||
284 | /** | |
285 | * Upgrade plugins | |
db9d4a3d | 286 | * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype') |
17da2e6f | 287 | * return void |
db9d4a3d | 288 | */ |
17da2e6f | 289 | function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { |
db9d4a3d | 290 | global $CFG, $DB; |
291 | ||
292 | /// special cases | |
293 | if ($type === 'mod') { | |
194cdca8 | 294 | return upgrade_plugins_modules($startcallback, $endcallback, $verbose); |
795a08ad | 295 | } else if ($type === 'block') { |
194cdca8 | 296 | return upgrade_plugins_blocks($startcallback, $endcallback, $verbose); |
db9d4a3d | 297 | } |
298 | ||
17da2e6f | 299 | $plugs = get_plugin_list($type); |
db9d4a3d | 300 | |
17da2e6f | 301 | foreach ($plugs as $plug=>$fullplug) { |
302 | $component = $type.'_'.$plug; // standardised plugin name | |
db9d4a3d | 303 | |
795a08ad | 304 | if (!is_readable($fullplug.'/version.php')) { |
305 | continue; | |
db9d4a3d | 306 | } |
307 | ||
795a08ad | 308 | $plugin = new object(); |
309 | require($fullplug.'/version.php'); // defines $plugin with version etc | |
db9d4a3d | 310 | |
795a08ad | 311 | if (empty($plugin->version)) { |
312 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); | |
db9d4a3d | 313 | } |
314 | ||
17da2e6f | 315 | $plugin->name = $plug; |
316 | $plugin->fullname = $component; | |
795a08ad | 317 | |
318 | ||
db9d4a3d | 319 | if (!empty($plugin->requires)) { |
320 | if ($plugin->requires > $CFG->version) { | |
795a08ad | 321 | throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); |
db9d4a3d | 322 | } |
323 | } | |
324 | ||
9b683d13 | 325 | // try to recover from interrupted install.php if needed |
326 | if (file_exists($fullplug.'/db/install.php')) { | |
327 | if (get_config($plugin->fullname, 'installrunning')) { | |
328 | require_once($fullplug.'/db/install.php'); | |
329 | $recover_install_function = 'xmldb_'.$plugin->fullname.'_install_recovery'; | |
330 | if (function_exists($recover_install_function)) { | |
331 | $startcallback($component, true, $verbose); | |
332 | $recover_install_function(); | |
333 | unset_config('installrunning', 'block_'.$plugin->fullname); | |
334 | update_capabilities($component); | |
c976e271 | 335 | external_update_descriptions($component); |
9b683d13 | 336 | events_update_definition($component); |
337 | message_update_providers($component); | |
de260e0f | 338 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 339 | $endcallback($component, true, $verbose); |
340 | } | |
341 | } | |
342 | } | |
db9d4a3d | 343 | |
9b683d13 | 344 | $installedversion = get_config($plugin->fullname, 'version'); |
795a08ad | 345 | if (empty($installedversion)) { // new installation |
194cdca8 | 346 | $startcallback($component, true, $verbose); |
db9d4a3d | 347 | |
795a08ad | 348 | /// Install tables if defined |
349 | if (file_exists($fullplug.'/db/install.xml')) { | |
350 | $DB->get_manager()->install_from_xmldb_file($fullplug.'/db/install.xml'); | |
db9d4a3d | 351 | } |
9b683d13 | 352 | |
353 | /// store version | |
354 | upgrade_plugin_savepoint(true, $plugin->version, $type, $plug, false); | |
355 | ||
795a08ad | 356 | /// execute post install file |
357 | if (file_exists($fullplug.'/db/install.php')) { | |
358 | require_once($fullplug.'/db/install.php'); | |
9b683d13 | 359 | set_config('installrunning', 1, 'block_'.$plugin->fullname); |
795a08ad | 360 | $post_install_function = 'xmldb_'.$plugin->fullname.'_install';; |
361 | $post_install_function(); | |
9b683d13 | 362 | unset_config('installrunning', 'block_'.$plugin->fullname); |
795a08ad | 363 | } |
364 | ||
795a08ad | 365 | /// Install various components |
366 | update_capabilities($component); | |
c976e271 | 367 | external_update_descriptions($component); |
795a08ad | 368 | events_update_definition($component); |
369 | message_update_providers($component); | |
de260e0f | 370 | upgrade_plugin_mnet_functions($component); |
795a08ad | 371 | |
0139ec3f | 372 | upgrade_reset_caches(); |
194cdca8 | 373 | $endcallback($component, true, $verbose); |
795a08ad | 374 | |
375 | } else if ($installedversion < $plugin->version) { // upgrade | |
376 | /// Run the upgrade function for the plugin. | |
194cdca8 | 377 | $startcallback($component, false, $verbose); |
795a08ad | 378 | |
379 | if (is_readable($fullplug.'/db/upgrade.php')) { | |
380 | require_once($fullplug.'/db/upgrade.php'); // defines upgrading function | |
381 | ||
382 | $newupgrade_function = 'xmldb_'.$plugin->fullname.'_upgrade'; | |
383 | $result = $newupgrade_function($installedversion); | |
384 | } else { | |
385 | $result = true; | |
386 | } | |
387 | ||
388 | $installedversion = get_config($plugin->fullname, 'version'); | |
389 | if ($installedversion < $plugin->version) { | |
390 | // store version if not already there | |
391 | upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false); | |
392 | } | |
393 | ||
394 | /// Upgrade various components | |
395 | update_capabilities($component); | |
c976e271 | 396 | external_update_descriptions($component); |
795a08ad | 397 | events_update_definition($component); |
398 | message_update_providers($component); | |
de260e0f | 399 | upgrade_plugin_mnet_functions($component); |
795a08ad | 400 | |
0139ec3f | 401 | upgrade_reset_caches(); |
194cdca8 | 402 | $endcallback($component, false, $verbose); |
795a08ad | 403 | |
404 | } else if ($installedversion > $plugin->version) { | |
405 | throw new downgrade_exception($component, $installedversion, $plugin->version); | |
db9d4a3d | 406 | } |
407 | } | |
db9d4a3d | 408 | } |
409 | ||
410 | /** | |
411 | * Find and check all modules and load them up or upgrade them if necessary | |
72fb21b6 | 412 | * |
413 | * @global object | |
414 | * @global object | |
db9d4a3d | 415 | */ |
194cdca8 | 416 | function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { |
db9d4a3d | 417 | global $CFG, $DB; |
418 | ||
17da2e6f | 419 | $mods = get_plugin_list('mod'); |
db9d4a3d | 420 | |
17da2e6f | 421 | foreach ($mods as $mod=>$fullmod) { |
db9d4a3d | 422 | |
423 | if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it | |
424 | continue; | |
425 | } | |
426 | ||
17da2e6f | 427 | $component = 'mod_'.$mod; |
db9d4a3d | 428 | |
795a08ad | 429 | if (!is_readable($fullmod.'/version.php')) { |
430 | throw new plugin_defective_exception($component, 'Missing version.php'); | |
db9d4a3d | 431 | } |
432 | ||
795a08ad | 433 | $module = new object(); |
434 | require($fullmod .'/version.php'); // defines $module with version etc | |
db9d4a3d | 435 | |
795a08ad | 436 | if (empty($module->version)) { |
c1fe2368 | 437 | if (isset($module->version)) { |
438 | // Version is empty but is set - it means its value is 0 or ''. Let us skip such module. | |
439 | // This is inteded for developers so they can work on the early stages of the module. | |
440 | continue; | |
441 | } | |
795a08ad | 442 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); |
db9d4a3d | 443 | } |
444 | ||
445 | if (!empty($module->requires)) { | |
446 | if ($module->requires > $CFG->version) { | |
795a08ad | 447 | throw new upgrade_requires_exception($component, $module->version, $CFG->version, $module->requires); |
db9d4a3d | 448 | } |
449 | } | |
450 | ||
451 | $module->name = $mod; // The name MUST match the directory | |
452 | ||
795a08ad | 453 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); |
db9d4a3d | 454 | |
9b683d13 | 455 | if (file_exists($fullmod.'/db/install.php')) { |
456 | if (get_config($module->name, 'installrunning')) { | |
457 | require_once($fullmod.'/db/install.php'); | |
458 | $recover_install_function = 'xmldb_'.$module->name.'_install_recovery'; | |
459 | if (function_exists($recover_install_function)) { | |
460 | $startcallback($component, true, $verbose); | |
461 | $recover_install_function(); | |
462 | unset_config('installrunning', $module->name); | |
463 | // Install various components too | |
464 | update_capabilities($component); | |
c976e271 | 465 | external_update_descriptions($component); |
9b683d13 | 466 | events_update_definition($component); |
467 | message_update_providers($component); | |
de260e0f | 468 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 469 | $endcallback($component, true, $verbose); |
470 | } | |
471 | } | |
472 | } | |
473 | ||
795a08ad | 474 | if (empty($currmodule->version)) { |
194cdca8 | 475 | $startcallback($component, true, $verbose); |
db9d4a3d | 476 | |
795a08ad | 477 | /// Execute install.xml (XMLDB) - must be present in all modules |
478 | $DB->get_manager()->install_from_xmldb_file($fullmod.'/db/install.xml'); | |
db9d4a3d | 479 | |
2e3da297 | 480 | /// Add record into modules table - may be needed in install.php already |
481 | $module->id = $DB->insert_record('modules', $module); | |
482 | ||
db9d4a3d | 483 | /// Post installation hook - optional |
484 | if (file_exists("$fullmod/db/install.php")) { | |
485 | require_once("$fullmod/db/install.php"); | |
9b683d13 | 486 | // Set installation running flag, we need to recover after exception or error |
487 | set_config('installrunning', 1, $module->name); | |
db9d4a3d | 488 | $post_install_function = 'xmldb_'.$module->name.'_install';; |
489 | $post_install_function(); | |
9b683d13 | 490 | unset_config('installrunning', $module->name); |
db9d4a3d | 491 | } |
492 | ||
795a08ad | 493 | /// Install various components |
494 | update_capabilities($component); | |
c976e271 | 495 | external_update_descriptions($component); |
795a08ad | 496 | events_update_definition($component); |
497 | message_update_providers($component); | |
de260e0f | 498 | upgrade_plugin_mnet_functions($component); |
795a08ad | 499 | |
0139ec3f | 500 | upgrade_reset_caches(); |
194cdca8 | 501 | $endcallback($component, true, $verbose); |
db9d4a3d | 502 | |
795a08ad | 503 | } else if ($currmodule->version < $module->version) { |
504 | /// If versions say that we need to upgrade but no upgrade files are available, notify and continue | |
194cdca8 | 505 | $startcallback($component, false, $verbose); |
795a08ad | 506 | |
507 | if (is_readable($fullmod.'/db/upgrade.php')) { | |
508 | require_once($fullmod.'/db/upgrade.php'); // defines new upgrading function | |
509 | $newupgrade_function = 'xmldb_'.$module->name.'_upgrade'; | |
510 | $result = $newupgrade_function($currmodule->version, $module); | |
511 | } else { | |
512 | $result = true; | |
513 | } | |
514 | ||
515 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); | |
516 | if ($currmodule->version < $module->version) { | |
517 | // store version if not already there | |
518 | upgrade_mod_savepoint($result, $module->version, $mod, false); | |
519 | } | |
520 | ||
521 | /// Upgrade various components | |
522 | update_capabilities($component); | |
c976e271 | 523 | external_update_descriptions($component); |
795a08ad | 524 | events_update_definition($component); |
525 | message_update_providers($component); | |
de260e0f | 526 | upgrade_plugin_mnet_functions($component); |
795a08ad | 527 | |
0139ec3f | 528 | upgrade_reset_caches(); |
795a08ad | 529 | remove_dir($CFG->dataroot.'/cache', true); // flush cache |
530 | ||
194cdca8 | 531 | $endcallback($component, false, $verbose); |
795a08ad | 532 | |
533 | } else if ($currmodule->version > $module->version) { | |
534 | throw new downgrade_exception($component, $currmodule->version, $module->version); | |
535 | } | |
536 | } | |
537 | } | |
db9d4a3d | 538 | |
db9d4a3d | 539 | |
795a08ad | 540 | /** |
541 | * This function finds all available blocks and install them | |
542 | * into blocks table or do all the upgrade process if newer. | |
72fb21b6 | 543 | * |
544 | * @global object | |
545 | * @global object | |
795a08ad | 546 | */ |
194cdca8 | 547 | function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { |
795a08ad | 548 | global $CFG, $DB; |
549 | ||
795a08ad | 550 | require_once($CFG->dirroot.'/blocks/moodleblock.class.php'); |
551 | ||
552 | $blocktitles = array(); // we do not want duplicate titles | |
553 | ||
554 | //Is this a first install | |
555 | $first_install = null; | |
556 | ||
17da2e6f | 557 | $blocks = get_plugin_list('block'); |
795a08ad | 558 | |
17da2e6f | 559 | foreach ($blocks as $blockname=>$fullblock) { |
795a08ad | 560 | |
561 | if (is_null($first_install)) { | |
562 | $first_install = ($DB->count_records('block') == 0); | |
563 | } | |
564 | ||
565 | if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it | |
566 | continue; | |
db9d4a3d | 567 | } |
568 | ||
17da2e6f | 569 | $component = 'block_'.$blockname; |
db9d4a3d | 570 | |
795a08ad | 571 | if (!is_readable($fullblock.'/block_'.$blockname.'.php')) { |
572 | throw new plugin_defective_exception('block/'.$blockname, 'Missing main block class file.'); | |
db9d4a3d | 573 | } |
795a08ad | 574 | require_once($fullblock.'/block_'.$blockname.'.php'); |
db9d4a3d | 575 | |
795a08ad | 576 | $classname = 'block_'.$blockname; |
577 | ||
578 | if (!class_exists($classname)) { | |
579 | throw new plugin_defective_exception($component, 'Can not load main class.'); | |
580 | } | |
581 | ||
582 | $blockobj = new $classname; // This is what we 'll be testing | |
583 | $blocktitle = $blockobj->get_title(); | |
584 | ||
585 | // OK, it's as we all hoped. For further tests, the object will do them itself. | |
586 | if (!$blockobj->_self_test()) { | |
587 | throw new plugin_defective_exception($component, 'Self test failed.'); | |
588 | } | |
589 | ||
590 | $block = new object(); // This may be used to update the db below | |
591 | $block->name = $blockname; // The name MUST match the directory | |
592 | $block->version = $blockobj->get_version(); | |
593 | $block->cron = !empty($blockobj->cron) ? $blockobj->cron : 0; | |
594 | $block->multiple = $blockobj->instance_allow_multiple() ? 1 : 0; | |
595 | ||
596 | if (empty($block->version)) { | |
597 | throw new plugin_defective_exception($component, 'Missing block version.'); | |
598 | } | |
599 | ||
600 | $currblock = $DB->get_record('block', array('name'=>$block->name)); | |
601 | ||
9b683d13 | 602 | if (file_exists($fullblock.'/db/install.php')) { |
603 | if (get_config('block_'.$blockname, 'installrunning')) { | |
604 | require_once($fullblock.'/db/install.php'); | |
605 | $recover_install_function = 'xmldb_block_'.$blockname.'_install_recovery'; | |
606 | if (function_exists($recover_install_function)) { | |
607 | $startcallback($component, true, $verbose); | |
608 | $recover_install_function(); | |
609 | unset_config('installrunning', 'block_'.$blockname); | |
610 | // Install various components | |
611 | update_capabilities($component); | |
c976e271 | 612 | external_update_descriptions($component); |
9b683d13 | 613 | events_update_definition($component); |
614 | message_update_providers($component); | |
de260e0f | 615 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 616 | $endcallback($component, true, $verbose); |
617 | } | |
618 | } | |
619 | } | |
620 | ||
795a08ad | 621 | if (empty($currblock->version)) { // block not installed yet, so install it |
622 | // If it allows multiples, start with it enabled | |
623 | ||
624 | $conflictblock = array_search($blocktitle, $blocktitles); | |
625 | if ($conflictblock !== false) { | |
626 | // Duplicate block titles are not allowed, they confuse people | |
627 | // AND PHP's associative arrays ;) | |
628 | throw new plugin_defective_exception($component, get_string('blocknameconflict', '', (object)array('name'=>$block->name, 'conflict'=>$conflictblock))); | |
629 | } | |
194cdca8 | 630 | $startcallback($component, true, $verbose); |
795a08ad | 631 | |
632 | if (file_exists($fullblock.'/db/install.xml')) { | |
633 | $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml'); | |
634 | } | |
635 | $block->id = $DB->insert_record('block', $block); | |
636 | ||
637 | if (file_exists($fullblock.'/db/install.php')) { | |
638 | require_once($fullblock.'/db/install.php'); | |
9b683d13 | 639 | // Set installation running flag, we need to recover after exception or error |
640 | set_config('installrunning', 1, 'block_'.$blockname); | |
795a08ad | 641 | $post_install_function = 'xmldb_block_'.$blockname.'_install';; |
642 | $post_install_function(); | |
9b683d13 | 643 | unset_config('installrunning', 'block_'.$blockname); |
795a08ad | 644 | } |
645 | ||
646 | $blocktitles[$block->name] = $blocktitle; | |
647 | ||
648 | // Install various components | |
649 | update_capabilities($component); | |
c976e271 | 650 | external_update_descriptions($component); |
795a08ad | 651 | events_update_definition($component); |
652 | message_update_providers($component); | |
de260e0f | 653 | upgrade_plugin_mnet_functions($component); |
795a08ad | 654 | |
0139ec3f | 655 | upgrade_reset_caches(); |
194cdca8 | 656 | $endcallback($component, true, $verbose); |
795a08ad | 657 | |
658 | } else if ($currblock->version < $block->version) { | |
194cdca8 | 659 | $startcallback($component, false, $verbose); |
795a08ad | 660 | |
661 | if (is_readable($fullblock.'/db/upgrade.php')) { | |
662 | require_once($fullblock.'/db/upgrade.php'); // defines new upgrading function | |
663 | $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade'; | |
664 | $result = $newupgrade_function($currblock->version, $block); | |
665 | } else { | |
666 | $result = true; | |
667 | } | |
668 | ||
669 | $currblock = $DB->get_record('block', array('name'=>$block->name)); | |
670 | if ($currblock->version < $block->version) { | |
671 | // store version if not already there | |
672 | upgrade_block_savepoint($result, $block->version, $block->name, false); | |
673 | } | |
674 | ||
675 | if ($currblock->cron != $block->cron) { | |
676 | // update cron flag if needed | |
677 | $currblock->cron = $block->cron; | |
678 | $DB->update_record('block', $currblock); | |
679 | } | |
680 | ||
681 | // Upgrade various componebts | |
795a08ad | 682 | update_capabilities($component); |
c976e271 | 683 | external_update_descriptions($component); |
17da2e6f | 684 | events_update_definition($component); |
795a08ad | 685 | message_update_providers($component); |
de260e0f | 686 | upgrade_plugin_mnet_functions($component); |
795a08ad | 687 | |
0139ec3f | 688 | upgrade_reset_caches(); |
194cdca8 | 689 | $endcallback($component, false, $verbose); |
795a08ad | 690 | |
691 | } else if ($currblock->version > $block->version) { | |
692 | throw new downgrade_exception($component, $currblock->version, $block->version); | |
693 | } | |
694 | } | |
695 | ||
696 | ||
697 | // Finally, if we are in the first_install of BLOCKS setup frontpage and admin page blocks | |
698 | if ($first_install) { | |
795a08ad | 699 | //Iterate over each course - there should be only site course here now |
700 | if ($courses = $DB->get_records('course')) { | |
701 | foreach ($courses as $course) { | |
9d1d606e | 702 | blocks_add_default_course_blocks($course); |
db9d4a3d | 703 | } |
704 | } | |
795a08ad | 705 | |
9d1d606e | 706 | blocks_add_default_system_blocks(); |
795a08ad | 707 | } |
708 | } | |
709 | ||
c976e271 | 710 | /** |
711 | * Web service discovery function used during install and upgrade. | |
712 | * @param string $component name of component (moodle, mod_assignment, etc.) | |
713 | * @return void | |
714 | */ | |
715 | function external_update_descriptions($component) { | |
716 | global $DB; | |
717 | ||
718 | $defpath = get_component_directory($component).'/db/services.php'; | |
719 | ||
720 | if (!file_exists($defpath)) { | |
721 | external_delete_descriptions($component); | |
722 | return; | |
723 | } | |
724 | ||
725 | // load new info | |
726 | $functions = array(); | |
727 | $services = array(); | |
728 | include($defpath); | |
729 | ||
730 | // update all function fist | |
731 | $dbfunctions = $DB->get_records('external_functions', array('component'=>$component)); | |
732 | foreach ($dbfunctions as $dbfunction) { | |
733 | if (empty($functions[$dbfunction->name])) { | |
734 | $DB->delete_records('external_functions', array('id'=>$dbfunction->id)); | |
735 | // do not delete functions from external_services_functions, beacuse | |
736 | // we want to notify admins when functions used in custom services disappear | |
737 | continue; | |
738 | } | |
739 | ||
740 | $function = $functions[$dbfunction->name]; | |
741 | unset($functions[$dbfunction->name]); | |
742 | $function['classpath'] = empty($function['classpath']) ? null : $function['classpath']; | |
743 | ||
744 | $update = false; | |
745 | if ($dbfunction->classname != $function['classname']) { | |
746 | $dbfunction->classname = $function['classname']; | |
747 | $update = true; | |
748 | } | |
749 | if ($dbfunction->methodname != $function['methodname']) { | |
750 | $dbfunction->methodname = $function['methodname']; | |
751 | $update = true; | |
752 | } | |
753 | if ($dbfunction->classpath != $function['classpath']) { | |
754 | $dbfunction->classpath = $function['classpath']; | |
755 | $update = true; | |
756 | } | |
757 | if ($update) { | |
758 | $DB->update_record('external_functions', $dbfunction); | |
759 | } | |
760 | } | |
761 | foreach ($functions as $fname => $function) { | |
762 | $dbfunction = new object(); | |
763 | $dbfunction->name = $fname; | |
764 | $dbfunction->classname = $function['classname']; | |
765 | $dbfunction->methodname = $function['methodname']; | |
766 | $dbfunction->classpath = empty($function['classpath']) ? null : $function['classpath']; | |
767 | $dbfunction->component = $component; | |
768 | $dbfunction->id = $DB->insert_record('external_functions', $dbfunction); | |
769 | } | |
770 | unset($functions); | |
771 | ||
772 | // now deal with services | |
773 | $dbservices = $DB->get_records('external_services', array('component'=>$component)); | |
774 | foreach ($dbservices as $dbservice) { | |
775 | if (empty($services[$dbservice->name])) { | |
776 | $DB->delete_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); | |
777 | $DB->delete_records('external_services_users', array('externalserviceid'=>$dbservice->id)); | |
778 | $DB->delete_records('external_services', array('id'=>$dbservice->id)); | |
779 | continue; | |
780 | } | |
781 | $service = $services[$dbservice->name]; | |
782 | unset($services[$dbservice->name]); | |
783 | $service['enabled'] = empty($service['enabled']) ? 0 : $service['enabled']; | |
784 | $service['requiredcapability'] = empty($service['requiredcapability']) ? null : $service['requiredcapability']; | |
785 | $service['restrictedusers'] = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; | |
786 | ||
787 | $update = false; | |
788 | if ($dbservice->enabled != $service['enabled']) { | |
789 | $dbservice->enabled = $service['enabled']; | |
790 | $update = true; | |
791 | } | |
792 | if ($dbservice->requiredcapability != $service['requiredcapability']) { | |
793 | $dbservice->requiredcapability = $service['requiredcapability']; | |
794 | $update = true; | |
795 | } | |
796 | if ($dbservice->restrictedusers != $service['restrictedusers']) { | |
797 | $dbservice->restrictedusers = $service['restrictedusers']; | |
798 | $update = true; | |
799 | } | |
800 | if ($update) { | |
801 | $DB->update_record('external_services', $dbservice); | |
802 | } | |
803 | ||
804 | $functions = $DB->get_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); | |
805 | foreach ($functions as $function) { | |
806 | $key = array_search($function->functionname, $service['functions']); | |
807 | if ($key === false) { | |
808 | $DB->delete_records('external_services_functions', array('id'=>$function->id)); | |
809 | } else { | |
810 | unset($service['functions'][$key]); | |
811 | } | |
812 | } | |
813 | foreach ($service['functions'] as $fname) { | |
814 | $newf = new object(); | |
815 | $newf->externalserviceid = $dbservice->id; | |
816 | $newf->functionname = $fname; | |
817 | $DB->insert_record('external_services_functions', $newf); | |
818 | } | |
819 | unset($functions); | |
820 | } | |
821 | foreach ($services as $name => $service) { | |
822 | $dbservice = new object(); | |
823 | $dbservice->name = $name; | |
824 | $dbservice->enabled = empty($service['enabled']) ? 0 : $service['enabled']; | |
825 | $dbservice->requiredcapability = empty($service['requiredcapability']) ? null : $service['requiredcapability']; | |
826 | $dbservice->restrictedusers = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; | |
827 | $dbservice->component = $component; | |
e5180580 | 828 | $dbservice->timecreated = time(); |
c976e271 | 829 | $dbservice->id = $DB->insert_record('external_services', $dbservice); |
830 | foreach ($service['functions'] as $fname) { | |
831 | $newf = new object(); | |
832 | $newf->externalserviceid = $dbservice->id; | |
833 | $newf->functionname = $fname; | |
834 | $DB->insert_record('external_services_functions', $newf); | |
835 | } | |
836 | } | |
837 | } | |
838 | ||
839 | /** | |
840 | * Delete all service and external functions information defined in the specified compoment. | |
841 | * @param string $component name of component (moodle, mod_assignment, etc.) | |
842 | * @return void | |
843 | */ | |
844 | function external_delete_descriptions($component) { | |
845 | global $DB; | |
846 | ||
847 | $params = array($component); | |
848 | ||
849 | $DB->delete_records_select('external_services_users', "externalserviceid IN (SELECT id FROM {external_services} WHERE component = ?)", $params); | |
850 | $DB->delete_records_select('external_services_functions', "externalserviceid IN (SELECT id FROM {external_services} WHERE component = ?)", $params); | |
851 | $DB->delete_records('external_services', array('component'=>$component)); | |
852 | $DB->delete_records('external_functions', array('component'=>$component)); | |
853 | } | |
854 | ||
72fb21b6 | 855 | /** |
856 | * upgrade logging functions | |
72fb21b6 | 857 | */ |
fd1a792e | 858 | function upgrade_handle_exception($ex, $plugin = null) { |
a56c457e PS |
859 | // rollback everything, we need to log all upgrade problems |
860 | abort_all_db_transactions(); | |
861 | ||
c19bc39c PS |
862 | $info = get_exception_info($ex); |
863 | ||
864 | // First log upgrade error | |
865 | upgrade_log(UPGRADE_LOG_ERROR, $plugin, 'Exception: ' . get_class($ex), $info->message, $info->backtrace); | |
866 | ||
867 | // Always turn on debugging - admins need to know what is going on | |
868 | $CFG->debug = DEBUG_DEVELOPER; | |
869 | ||
fd1a792e | 870 | default_exception_handler($ex, true, $plugin); |
db9d4a3d | 871 | } |
872 | ||
873 | /** | |
874 | * Adds log entry into upgrade_log table | |
875 | * | |
72fb21b6 | 876 | * @global object |
877 | * @global object | |
878 | * @global object | |
db9d4a3d | 879 | * @param int $type UPGRADE_LOG_NORMAL, UPGRADE_LOG_NOTICE or UPGRADE_LOG_ERROR |
880 | * @param string $plugin plugin or null if main | |
881 | * @param string $info short description text of log entry | |
882 | * @param string $details long problem description | |
883 | * @param string $backtrace string used for errors only | |
884 | * @return void | |
885 | */ | |
886 | function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { | |
887 | global $DB, $USER, $CFG; | |
888 | ||
795a08ad | 889 | $plugin = ($plugin==='moodle') ? null : $plugin; |
890 | ||
34a2777c | 891 | $backtrace = format_backtrace($backtrace, true); |
db9d4a3d | 892 | |
893 | $version = null; | |
894 | ||
895 | //first try to find out current version number | |
795a08ad | 896 | if (empty($plugin) or $plugin === 'moodle') { |
db9d4a3d | 897 | //main |
898 | $version = $CFG->version; | |
899 | ||
795a08ad | 900 | } else if ($plugin === 'local') { |
901 | //customisation | |
902 | $version = $CFG->local_version; | |
903 | ||
904 | } else if (strpos($plugin, 'mod/') === 0) { | |
db9d4a3d | 905 | try { |
906 | $modname = substr($plugin, strlen('mod/')); | |
907 | $version = $DB->get_field('modules', 'version', array('name'=>$modname)); | |
795a08ad | 908 | $version = ($version === false) ? null : $version; |
db9d4a3d | 909 | } catch (Exception $ignored) { |
910 | } | |
911 | ||
795a08ad | 912 | } else if (strpos($plugin, 'block/') === 0) { |
db9d4a3d | 913 | try { |
795a08ad | 914 | $blockname = substr($plugin, strlen('block/')); |
db9d4a3d | 915 | if ($block = $DB->get_record('block', array('name'=>$blockname))) { |
916 | $version = $block->version; | |
917 | } | |
918 | } catch (Exception $ignored) { | |
919 | } | |
795a08ad | 920 | |
921 | } else { | |
922 | $pluginversion = get_config(str_replace('/', '_', $plugin), 'version'); | |
923 | if (!empty($pluginversion)) { | |
924 | $version = $pluginversion; | |
925 | } | |
db9d4a3d | 926 | } |
927 | ||
928 | $log = new object(); | |
929 | $log->type = $type; | |
930 | $log->plugin = $plugin; | |
931 | $log->version = $version; | |
932 | $log->info = $info; | |
933 | $log->details = $details; | |
934 | $log->backtrace = $backtrace; | |
935 | $log->userid = $USER->id; | |
936 | $log->timemodified = time(); | |
db9d4a3d | 937 | try { |
938 | $DB->insert_record('upgrade_log', $log); | |
939 | } catch (Exception $ignored) { | |
795a08ad | 940 | // possible during install or 2.0 upgrade |
db9d4a3d | 941 | } |
942 | } | |
943 | ||
944 | /** | |
945 | * Marks start of upgrade, blocks any other access to site. | |
946 | * The upgrade is finished at the end of script or after timeout. | |
72fb21b6 | 947 | * |
948 | * @global object | |
949 | * @global object | |
950 | * @global object | |
db9d4a3d | 951 | */ |
952 | function upgrade_started($preinstall=false) { | |
de6d81e6 | 953 | global $CFG, $DB, $PAGE, $OUTPUT; |
db9d4a3d | 954 | |
955 | static $started = false; | |
956 | ||
957 | if ($preinstall) { | |
958 | ignore_user_abort(true); | |
959 | upgrade_setup_debug(true); | |
960 | ||
961 | } else if ($started) { | |
962 | upgrade_set_timeout(120); | |
963 | ||
964 | } else { | |
c13a5e71 | 965 | if (!CLI_SCRIPT and !$PAGE->headerprinted) { |
db9d4a3d | 966 | $strupgrade = get_string('upgradingversion', 'admin'); |
78946b9b | 967 | $PAGE->set_pagelayout('maintenance'); |
543f54d3 | 968 | upgrade_init_javascript(); |
de6d81e6 | 969 | $PAGE->set_title($strupgrade.' - Moodle '.$CFG->target_release); |
970 | $PAGE->set_heading($strupgrade); | |
971 | $PAGE->navbar->add($strupgrade); | |
972 | $PAGE->set_cacheable(false); | |
973 | echo $OUTPUT->header(); | |
db9d4a3d | 974 | } |
975 | ||
976 | ignore_user_abort(true); | |
977 | register_shutdown_function('upgrade_finished_handler'); | |
978 | upgrade_setup_debug(true); | |
979 | set_config('upgraderunning', time()+300); | |
980 | $started = true; | |
981 | } | |
982 | } | |
983 | ||
984 | /** | |
985 | * Internal function - executed if upgrade interruped. | |
986 | */ | |
987 | function upgrade_finished_handler() { | |
988 | upgrade_finished(); | |
989 | } | |
990 | ||
991 | /** | |
992 | * Indicates upgrade is finished. | |
993 | * | |
994 | * This function may be called repeatedly. | |
72fb21b6 | 995 | * |
996 | * @global object | |
997 | * @global object | |
db9d4a3d | 998 | */ |
999 | function upgrade_finished($continueurl=null) { | |
7e0d6675 | 1000 | global $CFG, $DB, $OUTPUT; |
db9d4a3d | 1001 | |
1002 | if (!empty($CFG->upgraderunning)) { | |
1003 | unset_config('upgraderunning'); | |
1004 | upgrade_setup_debug(false); | |
1005 | ignore_user_abort(false); | |
1006 | if ($continueurl) { | |
aa9a6867 | 1007 | echo $OUTPUT->continue_button($continueurl); |
7e0d6675 | 1008 | echo $OUTPUT->footer(); |
db9d4a3d | 1009 | die; |
1010 | } | |
1011 | } | |
1012 | } | |
1013 | ||
72fb21b6 | 1014 | /** |
1015 | * @global object | |
1016 | * @global object | |
1017 | */ | |
db9d4a3d | 1018 | function upgrade_setup_debug($starting) { |
1019 | global $CFG, $DB; | |
1020 | ||
1021 | static $originaldebug = null; | |
1022 | ||
1023 | if ($starting) { | |
1024 | if ($originaldebug === null) { | |
1025 | $originaldebug = $DB->get_debug(); | |
1026 | } | |
1027 | if (!empty($CFG->upgradeshowsql)) { | |
1028 | $DB->set_debug(true); | |
1029 | } | |
1030 | } else { | |
1031 | $DB->set_debug($originaldebug); | |
1032 | } | |
1033 | } | |
1034 | ||
72fb21b6 | 1035 | /** |
1036 | * @global object | |
1037 | */ | |
90509582 | 1038 | function print_upgrade_reload($url) { |
f2a1963c | 1039 | global $OUTPUT; |
90509582 | 1040 | |
1041 | echo "<br />"; | |
1042 | echo '<div class="continuebutton">'; | |
b5d0cafc | 1043 | echo '<a href="'.$url.'" title="'.get_string('reload').'" ><img src="'.$OUTPUT->pix_url('i/reload') . '" alt="" /> '.get_string('reload').'</a>'; |
90509582 | 1044 | echo '</div><br />'; |
1045 | } | |
1046 | ||
db9d4a3d | 1047 | function print_upgrade_separator() { |
1048 | if (!CLI_SCRIPT) { | |
1049 | echo '<hr />'; | |
1050 | } | |
1051 | } | |
1052 | ||
795a08ad | 1053 | /** |
1054 | * Default start upgrade callback | |
1055 | * @param string $plugin | |
1056 | * @param bool $installation true if installation, false menas upgrade | |
1057 | */ | |
194cdca8 | 1058 | function print_upgrade_part_start($plugin, $installation, $verbose) { |
3c159385 | 1059 | global $OUTPUT; |
795a08ad | 1060 | if (empty($plugin) or $plugin == 'moodle') { |
1061 | upgrade_started($installation); // does not store upgrade running flag yet | |
194cdca8 | 1062 | if ($verbose) { |
3c159385 | 1063 | echo $OUTPUT->heading(get_string('coresystem')); |
194cdca8 | 1064 | } |
795a08ad | 1065 | } else { |
1066 | upgrade_started(); | |
194cdca8 | 1067 | if ($verbose) { |
3c159385 | 1068 | echo $OUTPUT->heading($plugin); |
194cdca8 | 1069 | } |
795a08ad | 1070 | } |
1071 | if ($installation) { | |
1072 | if (empty($plugin) or $plugin == 'moodle') { | |
1073 | // no need to log - log table not yet there ;-) | |
1074 | } else { | |
1075 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin installation'); | |
1076 | } | |
1077 | } else { | |
1078 | if (empty($plugin) or $plugin == 'moodle') { | |
1079 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting core upgrade'); | |
1080 | } else { | |
1081 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin upgrade'); | |
1082 | } | |
1083 | } | |
1084 | } | |
1085 | ||
1086 | /** | |
1087 | * Default end upgrade callback | |
1088 | * @param string $plugin | |
1089 | * @param bool $installation true if installation, false menas upgrade | |
1090 | */ | |
194cdca8 | 1091 | function print_upgrade_part_end($plugin, $installation, $verbose) { |
aa9a6867 | 1092 | global $OUTPUT; |
795a08ad | 1093 | upgrade_started(); |
1094 | if ($installation) { | |
1095 | if (empty($plugin) or $plugin == 'moodle') { | |
1096 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core installed'); | |
1097 | } else { | |
1098 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin installed'); | |
1099 | } | |
1100 | } else { | |
1101 | if (empty($plugin) or $plugin == 'moodle') { | |
1102 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core upgraded'); | |
1103 | } else { | |
1104 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin upgraded'); | |
1105 | } | |
1106 | } | |
194cdca8 | 1107 | if ($verbose) { |
aa9a6867 | 1108 | echo $OUTPUT->notification(get_string('success'), 'notifysuccess'); |
194cdca8 | 1109 | print_upgrade_separator(); |
96db14f9 | 1110 | } |
1111 | } | |
1112 | ||
72fb21b6 | 1113 | /** |
543f54d3 | 1114 | * Sets up JS code required for all ugprade scripts. |
72fb21b6 | 1115 | * @global object |
1116 | */ | |
543f54d3 | 1117 | function upgrade_init_javascript() { |
e29380f3 | 1118 | global $PAGE; |
543f54d3 PS |
1119 | // scroll to the end of each upgrade page so that ppl see either error or continue button, |
1120 | // no need to scroll continuously any more, it is enough to jump to end once the footer is printed ;-) | |
1121 | $js = "window.scrollTo(0, 5000000);"; | |
1122 | $PAGE->requires->js_init_code($js); | |
db9d4a3d | 1123 | } |
1124 | ||
1125 | ||
1126 | /** | |
1127 | * Try to upgrade the given language pack (or current language) | |
72fb21b6 | 1128 | * @global object |
db9d4a3d | 1129 | */ |
1130 | function upgrade_language_pack($lang='') { | |
3c159385 | 1131 | global $CFG, $OUTPUT; |
db9d4a3d | 1132 | |
1133 | if (empty($lang)) { | |
1134 | $lang = current_language(); | |
1135 | } | |
1136 | ||
1137 | if ($lang == 'en_utf8') { | |
1138 | return true; // Nothing to do | |
1139 | } | |
1140 | ||
551fe0e5 | 1141 | upgrade_started(false); |
3c159385 | 1142 | echo $OUTPUT->heading(get_string('langimport', 'admin').': '.$lang); |
db9d4a3d | 1143 | |
1144 | @mkdir ($CFG->dataroot.'/temp/'); //make it in case it's a fresh install, it might not be there | |
1145 | @mkdir ($CFG->dataroot.'/lang/'); | |
1146 | ||
1147 | require_once($CFG->libdir.'/componentlib.class.php'); | |
1148 | ||
1149 | if ($cd = new component_installer('http://download.moodle.org', 'lang16', $lang.'.zip', 'languages.md5', 'lang')) { | |
1150 | $status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED) | |
1151 | ||
1152 | if ($status == COMPONENT_INSTALLED) { | |
db9d4a3d | 1153 | @unlink($CFG->dataroot.'/cache/languages'); |
551fe0e5 | 1154 | if ($parentlang = get_parent_language($lang)) { |
1155 | if ($cd = new component_installer('http://download.moodle.org', 'lang16', $parentlang.'.zip', 'languages.md5', 'lang')) { | |
1156 | $cd->install(); | |
1157 | } | |
1158 | } | |
aa9a6867 | 1159 | echo $OUTPUT->notification(get_string('success'), 'notifysuccess'); |
db9d4a3d | 1160 | } |
1161 | } | |
1162 | ||
551fe0e5 | 1163 | print_upgrade_separator(); |
db9d4a3d | 1164 | } |
8580535b | 1165 | |
1166 | /** | |
1167 | * Install core moodle tables and initialize | |
1168 | * @param float $version target version | |
1169 | * @param bool $verbose | |
1170 | * @return void, may throw exception | |
1171 | */ | |
1172 | function install_core($version, $verbose) { | |
1173 | global $CFG, $DB; | |
1174 | ||
1175 | try { | |
c3d0e149 | 1176 | set_time_limit(600); |
194cdca8 | 1177 | print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag |
8580535b | 1178 | |
1179 | $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml"); | |
1180 | upgrade_started(); // we want the flag to be stored in config table ;-) | |
1181 | ||
1182 | // set all core default records and default settings | |
1183 | require_once("$CFG->libdir/db/install.php"); | |
1184 | xmldb_main_install(); | |
1185 | ||
1186 | // store version | |
1187 | upgrade_main_savepoint(true, $version, false); | |
1188 | ||
1189 | // Continue with the instalation | |
1190 | events_update_definition('moodle'); | |
1191 | message_update_providers('moodle'); | |
8580535b | 1192 | |
1193 | // Write default settings unconditionlly | |
1194 | admin_apply_default_settings(NULL, true); | |
1195 | ||
194cdca8 | 1196 | print_upgrade_part_end(null, true, $verbose); |
8580535b | 1197 | } catch (exception $ex) { |
1198 | upgrade_handle_exception($ex); | |
1199 | } | |
1200 | } | |
1201 | ||
1202 | /** | |
1203 | * Upgrade moodle core | |
1204 | * @param float $version target version | |
1205 | * @param bool $verbose | |
1206 | * @return void, may throw exception | |
1207 | */ | |
1208 | function upgrade_core($version, $verbose) { | |
1209 | global $CFG; | |
1210 | ||
3316fe24 | 1211 | require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades |
3316fe24 | 1212 | |
8580535b | 1213 | try { |
1214 | // Upgrade current language pack if we can | |
1215 | if (empty($CFG->skiplangupgrade)) { | |
1216 | upgrade_language_pack(false); | |
1217 | } | |
1218 | ||
194cdca8 | 1219 | print_upgrade_part_start('moodle', false, $verbose); |
8580535b | 1220 | |
17da2e6f | 1221 | // one time special local migration pre 2.0 upgrade script |
1222 | if ($version < 2007101600) { | |
1223 | $pre20upgradefile = "$CFG->dirrot/local/upgrade_pre20.php"; | |
1224 | if (file_exists($pre20upgradefile)) { | |
1225 | set_time_limit(0); | |
1226 | require($pre20upgradefile); | |
1227 | // reset upgrade timeout to default | |
1228 | upgrade_set_timeout(); | |
1229 | } | |
1230 | } | |
1231 | ||
8580535b | 1232 | $result = xmldb_main_upgrade($CFG->version); |
1233 | if ($version > $CFG->version) { | |
1234 | // store version if not already there | |
1235 | upgrade_main_savepoint($result, $version, false); | |
1236 | } | |
1237 | ||
1238 | // perform all other component upgrade routines | |
1239 | update_capabilities('moodle'); | |
c976e271 | 1240 | external_update_descriptions('moodle'); |
8580535b | 1241 | events_update_definition('moodle'); |
1242 | message_update_providers('moodle'); | |
8580535b | 1243 | |
0139ec3f | 1244 | upgrade_reset_caches(); |
8580535b | 1245 | remove_dir($CFG->dataroot . '/cache', true); // flush cache |
1246 | ||
194cdca8 | 1247 | print_upgrade_part_end('moodle', false, $verbose); |
8580535b | 1248 | } catch (Exception $ex) { |
1249 | upgrade_handle_exception($ex); | |
1250 | } | |
1251 | } | |
1252 | ||
1253 | /** | |
1254 | * Upgrade/install other parts of moodle | |
1255 | * @param bool $verbose | |
1256 | * @return void, may throw exception | |
1257 | */ | |
1258 | function upgrade_noncore($verbose) { | |
1259 | global $CFG; | |
1260 | ||
8580535b | 1261 | // upgrade all plugins types |
1262 | try { | |
1263 | $plugintypes = get_plugin_types(); | |
1264 | foreach ($plugintypes as $type=>$location) { | |
17da2e6f | 1265 | upgrade_plugins($type, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose); |
8580535b | 1266 | } |
1267 | } catch (Exception $ex) { | |
1268 | upgrade_handle_exception($ex); | |
1269 | } | |
8580535b | 1270 | } |
3316fe24 | 1271 | |
1272 | /** | |
1273 | * Checks if the main tables have been installed yet or not. | |
1274 | * @return bool | |
1275 | */ | |
1276 | function core_tables_exist() { | |
1277 | global $DB; | |
1278 | ||
1279 | if (!$tables = $DB->get_tables() ) { // No tables yet at all. | |
1280 | return false; | |
9b683d13 | 1281 | |
3316fe24 | 1282 | } else { // Check for missing main tables |
1283 | $mtables = array('config', 'course', 'groupings'); // some tables used in 1.9 and 2.0, preferable something from the start and end of install.xml | |
1284 | foreach ($mtables as $mtable) { | |
1285 | if (!in_array($mtable, $tables)) { | |
1286 | return false; | |
1287 | } | |
1288 | } | |
1289 | return true; | |
9b683d13 | 1290 | } |
ba04999c | 1291 | } |
c71ade2f | 1292 | |
0139ec3f PS |
1293 | /** |
1294 | * Invalidates browser caches and cached data in temp | |
1295 | * @return void | |
1296 | */ | |
1297 | function upgrade_reset_caches() { | |
1298 | js_reset_all_caches(); | |
1299 | theme_reset_all_caches(); | |
1300 | } | |
1301 | ||
c71ade2f PL |
1302 | /** |
1303 | * upgrades the mnet rpc definitions for the given component. | |
1304 | * this method doesn't return status, an exception will be thrown in the case of an error | |
1305 | * | |
1306 | * @param string $component the plugin to upgrade, eg auth_mnet | |
1307 | */ | |
1308 | function upgrade_plugin_mnet_functions($component) { | |
1309 | global $DB, $CFG; | |
1310 | ||
1311 | list($type, $plugin) = explode('_', $component); | |
1312 | $path = get_plugin_directory($type, $plugin); | |
1313 | ||
1314 | if (file_exists($path . '/db/mnet.php')) { | |
1315 | require_once($path . '/db/mnet.php'); // $publishes comes from this file | |
1316 | } | |
1317 | if (empty($publishes)) { | |
1318 | $publishes = array(); // still need this to be able to disable stuff later | |
1319 | } | |
1320 | if (empty($subscribes)) { | |
1321 | $subscribes = array(); // still need this to be able to disable stuff later | |
1322 | } | |
1323 | ||
1324 | static $servicecache = array(); | |
1325 | ||
1326 | // rekey an array based on the rpc method for easy lookups later | |
1327 | $publishmethodservices = array(); | |
1328 | $subscribemethodservices = array(); | |
1329 | foreach($publishes as $servicename => $service) { | |
1330 | if (is_array($service['methods'])) { | |
1331 | foreach($service['methods'] as $methodname) { | |
1332 | $service['servicename'] = $servicename; | |
1333 | $publishmethodservices[$methodname][] = $service; | |
1334 | } | |
1335 | } | |
1336 | } | |
1337 | ||
1338 | // Disable functions that don't exist (any more) in the source | |
1339 | // Should these be deleted? What about their permissions records? | |
1340 | foreach ($DB->get_records('mnet_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { | |
1341 | if (!array_key_exists($rpc->functionname, $publishmethodservices) && $rpc->enabled) { | |
1342 | $DB->set_field('mnet_rpc', 'enabled', 0, array('id' => $rpc->id)); | |
1343 | } else if (array_key_exists($rpc->functionname, $publishmethodservices) && !$rpc->enabled) { | |
1344 | $DB->set_field('mnet_rpc', 'enabled', 1, array('id' => $rpc->id)); | |
1345 | } | |
1346 | } | |
1347 | ||
1348 | // reflect all the services we're publishing and save them | |
1349 | require_once($CFG->dirroot . '/lib/zend/Zend/Server/Reflection.php'); | |
1350 | static $cachedclasses = array(); // to store reflection information in | |
1351 | foreach ($publishes as $service => $data) { | |
1352 | $f = $data['filename']; | |
1353 | $c = $data['classname']; | |
1354 | foreach ($data['methods'] as $method) { | |
1355 | $dataobject = new stdclass; | |
1356 | $dataobject->plugintype = $type; | |
1357 | $dataobject->pluginname = $plugin; | |
1358 | $dataobject->enabled = 1; | |
1359 | $dataobject->classname = $c; | |
1360 | $dataobject->filename = $f; | |
1361 | ||
1362 | if (is_string($method)) { | |
1363 | $dataobject->functionname = $method; | |
1364 | ||
1365 | } else if (is_array($method)) { // wants to override file or class | |
1366 | $dataobject->functionname = $method['method']; | |
1367 | $dataobject->classname = $method['classname']; | |
1368 | $dataobject->filename = $method['filename']; | |
1369 | } | |
1370 | $dataobject->xmlrpcpath = $type.'/'.$plugin.'/'.$dataobject->filename.'/'.$method; | |
1371 | $dataobject->static = false; | |
1372 | ||
1373 | require_once($path . '/' . $dataobject->filename); | |
1374 | $functionreflect = null; // slightly different ways to get this depending on whether it's a class method or a function | |
1375 | if (!empty($dataobject->classname)) { | |
1376 | if (!class_exists($dataobject->classname)) { | |
1377 | throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); | |
1378 | } | |
1379 | $key = $dataobject->filename . '|' . $dataobject->classname; | |
1380 | if (!array_key_exists($key, $cachedclasses)) { // look to see if we've already got a reflection object | |
1381 | try { | |
1382 | $cachedclasses[$key] = Zend_Server_Reflection::reflectClass($dataobject->classname); | |
1383 | } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful | |
1384 | throw new moodle_exception('installreflectionclasserror', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname, 'error' => $e->getMessage())); | |
1385 | } | |
1386 | } | |
1387 | $r =& $cachedclasses[$key]; | |
1388 | if (!$r->hasMethod($dataobject->functionname)) { | |
1389 | throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); | |
1390 | } | |
1391 | // stupid workaround for zend not having a getMethod($name) function | |
1392 | $ms = $r->getMethods(); | |
1393 | foreach ($ms as $m) { | |
1394 | if ($m->getName() == $dataobject->functionname) { | |
1395 | $functionreflect = $m; | |
1396 | break; | |
1397 | } | |
1398 | } | |
1399 | $dataobject->static = (int)$functionreflect->isStatic(); | |
1400 | } else { | |
1401 | if (!function_exists($dataobject->functionname)) { | |
1402 | throw new moodle_exception('installnosuchfunction', 'mnet', '', (object)array('method' => $dataobject->functionname, 'file' => $dataobject->filename)); | |
1403 | } | |
1404 | try { | |
1405 | $functionreflect = Zend_Server_Reflection::reflectFunction($dataobject->functionname); | |
1406 | } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful | |
1407 | throw new moodle_exception('installreflectionfunctionerror', 'mnet', '', (object)array('method' => $dataobject->functionname, '' => $dataobject->filename, 'error' => $e->getMessage())); | |
1408 | } | |
1409 | } | |
1410 | $dataobject->profile = serialize(admin_mnet_method_profile($functionreflect)); | |
1411 | $dataobject->help = $functionreflect->getDescription(); | |
1412 | ||
1413 | if ($record_exists = $DB->get_record('mnet_rpc', array('xmlrpcpath'=>$dataobject->xmlrpcpath))) { | |
1414 | $dataobject->id = $record_exists->id; | |
1415 | $dataobject->enabled = $record_exists->enabled; | |
1416 | $DB->update_record('mnet_rpc', $dataobject); | |
1417 | } else { | |
1418 | $dataobject->id = $DB->insert_record('mnet_rpc', $dataobject, true); | |
1419 | } | |
1420 | } | |
1421 | ||
1422 | foreach ($publishmethodservices[$dataobject->functionname] as $service) { | |
1423 | if ($serviceobj = $DB->get_record('mnet_service', array('name'=>$service['servicename']))) { | |
1424 | $serviceobj->apiversion = $service['apiversion']; | |
1425 | $DB->update_record('mnet_service', $serviceobj); | |
1426 | } else { | |
1427 | $serviceobj = new stdClass(); | |
1428 | $serviceobj->name = $service['servicename']; | |
1429 | $serviceobj->apiversion = $service['apiversion']; | |
1430 | $serviceobj->offer = 1; | |
1431 | $serviceobj->id = $DB->insert_record('mnet_service', $serviceobj); | |
1432 | } | |
1433 | $servicecache[$service['servicename']] = $serviceobj; | |
1434 | if (!$DB->record_exists('mnet_service2rpc', array('rpcid'=>$dataobject->id, 'serviceid'=>$serviceobj->id))) { | |
1435 | $obj = new stdClass(); | |
1436 | $obj->rpcid = $dataobject->id; | |
1437 | $obj->serviceid = $serviceobj->id; | |
1438 | $DB->insert_record('mnet_service2rpc', $obj, true); | |
1439 | } | |
1440 | } | |
1441 | } | |
1442 | ||
1443 | // finished with methods we publish, now do subscribable methods | |
1444 | foreach($subscribes as $service => $methods) { | |
1445 | if (!array_key_exists($service, $servicecache)) { | |
1446 | if (!$serviceobj = $DB->get_record('mnet_service', array('name' => $service))) { | |
1447 | debugging("skipping unknown service $service"); | |
1448 | continue; | |
1449 | } | |
1450 | $servicecache[$service] = $serviceobj; | |
1451 | } else { | |
1452 | $serviceobj = $servicecache[$service]; | |
1453 | } | |
1454 | foreach ($methods as $method => $xmlrpcpath) { | |
1455 | if (!$rpcid = $DB->get_field('mnet_remote_rpc', 'id', array('xmlrpcpath'=>$xmlrpcpath))) { | |
1456 | $remoterpc = (object)array( | |
1457 | 'functionname' => $method, | |
1458 | 'xmlrpcpath' => $xmlrpcpath, | |
1459 | 'plugintype' => $type, | |
1460 | 'pluginname' => $plugin, | |
1461 | 'enabled' => 1, | |
1462 | ); | |
1463 | $rpcid = $remoterpc->id = $DB->insert_record('mnet_remote_rpc', $remoterpc, true); | |
1464 | } | |
1465 | if (!$DB->record_exists('mnet_remote_service2rpc', array('rpcid'=>$rpcid, 'serviceid'=>$serviceobj->id))) { | |
1466 | $obj = new stdClass(); | |
1467 | $obj->rpcid = $rpcid; | |
1468 | $obj->serviceid = $serviceobj->id; | |
1469 | $DB->insert_record('mnet_remote_service2rpc', $obj, true); | |
1470 | } | |
1471 | $subscribemethodservices[$method][] = $servicename; | |
1472 | } | |
1473 | } | |
1474 | ||
1475 | foreach ($DB->get_records('mnet_remote_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { | |
1476 | if (!array_key_exists($rpc->functionname, $subscribemethodservices) && $rpc->enabled) { | |
1477 | $DB->set_field('mnet_remote_rpc', 'enabled', 0, array('id' => $rpc->id)); | |
1478 | } else if (array_key_exists($rpc->functionname, $subscribemethodservices) && !$rpc->enabled) { | |
1479 | $DB->set_field('mnet_remote_rpc', 'enabled', 1, array('id' => $rpc->id)); | |
1480 | } | |
1481 | } | |
1482 | ||
1483 | return true; | |
1484 | } | |
1485 | ||
1486 | /** | |
1487 | * Given some sort of Zend Reflection function/method object, return a profile array, ready to be serialized and stored | |
1488 | * | |
1489 | * @param Zend_Server_Reflection_Function_Abstract $function can be any subclass of this object type | |
1490 | * | |
1491 | * @return array | |
1492 | */ | |
1493 | function admin_mnet_method_profile(Zend_Server_Reflection_Function_Abstract $function) { | |
1494 | $proto = array_pop($function->getPrototypes()); | |
1495 | $ret = $proto->getReturnValue(); | |
1496 | $profile = array( | |
1497 | 'parameters' => array(), | |
1498 | 'return' => array( | |
1499 | 'type' => $ret->getType(), | |
1500 | 'description' => $ret->getDescription(), | |
1501 | ), | |
1502 | ); | |
1503 | foreach ($proto->getParameters() as $p) { | |
1504 | $profile['parameters'][] = array( | |
1505 | 'name' => $p->getName(), | |
1506 | 'type' => $p->getType(), | |
1507 | 'description' => $p->getDescription(), | |
1508 | ); | |
1509 | } | |
1510 | return $profile; | |
1511 | } |