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