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