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 | * |
78bfb562 | 21 | * @package core |
96db14f9 | 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 | ||
78bfb562 PS |
27 | defined('MOODLE_INTERNAL') || die(); |
28 | ||
72fb21b6 | 29 | /** UPGRADE_LOG_NORMAL = 0 */ |
db9d4a3d | 30 | define('UPGRADE_LOG_NORMAL', 0); |
72fb21b6 | 31 | /** UPGRADE_LOG_NOTICE = 1 */ |
db9d4a3d | 32 | define('UPGRADE_LOG_NOTICE', 1); |
72fb21b6 | 33 | /** UPGRADE_LOG_ERROR = 2 */ |
795a08ad | 34 | define('UPGRADE_LOG_ERROR', 2); |
35 | ||
36 | /** | |
37 | * Exception indicating unknown error during upgrade. | |
72fb21b6 | 38 | * |
d32fb550 | 39 | * @package core |
72fb21b6 | 40 | * @subpackage upgrade |
d32fb550 | 41 | * @copyright 2009 Petr Skoda {@link http://skodak.org} |
72fb21b6 | 42 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
795a08ad | 43 | */ |
44 | class upgrade_exception extends moodle_exception { | |
4f0c2d00 | 45 | function __construct($plugin, $version, $debuginfo=NULL) { |
795a08ad | 46 | global $CFG; |
47 | $a = (object)array('plugin'=>$plugin, 'version'=>$version); | |
4f0c2d00 | 48 | parent::__construct('upgradeerror', 'admin', "$CFG->wwwroot/$CFG->admin/index.php", $a, $debuginfo); |
795a08ad | 49 | } |
50 | } | |
51 | ||
52 | /** | |
53 | * Exception indicating downgrade error during upgrade. | |
72fb21b6 | 54 | * |
d32fb550 | 55 | * @package core |
72fb21b6 | 56 | * @subpackage upgrade |
d32fb550 | 57 | * @copyright 2009 Petr Skoda {@link http://skodak.org} |
72fb21b6 | 58 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
795a08ad | 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 | ||
72fb21b6 | 69 | /** |
d32fb550 | 70 | * @package core |
72fb21b6 | 71 | * @subpackage upgrade |
d32fb550 | 72 | * @copyright 2009 Petr Skoda {@link http://skodak.org} |
72fb21b6 | 73 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
74 | */ | |
795a08ad | 75 | class upgrade_requires_exception extends moodle_exception { |
76 | function __construct($plugin, $pluginversion, $currentmoodle, $requiremoodle) { | |
77 | global $CFG; | |
365a5941 | 78 | $a = new stdClass(); |
795a08ad | 79 | $a->pluginname = $plugin; |
80 | $a->pluginversion = $pluginversion; | |
81 | $a->currentmoodle = $currentmoodle; | |
17da2e6f | 82 | $a->requiremoodle = $requiremoodle; |
795a08ad | 83 | parent::__construct('pluginrequirementsnotmet', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a); |
84 | } | |
85 | } | |
86 | ||
72fb21b6 | 87 | /** |
d32fb550 | 88 | * @package core |
72fb21b6 | 89 | * @subpackage upgrade |
d32fb550 | 90 | * @copyright 2009 Petr Skoda {@link http://skodak.org} |
72fb21b6 | 91 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
92 | */ | |
795a08ad | 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 | } | |
db9d4a3d | 99 | |
db9d4a3d | 100 | /** |
101 | * Upgrade savepoint, marks end of each upgrade block. | |
102 | * It stores new main version, resets upgrade timeout | |
103 | * and abort upgrade if user cancels page loading. | |
104 | * | |
105 | * Please do not make large upgrade blocks with lots of operations, | |
106 | * for example when adding tables keep only one table operation per block. | |
107 | * | |
72fb21b6 | 108 | * @global object |
db9d4a3d | 109 | * @param bool $result false if upgrade step failed, true if completed |
110 | * @param string or float $version main version | |
111 | * @param bool $allowabort allow user to abort script execution here | |
112 | * @return void | |
113 | */ | |
114 | function upgrade_main_savepoint($result, $version, $allowabort=true) { | |
115 | global $CFG; | |
116 | ||
795a08ad | 117 | if (!$result) { |
520cea92 | 118 | throw new upgrade_exception(null, $version); |
795a08ad | 119 | } |
120 | ||
121 | if ($CFG->version >= $version) { | |
122 | // something really wrong is going on in main upgrade script | |
123 | throw new downgrade_exception(null, $CFG->version, $version); | |
db9d4a3d | 124 | } |
125 | ||
795a08ad | 126 | set_config('version', $version); |
127 | upgrade_log(UPGRADE_LOG_NORMAL, null, 'Upgrade savepoint reached'); | |
128 | ||
db9d4a3d | 129 | // reset upgrade timeout to default |
130 | upgrade_set_timeout(); | |
131 | ||
132 | // this is a safe place to stop upgrades if user aborts page loading | |
133 | if ($allowabort and connection_aborted()) { | |
134 | die; | |
135 | } | |
136 | } | |
137 | ||
138 | /** | |
139 | * Module upgrade savepoint, marks end of module upgrade blocks | |
140 | * It stores module version, resets upgrade timeout | |
141 | * and abort upgrade if user cancels page loading. | |
142 | * | |
72fb21b6 | 143 | * @global object |
db9d4a3d | 144 | * @param bool $result false if upgrade step failed, true if completed |
145 | * @param string or float $version main version | |
146 | * @param string $modname name of module | |
147 | * @param bool $allowabort allow user to abort script execution here | |
148 | * @return void | |
149 | */ | |
150 | function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { | |
151 | global $DB; | |
152 | ||
795a08ad | 153 | if (!$result) { |
520cea92 | 154 | throw new upgrade_exception("mod_$modname", $version); |
795a08ad | 155 | } |
156 | ||
db9d4a3d | 157 | if (!$module = $DB->get_record('modules', array('name'=>$modname))) { |
158 | print_error('modulenotexist', 'debug', '', $modname); | |
159 | } | |
160 | ||
795a08ad | 161 | if ($module->version >= $version) { |
162 | // something really wrong is going on in upgrade script | |
520cea92 | 163 | throw new downgrade_exception("mod_$modname", $module->version, $version); |
db9d4a3d | 164 | } |
795a08ad | 165 | $module->version = $version; |
166 | $DB->update_record('modules', $module); | |
520cea92 | 167 | upgrade_log(UPGRADE_LOG_NORMAL, "mod_$modname", 'Upgrade savepoint reached'); |
db9d4a3d | 168 | |
169 | // reset upgrade timeout to default | |
170 | upgrade_set_timeout(); | |
171 | ||
172 | // this is a safe place to stop upgrades if user aborts page loading | |
173 | if ($allowabort and connection_aborted()) { | |
174 | die; | |
175 | } | |
176 | } | |
177 | ||
178 | /** | |
179 | * Blocks upgrade savepoint, marks end of blocks upgrade blocks | |
180 | * It stores block version, resets upgrade timeout | |
181 | * and abort upgrade if user cancels page loading. | |
182 | * | |
72fb21b6 | 183 | * @global object |
db9d4a3d | 184 | * @param bool $result false if upgrade step failed, true if completed |
185 | * @param string or float $version main version | |
186 | * @param string $blockname name of block | |
187 | * @param bool $allowabort allow user to abort script execution here | |
188 | * @return void | |
189 | */ | |
795a08ad | 190 | function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) { |
db9d4a3d | 191 | global $DB; |
192 | ||
795a08ad | 193 | if (!$result) { |
520cea92 | 194 | throw new upgrade_exception("block_$blockname", $version); |
795a08ad | 195 | } |
196 | ||
db9d4a3d | 197 | if (!$block = $DB->get_record('block', array('name'=>$blockname))) { |
198 | print_error('blocknotexist', 'debug', '', $blockname); | |
199 | } | |
200 | ||
795a08ad | 201 | if ($block->version >= $version) { |
202 | // something really wrong is going on in upgrade script | |
520cea92 | 203 | throw new downgrade_exception("block_$blockname", $block->version, $version); |
db9d4a3d | 204 | } |
795a08ad | 205 | $block->version = $version; |
206 | $DB->update_record('block', $block); | |
520cea92 | 207 | upgrade_log(UPGRADE_LOG_NORMAL, "block_$blockname", 'Upgrade savepoint reached'); |
db9d4a3d | 208 | |
209 | // reset upgrade timeout to default | |
210 | upgrade_set_timeout(); | |
211 | ||
212 | // this is a safe place to stop upgrades if user aborts page loading | |
213 | if ($allowabort and connection_aborted()) { | |
214 | die; | |
215 | } | |
216 | } | |
217 | ||
218 | /** | |
219 | * Plugins upgrade savepoint, marks end of blocks upgrade blocks | |
220 | * It stores plugin version, resets upgrade timeout | |
221 | * and abort upgrade if user cancels page loading. | |
222 | * | |
223 | * @param bool $result false if upgrade step failed, true if completed | |
224 | * @param string or float $version main version | |
225 | * @param string $type name of plugin | |
226 | * @param string $dir location of plugin | |
227 | * @param bool $allowabort allow user to abort script execution here | |
228 | * @return void | |
229 | */ | |
17da2e6f | 230 | function upgrade_plugin_savepoint($result, $version, $type, $plugin, $allowabort=true) { |
231 | $component = $type.'_'.$plugin; | |
232 | ||
795a08ad | 233 | if (!$result) { |
17da2e6f | 234 | throw new upgrade_exception($component, $version); |
db9d4a3d | 235 | } |
236 | ||
17da2e6f | 237 | $installedversion = get_config($component, 'version'); |
795a08ad | 238 | if ($installedversion >= $version) { |
239 | // Something really wrong is going on in the upgrade script | |
240 | throw new downgrade_exception($component, $installedversion, $version); | |
241 | } | |
17da2e6f | 242 | set_config('version', $version, $component); |
795a08ad | 243 | upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); |
244 | ||
db9d4a3d | 245 | // Reset upgrade timeout to default |
246 | upgrade_set_timeout(); | |
247 | ||
248 | // This is a safe place to stop upgrades if user aborts page loading | |
249 | if ($allowabort and connection_aborted()) { | |
250 | die; | |
251 | } | |
252 | } | |
253 | ||
254 | ||
255 | /** | |
256 | * Upgrade plugins | |
db9d4a3d | 257 | * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype') |
17da2e6f | 258 | * return void |
db9d4a3d | 259 | */ |
17da2e6f | 260 | function upgrade_plugins($type, $startcallback, $endcallback, $verbose) { |
db9d4a3d | 261 | global $CFG, $DB; |
262 | ||
263 | /// special cases | |
264 | if ($type === 'mod') { | |
194cdca8 | 265 | return upgrade_plugins_modules($startcallback, $endcallback, $verbose); |
795a08ad | 266 | } else if ($type === 'block') { |
194cdca8 | 267 | return upgrade_plugins_blocks($startcallback, $endcallback, $verbose); |
db9d4a3d | 268 | } |
269 | ||
17da2e6f | 270 | $plugs = get_plugin_list($type); |
db9d4a3d | 271 | |
17da2e6f | 272 | foreach ($plugs as $plug=>$fullplug) { |
273 | $component = $type.'_'.$plug; // standardised plugin name | |
db9d4a3d | 274 | |
3e858ea7 PS |
275 | // check plugin dir is valid name |
276 | $cplug = strtolower($plug); | |
277 | $cplug = clean_param($cplug, PARAM_SAFEDIR); | |
278 | $cplug = str_replace('-', '', $cplug); | |
279 | if ($plug !== $cplug) { | |
280 | throw new plugin_defective_exception($component, 'Invalid plugin directory name.'); | |
281 | } | |
282 | ||
795a08ad | 283 | if (!is_readable($fullplug.'/version.php')) { |
284 | continue; | |
db9d4a3d | 285 | } |
286 | ||
365a5941 | 287 | $plugin = new stdClass(); |
795a08ad | 288 | require($fullplug.'/version.php'); // defines $plugin with version etc |
db9d4a3d | 289 | |
3e858ea7 PS |
290 | // if plugin tells us it's full name we may check the location |
291 | if (isset($plugin->component)) { | |
292 | if ($plugin->component !== $component) { | |
293 | throw new plugin_defective_exception($component, 'Plugin installed in wrong folder.'); | |
294 | } | |
295 | } | |
296 | ||
795a08ad | 297 | if (empty($plugin->version)) { |
298 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); | |
db9d4a3d | 299 | } |
300 | ||
17da2e6f | 301 | $plugin->name = $plug; |
302 | $plugin->fullname = $component; | |
795a08ad | 303 | |
304 | ||
db9d4a3d | 305 | if (!empty($plugin->requires)) { |
306 | if ($plugin->requires > $CFG->version) { | |
795a08ad | 307 | throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); |
ef14c1e7 PS |
308 | } else if ($plugin->requires < 2010000000) { |
309 | throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); | |
db9d4a3d | 310 | } |
311 | } | |
312 | ||
9b683d13 | 313 | // try to recover from interrupted install.php if needed |
314 | if (file_exists($fullplug.'/db/install.php')) { | |
315 | if (get_config($plugin->fullname, 'installrunning')) { | |
316 | require_once($fullplug.'/db/install.php'); | |
317 | $recover_install_function = 'xmldb_'.$plugin->fullname.'_install_recovery'; | |
318 | if (function_exists($recover_install_function)) { | |
319 | $startcallback($component, true, $verbose); | |
320 | $recover_install_function(); | |
f6f06d69 | 321 | unset_config('installrunning', $plugin->fullname); |
9b683d13 | 322 | update_capabilities($component); |
c6d75bff | 323 | log_update_descriptions($component); |
c976e271 | 324 | external_update_descriptions($component); |
9b683d13 | 325 | events_update_definition($component); |
326 | message_update_providers($component); | |
de260e0f | 327 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 328 | $endcallback($component, true, $verbose); |
329 | } | |
330 | } | |
331 | } | |
db9d4a3d | 332 | |
9b683d13 | 333 | $installedversion = get_config($plugin->fullname, 'version'); |
795a08ad | 334 | if (empty($installedversion)) { // new installation |
194cdca8 | 335 | $startcallback($component, true, $verbose); |
db9d4a3d | 336 | |
795a08ad | 337 | /// Install tables if defined |
338 | if (file_exists($fullplug.'/db/install.xml')) { | |
339 | $DB->get_manager()->install_from_xmldb_file($fullplug.'/db/install.xml'); | |
db9d4a3d | 340 | } |
9b683d13 | 341 | |
342 | /// store version | |
343 | upgrade_plugin_savepoint(true, $plugin->version, $type, $plug, false); | |
344 | ||
795a08ad | 345 | /// execute post install file |
346 | if (file_exists($fullplug.'/db/install.php')) { | |
347 | require_once($fullplug.'/db/install.php'); | |
f6f06d69 PS |
348 | set_config('installrunning', 1, $plugin->fullname); |
349 | $post_install_function = 'xmldb_'.$plugin->fullname.'_install'; | |
795a08ad | 350 | $post_install_function(); |
f6f06d69 | 351 | unset_config('installrunning', $plugin->fullname); |
795a08ad | 352 | } |
353 | ||
795a08ad | 354 | /// Install various components |
355 | update_capabilities($component); | |
c6d75bff | 356 | log_update_descriptions($component); |
c976e271 | 357 | external_update_descriptions($component); |
795a08ad | 358 | events_update_definition($component); |
359 | message_update_providers($component); | |
de260e0f | 360 | upgrade_plugin_mnet_functions($component); |
795a08ad | 361 | |
f87eab7e | 362 | purge_all_caches(); |
194cdca8 | 363 | $endcallback($component, true, $verbose); |
795a08ad | 364 | |
365 | } else if ($installedversion < $plugin->version) { // upgrade | |
366 | /// Run the upgrade function for the plugin. | |
194cdca8 | 367 | $startcallback($component, false, $verbose); |
795a08ad | 368 | |
369 | if (is_readable($fullplug.'/db/upgrade.php')) { | |
370 | require_once($fullplug.'/db/upgrade.php'); // defines upgrading function | |
371 | ||
372 | $newupgrade_function = 'xmldb_'.$plugin->fullname.'_upgrade'; | |
373 | $result = $newupgrade_function($installedversion); | |
374 | } else { | |
375 | $result = true; | |
376 | } | |
377 | ||
378 | $installedversion = get_config($plugin->fullname, 'version'); | |
379 | if ($installedversion < $plugin->version) { | |
380 | // store version if not already there | |
381 | upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false); | |
382 | } | |
383 | ||
384 | /// Upgrade various components | |
385 | update_capabilities($component); | |
c6d75bff | 386 | log_update_descriptions($component); |
c976e271 | 387 | external_update_descriptions($component); |
795a08ad | 388 | events_update_definition($component); |
389 | message_update_providers($component); | |
de260e0f | 390 | upgrade_plugin_mnet_functions($component); |
795a08ad | 391 | |
f87eab7e | 392 | purge_all_caches(); |
194cdca8 | 393 | $endcallback($component, false, $verbose); |
795a08ad | 394 | |
395 | } else if ($installedversion > $plugin->version) { | |
396 | throw new downgrade_exception($component, $installedversion, $plugin->version); | |
db9d4a3d | 397 | } |
398 | } | |
db9d4a3d | 399 | } |
400 | ||
401 | /** | |
402 | * Find and check all modules and load them up or upgrade them if necessary | |
72fb21b6 | 403 | * |
404 | * @global object | |
405 | * @global object | |
db9d4a3d | 406 | */ |
194cdca8 | 407 | function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { |
db9d4a3d | 408 | global $CFG, $DB; |
409 | ||
17da2e6f | 410 | $mods = get_plugin_list('mod'); |
db9d4a3d | 411 | |
17da2e6f | 412 | foreach ($mods as $mod=>$fullmod) { |
db9d4a3d | 413 | |
3e858ea7 | 414 | if ($mod === 'NEWMODULE') { // Someone has unzipped the template, ignore it |
db9d4a3d | 415 | continue; |
416 | } | |
417 | ||
17da2e6f | 418 | $component = 'mod_'.$mod; |
db9d4a3d | 419 | |
3e858ea7 PS |
420 | // check module dir is valid name |
421 | $cmod = strtolower($mod); | |
422 | $cmod = clean_param($cmod, PARAM_SAFEDIR); | |
423 | $cmod = str_replace('-', '', $cmod); | |
424 | $cmod = str_replace('_', '', $cmod); // modules MUST not have '_' in name and never will, sorry | |
425 | if ($mod !== $cmod) { | |
426 | throw new plugin_defective_exception($component, 'Invalid plugin directory name.'); | |
427 | } | |
428 | ||
795a08ad | 429 | if (!is_readable($fullmod.'/version.php')) { |
430 | throw new plugin_defective_exception($component, 'Missing version.php'); | |
db9d4a3d | 431 | } |
432 | ||
365a5941 | 433 | $module = new stdClass(); |
795a08ad | 434 | require($fullmod .'/version.php'); // defines $module with version etc |
db9d4a3d | 435 | |
3e858ea7 PS |
436 | // if plugin tells us it's full name we may check the location |
437 | if (isset($module->component)) { | |
438 | if ($module->component !== $component) { | |
439 | throw new plugin_defective_exception($component, 'Plugin installed in wrong folder.'); | |
440 | } | |
441 | } | |
442 | ||
795a08ad | 443 | if (empty($module->version)) { |
c1fe2368 | 444 | if (isset($module->version)) { |
445 | // Version is empty but is set - it means its value is 0 or ''. Let us skip such module. | |
b921b6ee | 446 | // This is intended for developers so they can work on the early stages of the module. |
c1fe2368 | 447 | continue; |
448 | } | |
795a08ad | 449 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); |
db9d4a3d | 450 | } |
451 | ||
452 | if (!empty($module->requires)) { | |
453 | if ($module->requires > $CFG->version) { | |
795a08ad | 454 | throw new upgrade_requires_exception($component, $module->version, $CFG->version, $module->requires); |
ef14c1e7 PS |
455 | } else if ($module->requires < 2010000000) { |
456 | throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); | |
db9d4a3d | 457 | } |
458 | } | |
459 | ||
3e858ea7 PS |
460 | // all modules must have en lang pack |
461 | if (!is_readable("$fullmod/lang/en/$mod.php")) { | |
462 | throw new plugin_defective_exception($component, 'Missing mandatory en language pack.'); | |
463 | } | |
464 | ||
db9d4a3d | 465 | $module->name = $mod; // The name MUST match the directory |
466 | ||
795a08ad | 467 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); |
db9d4a3d | 468 | |
9b683d13 | 469 | if (file_exists($fullmod.'/db/install.php')) { |
470 | if (get_config($module->name, 'installrunning')) { | |
471 | require_once($fullmod.'/db/install.php'); | |
472 | $recover_install_function = 'xmldb_'.$module->name.'_install_recovery'; | |
473 | if (function_exists($recover_install_function)) { | |
474 | $startcallback($component, true, $verbose); | |
475 | $recover_install_function(); | |
476 | unset_config('installrunning', $module->name); | |
477 | // Install various components too | |
478 | update_capabilities($component); | |
c6d75bff | 479 | log_update_descriptions($component); |
c976e271 | 480 | external_update_descriptions($component); |
9b683d13 | 481 | events_update_definition($component); |
482 | message_update_providers($component); | |
de260e0f | 483 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 484 | $endcallback($component, true, $verbose); |
485 | } | |
486 | } | |
487 | } | |
488 | ||
795a08ad | 489 | if (empty($currmodule->version)) { |
194cdca8 | 490 | $startcallback($component, true, $verbose); |
db9d4a3d | 491 | |
795a08ad | 492 | /// Execute install.xml (XMLDB) - must be present in all modules |
493 | $DB->get_manager()->install_from_xmldb_file($fullmod.'/db/install.xml'); | |
db9d4a3d | 494 | |
2e3da297 | 495 | /// Add record into modules table - may be needed in install.php already |
496 | $module->id = $DB->insert_record('modules', $module); | |
497 | ||
db9d4a3d | 498 | /// Post installation hook - optional |
499 | if (file_exists("$fullmod/db/install.php")) { | |
500 | require_once("$fullmod/db/install.php"); | |
9b683d13 | 501 | // Set installation running flag, we need to recover after exception or error |
502 | set_config('installrunning', 1, $module->name); | |
db9d4a3d | 503 | $post_install_function = 'xmldb_'.$module->name.'_install';; |
504 | $post_install_function(); | |
9b683d13 | 505 | unset_config('installrunning', $module->name); |
db9d4a3d | 506 | } |
507 | ||
795a08ad | 508 | /// Install various components |
509 | update_capabilities($component); | |
c6d75bff | 510 | log_update_descriptions($component); |
c976e271 | 511 | external_update_descriptions($component); |
795a08ad | 512 | events_update_definition($component); |
513 | message_update_providers($component); | |
de260e0f | 514 | upgrade_plugin_mnet_functions($component); |
795a08ad | 515 | |
f87eab7e | 516 | purge_all_caches(); |
194cdca8 | 517 | $endcallback($component, true, $verbose); |
db9d4a3d | 518 | |
795a08ad | 519 | } else if ($currmodule->version < $module->version) { |
520 | /// If versions say that we need to upgrade but no upgrade files are available, notify and continue | |
194cdca8 | 521 | $startcallback($component, false, $verbose); |
795a08ad | 522 | |
523 | if (is_readable($fullmod.'/db/upgrade.php')) { | |
524 | require_once($fullmod.'/db/upgrade.php'); // defines new upgrading function | |
525 | $newupgrade_function = 'xmldb_'.$module->name.'_upgrade'; | |
526 | $result = $newupgrade_function($currmodule->version, $module); | |
527 | } else { | |
528 | $result = true; | |
529 | } | |
530 | ||
531 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); | |
532 | if ($currmodule->version < $module->version) { | |
533 | // store version if not already there | |
534 | upgrade_mod_savepoint($result, $module->version, $mod, false); | |
535 | } | |
536 | ||
537 | /// Upgrade various components | |
538 | update_capabilities($component); | |
c6d75bff | 539 | log_update_descriptions($component); |
c976e271 | 540 | external_update_descriptions($component); |
795a08ad | 541 | events_update_definition($component); |
542 | message_update_providers($component); | |
de260e0f | 543 | upgrade_plugin_mnet_functions($component); |
795a08ad | 544 | |
f87eab7e | 545 | purge_all_caches(); |
795a08ad | 546 | |
194cdca8 | 547 | $endcallback($component, false, $verbose); |
795a08ad | 548 | |
549 | } else if ($currmodule->version > $module->version) { | |
550 | throw new downgrade_exception($component, $currmodule->version, $module->version); | |
551 | } | |
552 | } | |
553 | } | |
db9d4a3d | 554 | |
db9d4a3d | 555 | |
795a08ad | 556 | /** |
557 | * This function finds all available blocks and install them | |
558 | * into blocks table or do all the upgrade process if newer. | |
72fb21b6 | 559 | * |
560 | * @global object | |
561 | * @global object | |
795a08ad | 562 | */ |
194cdca8 | 563 | function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { |
795a08ad | 564 | global $CFG, $DB; |
565 | ||
795a08ad | 566 | require_once($CFG->dirroot.'/blocks/moodleblock.class.php'); |
567 | ||
568 | $blocktitles = array(); // we do not want duplicate titles | |
569 | ||
570 | //Is this a first install | |
571 | $first_install = null; | |
572 | ||
17da2e6f | 573 | $blocks = get_plugin_list('block'); |
795a08ad | 574 | |
17da2e6f | 575 | foreach ($blocks as $blockname=>$fullblock) { |
795a08ad | 576 | |
577 | if (is_null($first_install)) { | |
f46eba7e | 578 | $first_install = ($DB->count_records('block_instances') == 0); |
795a08ad | 579 | } |
580 | ||
581 | if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it | |
582 | continue; | |
db9d4a3d | 583 | } |
584 | ||
17da2e6f | 585 | $component = 'block_'.$blockname; |
db9d4a3d | 586 | |
3e858ea7 PS |
587 | // check block dir is valid name |
588 | $cblockname = strtolower($blockname); | |
589 | $cblockname = clean_param($cblockname, PARAM_SAFEDIR); | |
590 | $cblockname = str_replace('-', '', $cblockname); | |
591 | if ($blockname !== $cblockname) { | |
592 | throw new plugin_defective_exception($component, 'Invalid plugin directory name.'); | |
593 | } | |
594 | ||
8571833f PS |
595 | if (!is_readable($fullblock.'/version.php')) { |
596 | throw new plugin_defective_exception('block/'.$blockname, 'Missing version.php file.'); | |
597 | } | |
365a5941 | 598 | $plugin = new stdClass(); |
8571833f PS |
599 | $plugin->version = NULL; |
600 | $plugin->cron = 0; | |
601 | include($fullblock.'/version.php'); | |
602 | $block = $plugin; | |
603 | ||
3e858ea7 PS |
604 | // if plugin tells us it's full name we may check the location |
605 | if (isset($block->component)) { | |
606 | if ($block->component !== $component) { | |
607 | throw new plugin_defective_exception($component, 'Plugin installed in wrong folder.'); | |
608 | } | |
609 | } | |
610 | ||
ef14c1e7 PS |
611 | if (!empty($plugin->requires)) { |
612 | if ($plugin->requires > $CFG->version) { | |
613 | throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); | |
614 | } else if ($plugin->requires < 2010000000) { | |
615 | throw new plugin_defective_exception($component, 'Plugin is not compatible with Moodle 2.x or later.'); | |
616 | } | |
617 | } | |
618 | ||
795a08ad | 619 | if (!is_readable($fullblock.'/block_'.$blockname.'.php')) { |
620 | throw new plugin_defective_exception('block/'.$blockname, 'Missing main block class file.'); | |
db9d4a3d | 621 | } |
8571833f | 622 | include_once($fullblock.'/block_'.$blockname.'.php'); |
db9d4a3d | 623 | |
795a08ad | 624 | $classname = 'block_'.$blockname; |
625 | ||
626 | if (!class_exists($classname)) { | |
627 | throw new plugin_defective_exception($component, 'Can not load main class.'); | |
628 | } | |
629 | ||
b921b6ee | 630 | $blockobj = new $classname; // This is what we'll be testing |
795a08ad | 631 | $blocktitle = $blockobj->get_title(); |
632 | ||
633 | // OK, it's as we all hoped. For further tests, the object will do them itself. | |
634 | if (!$blockobj->_self_test()) { | |
635 | throw new plugin_defective_exception($component, 'Self test failed.'); | |
636 | } | |
637 | ||
795a08ad | 638 | $block->name = $blockname; // The name MUST match the directory |
795a08ad | 639 | |
640 | if (empty($block->version)) { | |
641 | throw new plugin_defective_exception($component, 'Missing block version.'); | |
642 | } | |
643 | ||
644 | $currblock = $DB->get_record('block', array('name'=>$block->name)); | |
645 | ||
9b683d13 | 646 | if (file_exists($fullblock.'/db/install.php')) { |
647 | if (get_config('block_'.$blockname, 'installrunning')) { | |
648 | require_once($fullblock.'/db/install.php'); | |
649 | $recover_install_function = 'xmldb_block_'.$blockname.'_install_recovery'; | |
650 | if (function_exists($recover_install_function)) { | |
651 | $startcallback($component, true, $verbose); | |
652 | $recover_install_function(); | |
653 | unset_config('installrunning', 'block_'.$blockname); | |
654 | // Install various components | |
655 | update_capabilities($component); | |
c6d75bff | 656 | log_update_descriptions($component); |
c976e271 | 657 | external_update_descriptions($component); |
9b683d13 | 658 | events_update_definition($component); |
659 | message_update_providers($component); | |
de260e0f | 660 | upgrade_plugin_mnet_functions($component); |
9b683d13 | 661 | $endcallback($component, true, $verbose); |
662 | } | |
663 | } | |
664 | } | |
665 | ||
795a08ad | 666 | if (empty($currblock->version)) { // block not installed yet, so install it |
795a08ad | 667 | $conflictblock = array_search($blocktitle, $blocktitles); |
668 | if ($conflictblock !== false) { | |
669 | // Duplicate block titles are not allowed, they confuse people | |
670 | // AND PHP's associative arrays ;) | |
c1ddac83 | 671 | throw new plugin_defective_exception($component, get_string('blocknameconflict', 'error', (object)array('name'=>$block->name, 'conflict'=>$conflictblock))); |
795a08ad | 672 | } |
194cdca8 | 673 | $startcallback($component, true, $verbose); |
795a08ad | 674 | |
675 | if (file_exists($fullblock.'/db/install.xml')) { | |
676 | $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml'); | |
677 | } | |
678 | $block->id = $DB->insert_record('block', $block); | |
679 | ||
680 | if (file_exists($fullblock.'/db/install.php')) { | |
681 | require_once($fullblock.'/db/install.php'); | |
9b683d13 | 682 | // Set installation running flag, we need to recover after exception or error |
683 | set_config('installrunning', 1, 'block_'.$blockname); | |
795a08ad | 684 | $post_install_function = 'xmldb_block_'.$blockname.'_install';; |
685 | $post_install_function(); | |
9b683d13 | 686 | unset_config('installrunning', 'block_'.$blockname); |
795a08ad | 687 | } |
688 | ||
689 | $blocktitles[$block->name] = $blocktitle; | |
690 | ||
691 | // Install various components | |
692 | update_capabilities($component); | |
c6d75bff | 693 | log_update_descriptions($component); |
c976e271 | 694 | external_update_descriptions($component); |
795a08ad | 695 | events_update_definition($component); |
696 | message_update_providers($component); | |
de260e0f | 697 | upgrade_plugin_mnet_functions($component); |
795a08ad | 698 | |
f87eab7e | 699 | purge_all_caches(); |
194cdca8 | 700 | $endcallback($component, true, $verbose); |
795a08ad | 701 | |
702 | } else if ($currblock->version < $block->version) { | |
194cdca8 | 703 | $startcallback($component, false, $verbose); |
795a08ad | 704 | |
705 | if (is_readable($fullblock.'/db/upgrade.php')) { | |
706 | require_once($fullblock.'/db/upgrade.php'); // defines new upgrading function | |
707 | $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade'; | |
708 | $result = $newupgrade_function($currblock->version, $block); | |
709 | } else { | |
710 | $result = true; | |
711 | } | |
712 | ||
713 | $currblock = $DB->get_record('block', array('name'=>$block->name)); | |
714 | if ($currblock->version < $block->version) { | |
715 | // store version if not already there | |
716 | upgrade_block_savepoint($result, $block->version, $block->name, false); | |
717 | } | |
718 | ||
719 | if ($currblock->cron != $block->cron) { | |
720 | // update cron flag if needed | |
721 | $currblock->cron = $block->cron; | |
722 | $DB->update_record('block', $currblock); | |
723 | } | |
724 | ||
b921b6ee | 725 | // Upgrade various components |
795a08ad | 726 | update_capabilities($component); |
c6d75bff | 727 | log_update_descriptions($component); |
c976e271 | 728 | external_update_descriptions($component); |
17da2e6f | 729 | events_update_definition($component); |
795a08ad | 730 | message_update_providers($component); |
de260e0f | 731 | upgrade_plugin_mnet_functions($component); |
795a08ad | 732 | |
f87eab7e | 733 | purge_all_caches(); |
194cdca8 | 734 | $endcallback($component, false, $verbose); |
795a08ad | 735 | |
736 | } else if ($currblock->version > $block->version) { | |
737 | throw new downgrade_exception($component, $currblock->version, $block->version); | |
738 | } | |
739 | } | |
740 | ||
741 | ||
742 | // Finally, if we are in the first_install of BLOCKS setup frontpage and admin page blocks | |
743 | if ($first_install) { | |
795a08ad | 744 | //Iterate over each course - there should be only site course here now |
745 | if ($courses = $DB->get_records('course')) { | |
746 | foreach ($courses as $course) { | |
9d1d606e | 747 | blocks_add_default_course_blocks($course); |
db9d4a3d | 748 | } |
749 | } | |
795a08ad | 750 | |
9d1d606e | 751 | blocks_add_default_system_blocks(); |
795a08ad | 752 | } |
753 | } | |
754 | ||
c6d75bff PS |
755 | |
756 | /** | |
757 | * Log_display description function used during install and upgrade. | |
758 | * | |
759 | * @param string $component name of component (moodle, mod_assignment, etc.) | |
760 | * @return void | |
761 | */ | |
762 | function log_update_descriptions($component) { | |
763 | global $DB; | |
764 | ||
765 | $defpath = get_component_directory($component).'/db/log.php'; | |
766 | ||
767 | if (!file_exists($defpath)) { | |
768 | $DB->delete_records('log_display', array('component'=>$component)); | |
769 | return; | |
770 | } | |
771 | ||
772 | // load new info | |
773 | $logs = array(); | |
774 | include($defpath); | |
775 | $newlogs = array(); | |
776 | foreach ($logs as $log) { | |
777 | $newlogs[$log['module'].'-'.$log['action']] = $log; // kind of unique name | |
778 | } | |
779 | unset($logs); | |
780 | $logs = $newlogs; | |
781 | ||
782 | $fields = array('module', 'action', 'mtable', 'field'); | |
783 | // update all log fist | |
784 | $dblogs = $DB->get_records('log_display', array('component'=>$component)); | |
785 | foreach ($dblogs as $dblog) { | |
786 | $name = $dblog->module.'-'.$dblog->action; | |
787 | ||
788 | if (empty($logs[$name])) { | |
789 | $DB->delete_records('log_display', array('id'=>$dblog->id)); | |
790 | continue; | |
791 | } | |
792 | ||
793 | $log = $logs[$name]; | |
794 | unset($logs[$name]); | |
795 | ||
796 | $update = false; | |
797 | foreach ($fields as $field) { | |
798 | if ($dblog->$field != $log[$field]) { | |
799 | $dblog->$field = $log[$field]; | |
800 | $update = true; | |
801 | } | |
802 | } | |
803 | if ($update) { | |
804 | $DB->update_record('log_display', $dblog); | |
805 | } | |
806 | } | |
807 | foreach ($logs as $log) { | |
808 | $dblog = (object)$log; | |
809 | $dblog->component = $component; | |
810 | $DB->insert_record('log_display', $dblog); | |
811 | } | |
812 | } | |
813 | ||
c976e271 | 814 | /** |
815 | * Web service discovery function used during install and upgrade. | |
816 | * @param string $component name of component (moodle, mod_assignment, etc.) | |
817 | * @return void | |
818 | */ | |
819 | function external_update_descriptions($component) { | |
820 | global $DB; | |
821 | ||
822 | $defpath = get_component_directory($component).'/db/services.php'; | |
823 | ||
824 | if (!file_exists($defpath)) { | |
825 | external_delete_descriptions($component); | |
826 | return; | |
827 | } | |
828 | ||
829 | // load new info | |
830 | $functions = array(); | |
831 | $services = array(); | |
832 | include($defpath); | |
833 | ||
834 | // update all function fist | |
835 | $dbfunctions = $DB->get_records('external_functions', array('component'=>$component)); | |
836 | foreach ($dbfunctions as $dbfunction) { | |
837 | if (empty($functions[$dbfunction->name])) { | |
838 | $DB->delete_records('external_functions', array('id'=>$dbfunction->id)); | |
839 | // do not delete functions from external_services_functions, beacuse | |
840 | // we want to notify admins when functions used in custom services disappear | |
c6d75bff PS |
841 | |
842 | //TODO: this looks wrong, we have to delete it eventually (skodak) | |
c976e271 | 843 | continue; |
844 | } | |
845 | ||
846 | $function = $functions[$dbfunction->name]; | |
847 | unset($functions[$dbfunction->name]); | |
848 | $function['classpath'] = empty($function['classpath']) ? null : $function['classpath']; | |
849 | ||
850 | $update = false; | |
851 | if ($dbfunction->classname != $function['classname']) { | |
852 | $dbfunction->classname = $function['classname']; | |
853 | $update = true; | |
854 | } | |
855 | if ($dbfunction->methodname != $function['methodname']) { | |
856 | $dbfunction->methodname = $function['methodname']; | |
857 | $update = true; | |
858 | } | |
859 | if ($dbfunction->classpath != $function['classpath']) { | |
860 | $dbfunction->classpath = $function['classpath']; | |
861 | $update = true; | |
862 | } | |
72f68b51 | 863 | $functioncapabilities = key_exists('capabilities', $function)?$function['capabilities']:''; |
864 | if ($dbfunction->capabilities != $functioncapabilities) { | |
865 | $dbfunction->capabilities = $functioncapabilities; | |
866 | $update = true; | |
867 | } | |
c976e271 | 868 | if ($update) { |
869 | $DB->update_record('external_functions', $dbfunction); | |
870 | } | |
871 | } | |
872 | foreach ($functions as $fname => $function) { | |
365a5941 | 873 | $dbfunction = new stdClass(); |
c976e271 | 874 | $dbfunction->name = $fname; |
875 | $dbfunction->classname = $function['classname']; | |
876 | $dbfunction->methodname = $function['methodname']; | |
877 | $dbfunction->classpath = empty($function['classpath']) ? null : $function['classpath']; | |
878 | $dbfunction->component = $component; | |
72f68b51 | 879 | $dbfunction->capabilities = key_exists('capabilities', $function)?$function['capabilities']:''; |
c976e271 | 880 | $dbfunction->id = $DB->insert_record('external_functions', $dbfunction); |
881 | } | |
882 | unset($functions); | |
883 | ||
884 | // now deal with services | |
885 | $dbservices = $DB->get_records('external_services', array('component'=>$component)); | |
886 | foreach ($dbservices as $dbservice) { | |
887 | if (empty($services[$dbservice->name])) { | |
888 | $DB->delete_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); | |
889 | $DB->delete_records('external_services_users', array('externalserviceid'=>$dbservice->id)); | |
890 | $DB->delete_records('external_services', array('id'=>$dbservice->id)); | |
891 | continue; | |
892 | } | |
893 | $service = $services[$dbservice->name]; | |
894 | unset($services[$dbservice->name]); | |
895 | $service['enabled'] = empty($service['enabled']) ? 0 : $service['enabled']; | |
896 | $service['requiredcapability'] = empty($service['requiredcapability']) ? null : $service['requiredcapability']; | |
897 | $service['restrictedusers'] = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; | |
898 | ||
899 | $update = false; | |
900 | if ($dbservice->enabled != $service['enabled']) { | |
901 | $dbservice->enabled = $service['enabled']; | |
902 | $update = true; | |
903 | } | |
904 | if ($dbservice->requiredcapability != $service['requiredcapability']) { | |
905 | $dbservice->requiredcapability = $service['requiredcapability']; | |
906 | $update = true; | |
907 | } | |
908 | if ($dbservice->restrictedusers != $service['restrictedusers']) { | |
909 | $dbservice->restrictedusers = $service['restrictedusers']; | |
910 | $update = true; | |
911 | } | |
912 | if ($update) { | |
913 | $DB->update_record('external_services', $dbservice); | |
914 | } | |
915 | ||
916 | $functions = $DB->get_records('external_services_functions', array('externalserviceid'=>$dbservice->id)); | |
917 | foreach ($functions as $function) { | |
918 | $key = array_search($function->functionname, $service['functions']); | |
919 | if ($key === false) { | |
920 | $DB->delete_records('external_services_functions', array('id'=>$function->id)); | |
921 | } else { | |
922 | unset($service['functions'][$key]); | |
923 | } | |
924 | } | |
925 | foreach ($service['functions'] as $fname) { | |
365a5941 | 926 | $newf = new stdClass(); |
c976e271 | 927 | $newf->externalserviceid = $dbservice->id; |
928 | $newf->functionname = $fname; | |
929 | $DB->insert_record('external_services_functions', $newf); | |
930 | } | |
931 | unset($functions); | |
932 | } | |
933 | foreach ($services as $name => $service) { | |
365a5941 | 934 | $dbservice = new stdClass(); |
c976e271 | 935 | $dbservice->name = $name; |
936 | $dbservice->enabled = empty($service['enabled']) ? 0 : $service['enabled']; | |
937 | $dbservice->requiredcapability = empty($service['requiredcapability']) ? null : $service['requiredcapability']; | |
938 | $dbservice->restrictedusers = !isset($service['restrictedusers']) ? 1 : $service['restrictedusers']; | |
939 | $dbservice->component = $component; | |
e5180580 | 940 | $dbservice->timecreated = time(); |
c976e271 | 941 | $dbservice->id = $DB->insert_record('external_services', $dbservice); |
942 | foreach ($service['functions'] as $fname) { | |
365a5941 | 943 | $newf = new stdClass(); |
c976e271 | 944 | $newf->externalserviceid = $dbservice->id; |
945 | $newf->functionname = $fname; | |
946 | $DB->insert_record('external_services_functions', $newf); | |
947 | } | |
948 | } | |
949 | } | |
950 | ||
951 | /** | |
b921b6ee | 952 | * Delete all service and external functions information defined in the specified component. |
c976e271 | 953 | * @param string $component name of component (moodle, mod_assignment, etc.) |
954 | * @return void | |
955 | */ | |
956 | function external_delete_descriptions($component) { | |
957 | global $DB; | |
958 | ||
959 | $params = array($component); | |
960 | ||
961 | $DB->delete_records_select('external_services_users', "externalserviceid IN (SELECT id FROM {external_services} WHERE component = ?)", $params); | |
962 | $DB->delete_records_select('external_services_functions', "externalserviceid IN (SELECT id FROM {external_services} WHERE component = ?)", $params); | |
963 | $DB->delete_records('external_services', array('component'=>$component)); | |
964 | $DB->delete_records('external_functions', array('component'=>$component)); | |
965 | } | |
966 | ||
72fb21b6 | 967 | /** |
968 | * upgrade logging functions | |
72fb21b6 | 969 | */ |
fd1a792e | 970 | function upgrade_handle_exception($ex, $plugin = null) { |
96e1fdf4 PS |
971 | global $CFG; |
972 | ||
a56c457e PS |
973 | // rollback everything, we need to log all upgrade problems |
974 | abort_all_db_transactions(); | |
975 | ||
c19bc39c PS |
976 | $info = get_exception_info($ex); |
977 | ||
978 | // First log upgrade error | |
979 | upgrade_log(UPGRADE_LOG_ERROR, $plugin, 'Exception: ' . get_class($ex), $info->message, $info->backtrace); | |
980 | ||
981 | // Always turn on debugging - admins need to know what is going on | |
982 | $CFG->debug = DEBUG_DEVELOPER; | |
983 | ||
fd1a792e | 984 | default_exception_handler($ex, true, $plugin); |
db9d4a3d | 985 | } |
986 | ||
987 | /** | |
988 | * Adds log entry into upgrade_log table | |
989 | * | |
990 | * @param int $type UPGRADE_LOG_NORMAL, UPGRADE_LOG_NOTICE or UPGRADE_LOG_ERROR | |
bb8b2971 | 991 | * @param string $plugin frankenstyle component name |
db9d4a3d | 992 | * @param string $info short description text of log entry |
993 | * @param string $details long problem description | |
994 | * @param string $backtrace string used for errors only | |
995 | * @return void | |
996 | */ | |
997 | function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { | |
998 | global $DB, $USER, $CFG; | |
999 | ||
bb8b2971 PS |
1000 | if (empty($plugin)) { |
1001 | $plugin = 'core'; | |
1002 | } | |
1003 | ||
1004 | list($plugintype, $pluginname) = normalize_component($plugin); | |
1005 | $component = is_null($pluginname) ? $plugintype : $plugintype . '_' . $pluginname; | |
795a08ad | 1006 | |
34a2777c | 1007 | $backtrace = format_backtrace($backtrace, true); |
db9d4a3d | 1008 | |
bb8b2971 PS |
1009 | $currentversion = null; |
1010 | $targetversion = null; | |
db9d4a3d | 1011 | |
1012 | //first try to find out current version number | |
bb8b2971 | 1013 | if ($plugintype === 'core') { |
db9d4a3d | 1014 | //main |
bb8b2971 | 1015 | $currentversion = $CFG->version; |
db9d4a3d | 1016 | |
bb8b2971 PS |
1017 | $version = null; |
1018 | include("$CFG->dirroot/version.php"); | |
1019 | $targetversion = $version; | |
795a08ad | 1020 | |
bb8b2971 | 1021 | } else if ($plugintype === 'mod') { |
db9d4a3d | 1022 | try { |
bb8b2971 PS |
1023 | $currentversion = $DB->get_field('modules', 'version', array('name'=>$pluginname)); |
1024 | $currentversion = ($currentversion === false) ? null : $currentversion; | |
db9d4a3d | 1025 | } catch (Exception $ignored) { |
1026 | } | |
bb8b2971 PS |
1027 | $cd = get_component_directory($component); |
1028 | if (file_exists("$cd/version.php")) { | |
1029 | $module = new stdClass(); | |
1030 | $module->version = null; | |
1031 | include("$cd/version.php"); | |
1032 | $targetversion = $module->version; | |
1033 | } | |
db9d4a3d | 1034 | |
bb8b2971 | 1035 | } else if ($plugintype === 'block') { |
db9d4a3d | 1036 | try { |
bb8b2971 PS |
1037 | if ($block = $DB->get_record('block', array('name'=>$pluginname))) { |
1038 | $currentversion = $block->version; | |
db9d4a3d | 1039 | } |
1040 | } catch (Exception $ignored) { | |
1041 | } | |
bb8b2971 PS |
1042 | $cd = get_component_directory($component); |
1043 | if (file_exists("$cd/version.php")) { | |
1044 | $plugin = new stdClass(); | |
1045 | $plugin->version = null; | |
1046 | include("$cd/version.php"); | |
1047 | $targetversion = $plugin->version; | |
1048 | } | |
795a08ad | 1049 | |
1050 | } else { | |
bb8b2971 | 1051 | $pluginversion = get_config($component, 'version'); |
795a08ad | 1052 | if (!empty($pluginversion)) { |
bb8b2971 PS |
1053 | $currentversion = $pluginversion; |
1054 | } | |
1055 | $cd = get_component_directory($component); | |
1056 | if (file_exists("$cd/version.php")) { | |
1057 | $plugin = new stdClass(); | |
1058 | $plugin->version = null; | |
1059 | include("$cd/version.php"); | |
1060 | $targetversion = $plugin->version; | |
795a08ad | 1061 | } |
db9d4a3d | 1062 | } |
1063 | ||
365a5941 | 1064 | $log = new stdClass(); |
bb8b2971 PS |
1065 | $log->type = $type; |
1066 | $log->plugin = $component; | |
1067 | $log->version = $currentversion; | |
1068 | $log->targetversion = $targetversion; | |
1069 | $log->info = $info; | |
1070 | $log->details = $details; | |
1071 | $log->backtrace = $backtrace; | |
1072 | $log->userid = $USER->id; | |
1073 | $log->timemodified = time(); | |
db9d4a3d | 1074 | try { |
1075 | $DB->insert_record('upgrade_log', $log); | |
1076 | } catch (Exception $ignored) { | |
795a08ad | 1077 | // possible during install or 2.0 upgrade |
db9d4a3d | 1078 | } |
1079 | } | |
1080 | ||
1081 | /** | |
1082 | * Marks start of upgrade, blocks any other access to site. | |
1083 | * The upgrade is finished at the end of script or after timeout. | |
72fb21b6 | 1084 | * |
1085 | * @global object | |
1086 | * @global object | |
1087 | * @global object | |
db9d4a3d | 1088 | */ |
1089 | function upgrade_started($preinstall=false) { | |
de6d81e6 | 1090 | global $CFG, $DB, $PAGE, $OUTPUT; |
db9d4a3d | 1091 | |
1092 | static $started = false; | |
1093 | ||
1094 | if ($preinstall) { | |
1095 | ignore_user_abort(true); | |
1096 | upgrade_setup_debug(true); | |
1097 | ||
1098 | } else if ($started) { | |
1099 | upgrade_set_timeout(120); | |
1100 | ||
1101 | } else { | |
c13a5e71 | 1102 | if (!CLI_SCRIPT and !$PAGE->headerprinted) { |
db9d4a3d | 1103 | $strupgrade = get_string('upgradingversion', 'admin'); |
78946b9b | 1104 | $PAGE->set_pagelayout('maintenance'); |
543f54d3 | 1105 | upgrade_init_javascript(); |
de6d81e6 | 1106 | $PAGE->set_title($strupgrade.' - Moodle '.$CFG->target_release); |
1107 | $PAGE->set_heading($strupgrade); | |
1108 | $PAGE->navbar->add($strupgrade); | |
1109 | $PAGE->set_cacheable(false); | |
1110 | echo $OUTPUT->header(); | |
db9d4a3d | 1111 | } |
1112 | ||
1113 | ignore_user_abort(true); | |
1114 | register_shutdown_function('upgrade_finished_handler'); | |
1115 | upgrade_setup_debug(true); | |
1116 | set_config('upgraderunning', time()+300); | |
1117 | $started = true; | |
1118 | } | |
1119 | } | |
1120 | ||
1121 | /** | |
b921b6ee | 1122 | * Internal function - executed if upgrade interrupted. |
db9d4a3d | 1123 | */ |
1124 | function upgrade_finished_handler() { | |
1125 | upgrade_finished(); | |
1126 | } | |
1127 | ||
1128 | /** | |
1129 | * Indicates upgrade is finished. | |
1130 | * | |
1131 | * This function may be called repeatedly. | |
72fb21b6 | 1132 | * |
1133 | * @global object | |
1134 | * @global object | |
db9d4a3d | 1135 | */ |
1136 | function upgrade_finished($continueurl=null) { | |
7e0d6675 | 1137 | global $CFG, $DB, $OUTPUT; |
db9d4a3d | 1138 | |
1139 | if (!empty($CFG->upgraderunning)) { | |
1140 | unset_config('upgraderunning'); | |
1141 | upgrade_setup_debug(false); | |
1142 | ignore_user_abort(false); | |
1143 | if ($continueurl) { | |
aa9a6867 | 1144 | echo $OUTPUT->continue_button($continueurl); |
7e0d6675 | 1145 | echo $OUTPUT->footer(); |
db9d4a3d | 1146 | die; |
1147 | } | |
1148 | } | |
1149 | } | |
1150 | ||
72fb21b6 | 1151 | /** |
1152 | * @global object | |
1153 | * @global object | |
1154 | */ | |
db9d4a3d | 1155 | function upgrade_setup_debug($starting) { |
1156 | global $CFG, $DB; | |
1157 | ||
1158 | static $originaldebug = null; | |
1159 | ||
1160 | if ($starting) { | |
1161 | if ($originaldebug === null) { | |
1162 | $originaldebug = $DB->get_debug(); | |
1163 | } | |
1164 | if (!empty($CFG->upgradeshowsql)) { | |
1165 | $DB->set_debug(true); | |
1166 | } | |
1167 | } else { | |
1168 | $DB->set_debug($originaldebug); | |
1169 | } | |
1170 | } | |
1171 | ||
72fb21b6 | 1172 | /** |
1173 | * @global object | |
1174 | */ | |
90509582 | 1175 | function print_upgrade_reload($url) { |
f2a1963c | 1176 | global $OUTPUT; |
90509582 | 1177 | |
1178 | echo "<br />"; | |
1179 | echo '<div class="continuebutton">'; | |
b5d0cafc | 1180 | echo '<a href="'.$url.'" title="'.get_string('reload').'" ><img src="'.$OUTPUT->pix_url('i/reload') . '" alt="" /> '.get_string('reload').'</a>'; |
90509582 | 1181 | echo '</div><br />'; |
1182 | } | |
1183 | ||
db9d4a3d | 1184 | function print_upgrade_separator() { |
1185 | if (!CLI_SCRIPT) { | |
1186 | echo '<hr />'; | |
1187 | } | |
1188 | } | |
1189 | ||
795a08ad | 1190 | /** |
1191 | * Default start upgrade callback | |
1192 | * @param string $plugin | |
b921b6ee | 1193 | * @param bool $installation true if installation, false means upgrade |
795a08ad | 1194 | */ |
194cdca8 | 1195 | function print_upgrade_part_start($plugin, $installation, $verbose) { |
3c159385 | 1196 | global $OUTPUT; |
795a08ad | 1197 | if (empty($plugin) or $plugin == 'moodle') { |
1198 | upgrade_started($installation); // does not store upgrade running flag yet | |
194cdca8 | 1199 | if ($verbose) { |
3c159385 | 1200 | echo $OUTPUT->heading(get_string('coresystem')); |
194cdca8 | 1201 | } |
795a08ad | 1202 | } else { |
1203 | upgrade_started(); | |
194cdca8 | 1204 | if ($verbose) { |
3c159385 | 1205 | echo $OUTPUT->heading($plugin); |
194cdca8 | 1206 | } |
795a08ad | 1207 | } |
1208 | if ($installation) { | |
1209 | if (empty($plugin) or $plugin == 'moodle') { | |
1210 | // no need to log - log table not yet there ;-) | |
1211 | } else { | |
1212 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin installation'); | |
1213 | } | |
1214 | } else { | |
1215 | if (empty($plugin) or $plugin == 'moodle') { | |
1216 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting core upgrade'); | |
1217 | } else { | |
1218 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin upgrade'); | |
1219 | } | |
1220 | } | |
1221 | } | |
1222 | ||
1223 | /** | |
1224 | * Default end upgrade callback | |
1225 | * @param string $plugin | |
b921b6ee | 1226 | * @param bool $installation true if installation, false means upgrade |
795a08ad | 1227 | */ |
194cdca8 | 1228 | function print_upgrade_part_end($plugin, $installation, $verbose) { |
aa9a6867 | 1229 | global $OUTPUT; |
795a08ad | 1230 | upgrade_started(); |
1231 | if ($installation) { | |
1232 | if (empty($plugin) or $plugin == 'moodle') { | |
1233 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core installed'); | |
1234 | } else { | |
1235 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin installed'); | |
1236 | } | |
1237 | } else { | |
1238 | if (empty($plugin) or $plugin == 'moodle') { | |
1239 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core upgraded'); | |
1240 | } else { | |
1241 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin upgraded'); | |
1242 | } | |
1243 | } | |
194cdca8 | 1244 | if ($verbose) { |
aa9a6867 | 1245 | echo $OUTPUT->notification(get_string('success'), 'notifysuccess'); |
194cdca8 | 1246 | print_upgrade_separator(); |
96db14f9 | 1247 | } |
1248 | } | |
1249 | ||
72fb21b6 | 1250 | /** |
b921b6ee | 1251 | * Sets up JS code required for all upgrade scripts. |
72fb21b6 | 1252 | * @global object |
1253 | */ | |
543f54d3 | 1254 | function upgrade_init_javascript() { |
e29380f3 | 1255 | global $PAGE; |
543f54d3 PS |
1256 | // scroll to the end of each upgrade page so that ppl see either error or continue button, |
1257 | // no need to scroll continuously any more, it is enough to jump to end once the footer is printed ;-) | |
1258 | $js = "window.scrollTo(0, 5000000);"; | |
1259 | $PAGE->requires->js_init_code($js); | |
db9d4a3d | 1260 | } |
1261 | ||
db9d4a3d | 1262 | /** |
1263 | * Try to upgrade the given language pack (or current language) | |
af8d4d91 | 1264 | * |
74a4c9a9 | 1265 | * @param string $lang the code of the language to update, defaults to the current language |
db9d4a3d | 1266 | */ |
1267 | function upgrade_language_pack($lang='') { | |
3c159385 | 1268 | global $CFG, $OUTPUT; |
db9d4a3d | 1269 | |
af8d4d91 DM |
1270 | get_string_manager()->reset_caches(); |
1271 | ||
db9d4a3d | 1272 | if (empty($lang)) { |
1273 | $lang = current_language(); | |
1274 | } | |
1275 | ||
3a915b06 | 1276 | if ($lang == 'en') { |
db9d4a3d | 1277 | return true; // Nothing to do |
1278 | } | |
1279 | ||
551fe0e5 | 1280 | upgrade_started(false); |
3c159385 | 1281 | echo $OUTPUT->heading(get_string('langimport', 'admin').': '.$lang); |
db9d4a3d | 1282 | |
1283 | @mkdir ($CFG->dataroot.'/temp/'); //make it in case it's a fresh install, it might not be there | |
1284 | @mkdir ($CFG->dataroot.'/lang/'); | |
1285 | ||
1286 | require_once($CFG->libdir.'/componentlib.class.php'); | |
af8d4d91 | 1287 | |
52deafc1 | 1288 | $installer = new lang_installer($lang); |
74a4c9a9 DM |
1289 | $results = $installer->run(); |
1290 | foreach ($results as $langcode => $langstatus) { | |
1291 | switch ($langstatus) { | |
1292 | case lang_installer::RESULT_DOWNLOADERROR: | |
52deafc1 | 1293 | echo $OUTPUT->notification($langcode . '.zip'); |
74a4c9a9 DM |
1294 | break; |
1295 | case lang_installer::RESULT_INSTALLED: | |
1296 | echo $OUTPUT->notification(get_string('langpackinstalled', 'admin', $langcode), 'notifysuccess'); | |
1297 | break; | |
1298 | case lang_installer::RESULT_UPTODATE: | |
1299 | echo $OUTPUT->notification(get_string('langpackuptodate', 'admin', $langcode), 'notifysuccess'); | |
1300 | break; | |
db9d4a3d | 1301 | } |
1302 | } | |
af8d4d91 DM |
1303 | |
1304 | get_string_manager()->reset_caches(); | |
1305 | ||
551fe0e5 | 1306 | print_upgrade_separator(); |
db9d4a3d | 1307 | } |
8580535b | 1308 | |
1309 | /** | |
1310 | * Install core moodle tables and initialize | |
1311 | * @param float $version target version | |
1312 | * @param bool $verbose | |
1313 | * @return void, may throw exception | |
1314 | */ | |
1315 | function install_core($version, $verbose) { | |
1316 | global $CFG, $DB; | |
1317 | ||
1318 | try { | |
c3d0e149 | 1319 | set_time_limit(600); |
194cdca8 | 1320 | print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag |
8580535b | 1321 | |
1322 | $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml"); | |
1323 | upgrade_started(); // we want the flag to be stored in config table ;-) | |
1324 | ||
1325 | // set all core default records and default settings | |
1326 | require_once("$CFG->libdir/db/install.php"); | |
c6d75bff | 1327 | xmldb_main_install(); // installs the capabilities too |
8580535b | 1328 | |
1329 | // store version | |
1330 | upgrade_main_savepoint(true, $version, false); | |
1331 | ||
df997f84 | 1332 | // Continue with the installation |
c6d75bff PS |
1333 | log_update_descriptions('moodle'); |
1334 | external_update_descriptions('moodle'); | |
8580535b | 1335 | events_update_definition('moodle'); |
1336 | message_update_providers('moodle'); | |
8580535b | 1337 | |
df997f84 | 1338 | // Write default settings unconditionally |
8580535b | 1339 | admin_apply_default_settings(NULL, true); |
1340 | ||
194cdca8 | 1341 | print_upgrade_part_end(null, true, $verbose); |
8580535b | 1342 | } catch (exception $ex) { |
1343 | upgrade_handle_exception($ex); | |
1344 | } | |
1345 | } | |
1346 | ||
1347 | /** | |
1348 | * Upgrade moodle core | |
1349 | * @param float $version target version | |
1350 | * @param bool $verbose | |
1351 | * @return void, may throw exception | |
1352 | */ | |
1353 | function upgrade_core($version, $verbose) { | |
1354 | global $CFG; | |
1355 | ||
41209c1e PS |
1356 | raise_memory_limit(MEMORY_EXTRA); |
1357 | ||
3316fe24 | 1358 | require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades |
3316fe24 | 1359 | |
8580535b | 1360 | try { |
dcf9be7f | 1361 | // Reset caches before any output |
f87eab7e | 1362 | purge_all_caches(); |
dcf9be7f | 1363 | |
8580535b | 1364 | // Upgrade current language pack if we can |
1365 | if (empty($CFG->skiplangupgrade)) { | |
63891a7c DM |
1366 | if (get_string_manager()->translation_exists(current_language())) { |
1367 | upgrade_language_pack(false); | |
1368 | } | |
8580535b | 1369 | } |
1370 | ||
194cdca8 | 1371 | print_upgrade_part_start('moodle', false, $verbose); |
8580535b | 1372 | |
17da2e6f | 1373 | // one time special local migration pre 2.0 upgrade script |
8d33799d PS |
1374 | if ($CFG->version < 2007101600) { |
1375 | $pre20upgradefile = "$CFG->dirroot/local/upgrade_pre20.php"; | |
17da2e6f | 1376 | if (file_exists($pre20upgradefile)) { |
1377 | set_time_limit(0); | |
1378 | require($pre20upgradefile); | |
1379 | // reset upgrade timeout to default | |
1380 | upgrade_set_timeout(); | |
1381 | } | |
1382 | } | |
1383 | ||
8580535b | 1384 | $result = xmldb_main_upgrade($CFG->version); |
1385 | if ($version > $CFG->version) { | |
1386 | // store version if not already there | |
1387 | upgrade_main_savepoint($result, $version, false); | |
1388 | } | |
1389 | ||
1390 | // perform all other component upgrade routines | |
1391 | update_capabilities('moodle'); | |
c6d75bff | 1392 | log_update_descriptions('moodle'); |
c976e271 | 1393 | external_update_descriptions('moodle'); |
8580535b | 1394 | events_update_definition('moodle'); |
1395 | message_update_providers('moodle'); | |
8580535b | 1396 | |
dcf9be7f | 1397 | // Reset caches again, just to be sure |
f87eab7e | 1398 | purge_all_caches(); |
2e5eba85 PS |
1399 | |
1400 | // Clean up contexts - more and more stuff depends on existence of paths and contexts | |
1401 | cleanup_contexts(); | |
1402 | create_contexts(); | |
1403 | build_context_path(); | |
df997f84 PS |
1404 | $syscontext = get_context_instance(CONTEXT_SYSTEM); |
1405 | mark_context_dirty($syscontext->path); | |
8580535b | 1406 | |
194cdca8 | 1407 | print_upgrade_part_end('moodle', false, $verbose); |
8580535b | 1408 | } catch (Exception $ex) { |
1409 | upgrade_handle_exception($ex); | |
1410 | } | |
1411 | } | |
1412 | ||
1413 | /** | |
1414 | * Upgrade/install other parts of moodle | |
1415 | * @param bool $verbose | |
1416 | * @return void, may throw exception | |
1417 | */ | |
1418 | function upgrade_noncore($verbose) { | |
1419 | global $CFG; | |
1420 | ||
41209c1e PS |
1421 | raise_memory_limit(MEMORY_EXTRA); |
1422 | ||
8580535b | 1423 | // upgrade all plugins types |
1424 | try { | |
1425 | $plugintypes = get_plugin_types(); | |
1426 | foreach ($plugintypes as $type=>$location) { | |
17da2e6f | 1427 | upgrade_plugins($type, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose); |
8580535b | 1428 | } |
1429 | } catch (Exception $ex) { | |
1430 | upgrade_handle_exception($ex); | |
1431 | } | |
8580535b | 1432 | } |
3316fe24 | 1433 | |
1434 | /** | |
1435 | * Checks if the main tables have been installed yet or not. | |
1436 | * @return bool | |
1437 | */ | |
1438 | function core_tables_exist() { | |
1439 | global $DB; | |
1440 | ||
1441 | if (!$tables = $DB->get_tables() ) { // No tables yet at all. | |
1442 | return false; | |
9b683d13 | 1443 | |
3316fe24 | 1444 | } else { // Check for missing main tables |
1445 | $mtables = array('config', 'course', 'groupings'); // some tables used in 1.9 and 2.0, preferable something from the start and end of install.xml | |
1446 | foreach ($mtables as $mtable) { | |
1447 | if (!in_array($mtable, $tables)) { | |
1448 | return false; | |
1449 | } | |
1450 | } | |
1451 | return true; | |
9b683d13 | 1452 | } |
ba04999c | 1453 | } |
c71ade2f PL |
1454 | |
1455 | /** | |
1456 | * upgrades the mnet rpc definitions for the given component. | |
1457 | * this method doesn't return status, an exception will be thrown in the case of an error | |
1458 | * | |
1459 | * @param string $component the plugin to upgrade, eg auth_mnet | |
1460 | */ | |
1461 | function upgrade_plugin_mnet_functions($component) { | |
1462 | global $DB, $CFG; | |
1463 | ||
1464 | list($type, $plugin) = explode('_', $component); | |
1465 | $path = get_plugin_directory($type, $plugin); | |
1466 | ||
17b71716 PS |
1467 | $publishes = array(); |
1468 | $subscribes = array(); | |
c71ade2f PL |
1469 | if (file_exists($path . '/db/mnet.php')) { |
1470 | require_once($path . '/db/mnet.php'); // $publishes comes from this file | |
1471 | } | |
1472 | if (empty($publishes)) { | |
1473 | $publishes = array(); // still need this to be able to disable stuff later | |
1474 | } | |
1475 | if (empty($subscribes)) { | |
1476 | $subscribes = array(); // still need this to be able to disable stuff later | |
1477 | } | |
1478 | ||
1479 | static $servicecache = array(); | |
1480 | ||
1481 | // rekey an array based on the rpc method for easy lookups later | |
1482 | $publishmethodservices = array(); | |
1483 | $subscribemethodservices = array(); | |
1484 | foreach($publishes as $servicename => $service) { | |
1485 | if (is_array($service['methods'])) { | |
1486 | foreach($service['methods'] as $methodname) { | |
1487 | $service['servicename'] = $servicename; | |
1488 | $publishmethodservices[$methodname][] = $service; | |
1489 | } | |
1490 | } | |
1491 | } | |
1492 | ||
1493 | // Disable functions that don't exist (any more) in the source | |
1494 | // Should these be deleted? What about their permissions records? | |
1495 | foreach ($DB->get_records('mnet_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { | |
1496 | if (!array_key_exists($rpc->functionname, $publishmethodservices) && $rpc->enabled) { | |
1497 | $DB->set_field('mnet_rpc', 'enabled', 0, array('id' => $rpc->id)); | |
1498 | } else if (array_key_exists($rpc->functionname, $publishmethodservices) && !$rpc->enabled) { | |
1499 | $DB->set_field('mnet_rpc', 'enabled', 1, array('id' => $rpc->id)); | |
1500 | } | |
1501 | } | |
1502 | ||
1503 | // reflect all the services we're publishing and save them | |
1504 | require_once($CFG->dirroot . '/lib/zend/Zend/Server/Reflection.php'); | |
1505 | static $cachedclasses = array(); // to store reflection information in | |
1506 | foreach ($publishes as $service => $data) { | |
1507 | $f = $data['filename']; | |
1508 | $c = $data['classname']; | |
1509 | foreach ($data['methods'] as $method) { | |
6bdfef5d | 1510 | $dataobject = new stdClass(); |
c71ade2f PL |
1511 | $dataobject->plugintype = $type; |
1512 | $dataobject->pluginname = $plugin; | |
1513 | $dataobject->enabled = 1; | |
1514 | $dataobject->classname = $c; | |
1515 | $dataobject->filename = $f; | |
1516 | ||
1517 | if (is_string($method)) { | |
1518 | $dataobject->functionname = $method; | |
1519 | ||
1520 | } else if (is_array($method)) { // wants to override file or class | |
1521 | $dataobject->functionname = $method['method']; | |
1522 | $dataobject->classname = $method['classname']; | |
1523 | $dataobject->filename = $method['filename']; | |
1524 | } | |
1525 | $dataobject->xmlrpcpath = $type.'/'.$plugin.'/'.$dataobject->filename.'/'.$method; | |
1526 | $dataobject->static = false; | |
1527 | ||
1528 | require_once($path . '/' . $dataobject->filename); | |
1529 | $functionreflect = null; // slightly different ways to get this depending on whether it's a class method or a function | |
1530 | if (!empty($dataobject->classname)) { | |
1531 | if (!class_exists($dataobject->classname)) { | |
1532 | throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); | |
1533 | } | |
1534 | $key = $dataobject->filename . '|' . $dataobject->classname; | |
1535 | if (!array_key_exists($key, $cachedclasses)) { // look to see if we've already got a reflection object | |
1536 | try { | |
1537 | $cachedclasses[$key] = Zend_Server_Reflection::reflectClass($dataobject->classname); | |
1538 | } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful | |
1539 | throw new moodle_exception('installreflectionclasserror', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname, 'error' => $e->getMessage())); | |
1540 | } | |
1541 | } | |
1542 | $r =& $cachedclasses[$key]; | |
1543 | if (!$r->hasMethod($dataobject->functionname)) { | |
1544 | throw new moodle_exception('installnosuchmethod', 'mnet', '', (object)array('method' => $dataobject->functionname, 'class' => $dataobject->classname)); | |
1545 | } | |
1546 | // stupid workaround for zend not having a getMethod($name) function | |
1547 | $ms = $r->getMethods(); | |
1548 | foreach ($ms as $m) { | |
1549 | if ($m->getName() == $dataobject->functionname) { | |
1550 | $functionreflect = $m; | |
1551 | break; | |
1552 | } | |
1553 | } | |
1554 | $dataobject->static = (int)$functionreflect->isStatic(); | |
1555 | } else { | |
1556 | if (!function_exists($dataobject->functionname)) { | |
1557 | throw new moodle_exception('installnosuchfunction', 'mnet', '', (object)array('method' => $dataobject->functionname, 'file' => $dataobject->filename)); | |
1558 | } | |
1559 | try { | |
1560 | $functionreflect = Zend_Server_Reflection::reflectFunction($dataobject->functionname); | |
1561 | } catch (Zend_Server_Reflection_Exception $e) { // catch these and rethrow them to something more helpful | |
1562 | throw new moodle_exception('installreflectionfunctionerror', 'mnet', '', (object)array('method' => $dataobject->functionname, '' => $dataobject->filename, 'error' => $e->getMessage())); | |
1563 | } | |
1564 | } | |
1565 | $dataobject->profile = serialize(admin_mnet_method_profile($functionreflect)); | |
1566 | $dataobject->help = $functionreflect->getDescription(); | |
1567 | ||
1568 | if ($record_exists = $DB->get_record('mnet_rpc', array('xmlrpcpath'=>$dataobject->xmlrpcpath))) { | |
1569 | $dataobject->id = $record_exists->id; | |
1570 | $dataobject->enabled = $record_exists->enabled; | |
1571 | $DB->update_record('mnet_rpc', $dataobject); | |
1572 | } else { | |
1573 | $dataobject->id = $DB->insert_record('mnet_rpc', $dataobject, true); | |
1574 | } | |
c71ade2f | 1575 | |
d5bd0b01 DM |
1576 | // TODO this API versioning must be reworked, here the recently processed method |
1577 | // sets the service API which may not be correct | |
677b6321 PL |
1578 | foreach ($publishmethodservices[$dataobject->functionname] as $service) { |
1579 | if ($serviceobj = $DB->get_record('mnet_service', array('name'=>$service['servicename']))) { | |
1580 | $serviceobj->apiversion = $service['apiversion']; | |
1581 | $DB->update_record('mnet_service', $serviceobj); | |
1582 | } else { | |
1583 | $serviceobj = new stdClass(); | |
1584 | $serviceobj->name = $service['servicename']; | |
f4a2817a | 1585 | $serviceobj->description = empty($service['description']) ? '' : $service['description']; |
677b6321 PL |
1586 | $serviceobj->apiversion = $service['apiversion']; |
1587 | $serviceobj->offer = 1; | |
1588 | $serviceobj->id = $DB->insert_record('mnet_service', $serviceobj); | |
1589 | } | |
1590 | $servicecache[$service['servicename']] = $serviceobj; | |
1591 | if (!$DB->record_exists('mnet_service2rpc', array('rpcid'=>$dataobject->id, 'serviceid'=>$serviceobj->id))) { | |
1592 | $obj = new stdClass(); | |
1593 | $obj->rpcid = $dataobject->id; | |
1594 | $obj->serviceid = $serviceobj->id; | |
1595 | $DB->insert_record('mnet_service2rpc', $obj, true); | |
1596 | } | |
c71ade2f PL |
1597 | } |
1598 | } | |
1599 | } | |
c71ade2f PL |
1600 | // finished with methods we publish, now do subscribable methods |
1601 | foreach($subscribes as $service => $methods) { | |
1602 | if (!array_key_exists($service, $servicecache)) { | |
1603 | if (!$serviceobj = $DB->get_record('mnet_service', array('name' => $service))) { | |
81634f03 | 1604 | debugging("TODO: skipping unknown service $service - somebody needs to fix MDL-21993"); |
c71ade2f PL |
1605 | continue; |
1606 | } | |
1607 | $servicecache[$service] = $serviceobj; | |
1608 | } else { | |
1609 | $serviceobj = $servicecache[$service]; | |
1610 | } | |
1611 | foreach ($methods as $method => $xmlrpcpath) { | |
1612 | if (!$rpcid = $DB->get_field('mnet_remote_rpc', 'id', array('xmlrpcpath'=>$xmlrpcpath))) { | |
1613 | $remoterpc = (object)array( | |
1614 | 'functionname' => $method, | |
1615 | 'xmlrpcpath' => $xmlrpcpath, | |
1616 | 'plugintype' => $type, | |
1617 | 'pluginname' => $plugin, | |
1618 | 'enabled' => 1, | |
1619 | ); | |
1620 | $rpcid = $remoterpc->id = $DB->insert_record('mnet_remote_rpc', $remoterpc, true); | |
1621 | } | |
1622 | if (!$DB->record_exists('mnet_remote_service2rpc', array('rpcid'=>$rpcid, 'serviceid'=>$serviceobj->id))) { | |
1623 | $obj = new stdClass(); | |
1624 | $obj->rpcid = $rpcid; | |
1625 | $obj->serviceid = $serviceobj->id; | |
1626 | $DB->insert_record('mnet_remote_service2rpc', $obj, true); | |
1627 | } | |
677b6321 | 1628 | $subscribemethodservices[$method][] = $service; |
c71ade2f PL |
1629 | } |
1630 | } | |
1631 | ||
1632 | foreach ($DB->get_records('mnet_remote_rpc', array('pluginname'=>$plugin, 'plugintype'=>$type), 'functionname ASC ') as $rpc) { | |
1633 | if (!array_key_exists($rpc->functionname, $subscribemethodservices) && $rpc->enabled) { | |
1634 | $DB->set_field('mnet_remote_rpc', 'enabled', 0, array('id' => $rpc->id)); | |
1635 | } else if (array_key_exists($rpc->functionname, $subscribemethodservices) && !$rpc->enabled) { | |
1636 | $DB->set_field('mnet_remote_rpc', 'enabled', 1, array('id' => $rpc->id)); | |
1637 | } | |
1638 | } | |
1639 | ||
1640 | return true; | |
1641 | } | |
1642 | ||
1643 | /** | |
1644 | * Given some sort of Zend Reflection function/method object, return a profile array, ready to be serialized and stored | |
1645 | * | |
1646 | * @param Zend_Server_Reflection_Function_Abstract $function can be any subclass of this object type | |
1647 | * | |
1648 | * @return array | |
1649 | */ | |
1650 | function admin_mnet_method_profile(Zend_Server_Reflection_Function_Abstract $function) { | |
1651 | $proto = array_pop($function->getPrototypes()); | |
1652 | $ret = $proto->getReturnValue(); | |
1653 | $profile = array( | |
1654 | 'parameters' => array(), | |
1655 | 'return' => array( | |
1656 | 'type' => $ret->getType(), | |
1657 | 'description' => $ret->getDescription(), | |
1658 | ), | |
1659 | ); | |
1660 | foreach ($proto->getParameters() as $p) { | |
1661 | $profile['parameters'][] = array( | |
1662 | 'name' => $p->getName(), | |
1663 | 'type' => $p->getType(), | |
1664 | 'description' => $p->getDescription(), | |
1665 | ); | |
1666 | } | |
1667 | return $profile; | |
1668 | } |