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