96db14f9 |
1 | <?php |
2 | |
3 | // This file is part of Moodle - http://moodle.org/ |
4 | // |
5 | // Moodle is free software: you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation, either version 3 of the License, or |
8 | // (at your option) any later version. |
9 | // |
10 | // Moodle is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | // |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
db9d4a3d |
17 | |
18 | /** |
8580535b |
19 | * Various upgrade/install related functions and classes. |
db9d4a3d |
20 | * |
96db14f9 |
21 | * @package moodlecore |
22 | * @subpackage upgrade |
23 | * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) |
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
db9d4a3d |
25 | */ |
26 | |
72fb21b6 |
27 | /** UPGRADE_LOG_NORMAL = 0 */ |
db9d4a3d |
28 | define('UPGRADE_LOG_NORMAL', 0); |
72fb21b6 |
29 | /** UPGRADE_LOG_NOTICE = 1 */ |
db9d4a3d |
30 | define('UPGRADE_LOG_NOTICE', 1); |
72fb21b6 |
31 | /** UPGRADE_LOG_ERROR = 2 */ |
795a08ad |
32 | define('UPGRADE_LOG_ERROR', 2); |
33 | |
34 | /** |
35 | * Exception indicating unknown error during upgrade. |
72fb21b6 |
36 | * |
37 | * @package moodlecore |
38 | * @subpackage upgrade |
39 | * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) |
40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
795a08ad |
41 | */ |
42 | class upgrade_exception extends moodle_exception { |
43 | function __construct($plugin, $version) { |
44 | global $CFG; |
45 | $a = (object)array('plugin'=>$plugin, 'version'=>$version); |
46 | parent::__construct('upgradeerror', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a); |
47 | } |
48 | } |
49 | |
50 | /** |
51 | * Exception indicating downgrade error during upgrade. |
72fb21b6 |
52 | * |
53 | * @package moodlecore |
54 | * @subpackage upgrade |
55 | * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) |
56 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
795a08ad |
57 | */ |
58 | class downgrade_exception extends moodle_exception { |
59 | function __construct($plugin, $oldversion, $newversion) { |
60 | global $CFG; |
61 | $plugin = is_null($plugin) ? 'moodle' : $plugin; |
62 | $a = (object)array('plugin'=>$plugin, 'oldversion'=>$oldversion, 'newversion'=>$newversion); |
63 | parent::__construct('cannotdowngrade', 'debug', "$CFG->wwwroot/$CFG->admin/index.php", $a); |
64 | } |
65 | } |
66 | |
72fb21b6 |
67 | /** |
68 | * @package moodlecore |
69 | * @subpackage upgrade |
70 | * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) |
71 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
72 | */ |
795a08ad |
73 | class upgrade_requires_exception extends moodle_exception { |
74 | function __construct($plugin, $pluginversion, $currentmoodle, $requiremoodle) { |
75 | global $CFG; |
76 | $a = new object(); |
77 | $a->pluginname = $plugin; |
78 | $a->pluginversion = $pluginversion; |
79 | $a->currentmoodle = $currentmoodle; |
80 | $a->requiremoodle = $currentmoodle; |
81 | parent::__construct('pluginrequirementsnotmet', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $a); |
82 | } |
83 | } |
84 | |
72fb21b6 |
85 | /** |
86 | * @package moodlecore |
87 | * @subpackage upgrade |
88 | * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) |
89 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
90 | */ |
795a08ad |
91 | class plugin_defective_exception extends moodle_exception { |
92 | function __construct($plugin, $details) { |
93 | global $CFG; |
94 | parent::__construct('detectedbrokenplugin', 'error', "$CFG->wwwroot/$CFG->admin/index.php", $plugin, $details); |
95 | } |
96 | } |
db9d4a3d |
97 | |
98 | /** |
99 | * Insert or update log display entry. Entry may already exist. |
100 | * $module, $action must be unique |
101 | * |
72fb21b6 |
102 | * @global object |
db9d4a3d |
103 | * @param string $module |
104 | * @param string $action |
105 | * @param string $mtable |
106 | * @param string $field |
107 | * @return void |
108 | * |
109 | */ |
110 | function update_log_display_entry($module, $action, $mtable, $field) { |
111 | global $DB; |
112 | |
113 | if ($type = $DB->get_record('log_display', array('module'=>$module, 'action'=>$action))) { |
114 | $type->mtable = $mtable; |
115 | $type->field = $field; |
116 | $DB->update_record('log_display', $type); |
117 | |
118 | } else { |
119 | $type = new object(); |
120 | $type->module = $module; |
121 | $type->action = $action; |
122 | $type->mtable = $mtable; |
123 | $type->field = $field; |
124 | |
125 | $DB->insert_record('log_display', $type, false); |
126 | } |
127 | } |
128 | |
129 | /** |
130 | * Upgrade savepoint, marks end of each upgrade block. |
131 | * It stores new main version, resets upgrade timeout |
132 | * and abort upgrade if user cancels page loading. |
133 | * |
134 | * Please do not make large upgrade blocks with lots of operations, |
135 | * for example when adding tables keep only one table operation per block. |
136 | * |
72fb21b6 |
137 | * @global object |
db9d4a3d |
138 | * @param bool $result false if upgrade step failed, true if completed |
139 | * @param string or float $version main version |
140 | * @param bool $allowabort allow user to abort script execution here |
141 | * @return void |
142 | */ |
143 | function upgrade_main_savepoint($result, $version, $allowabort=true) { |
144 | global $CFG; |
145 | |
795a08ad |
146 | if (!$result) { |
147 | throw new upgrade_exception('moodle core', $version); |
148 | } |
149 | |
150 | if ($CFG->version >= $version) { |
151 | // something really wrong is going on in main upgrade script |
152 | throw new downgrade_exception(null, $CFG->version, $version); |
db9d4a3d |
153 | } |
154 | |
795a08ad |
155 | set_config('version', $version); |
156 | upgrade_log(UPGRADE_LOG_NORMAL, null, 'Upgrade savepoint reached'); |
157 | |
db9d4a3d |
158 | // reset upgrade timeout to default |
159 | upgrade_set_timeout(); |
160 | |
161 | // this is a safe place to stop upgrades if user aborts page loading |
162 | if ($allowabort and connection_aborted()) { |
163 | die; |
164 | } |
165 | } |
166 | |
167 | /** |
168 | * Module upgrade savepoint, marks end of module upgrade blocks |
169 | * It stores module version, resets upgrade timeout |
170 | * and abort upgrade if user cancels page loading. |
171 | * |
72fb21b6 |
172 | * @global object |
db9d4a3d |
173 | * @param bool $result false if upgrade step failed, true if completed |
174 | * @param string or float $version main version |
175 | * @param string $modname name of module |
176 | * @param bool $allowabort allow user to abort script execution here |
177 | * @return void |
178 | */ |
179 | function upgrade_mod_savepoint($result, $version, $modname, $allowabort=true) { |
180 | global $DB; |
181 | |
795a08ad |
182 | if (!$result) { |
183 | throw new upgrade_exception("mod/$modname", $version); |
184 | } |
185 | |
db9d4a3d |
186 | if (!$module = $DB->get_record('modules', array('name'=>$modname))) { |
187 | print_error('modulenotexist', 'debug', '', $modname); |
188 | } |
189 | |
795a08ad |
190 | if ($module->version >= $version) { |
191 | // something really wrong is going on in upgrade script |
192 | throw new downgrade_exception("mod/$modname", $module->version, $version); |
db9d4a3d |
193 | } |
795a08ad |
194 | $module->version = $version; |
195 | $DB->update_record('modules', $module); |
196 | upgrade_log(UPGRADE_LOG_NORMAL, "mod/$modname", 'Upgrade savepoint reached'); |
db9d4a3d |
197 | |
198 | // reset upgrade timeout to default |
199 | upgrade_set_timeout(); |
200 | |
201 | // this is a safe place to stop upgrades if user aborts page loading |
202 | if ($allowabort and connection_aborted()) { |
203 | die; |
204 | } |
205 | } |
206 | |
207 | /** |
208 | * Blocks upgrade savepoint, marks end of blocks upgrade blocks |
209 | * It stores block version, resets upgrade timeout |
210 | * and abort upgrade if user cancels page loading. |
211 | * |
72fb21b6 |
212 | * @global object |
db9d4a3d |
213 | * @param bool $result false if upgrade step failed, true if completed |
214 | * @param string or float $version main version |
215 | * @param string $blockname name of block |
216 | * @param bool $allowabort allow user to abort script execution here |
217 | * @return void |
218 | */ |
795a08ad |
219 | function upgrade_block_savepoint($result, $version, $blockname, $allowabort=true) { |
db9d4a3d |
220 | global $DB; |
221 | |
795a08ad |
222 | if (!$result) { |
223 | throw new upgrade_exception("blocks/$blockname", $version); |
224 | } |
225 | |
db9d4a3d |
226 | if (!$block = $DB->get_record('block', array('name'=>$blockname))) { |
227 | print_error('blocknotexist', 'debug', '', $blockname); |
228 | } |
229 | |
795a08ad |
230 | if ($block->version >= $version) { |
231 | // something really wrong is going on in upgrade script |
232 | throw new downgrade_exception("blocks/$blockname", $block->version, $version); |
db9d4a3d |
233 | } |
795a08ad |
234 | $block->version = $version; |
235 | $DB->update_record('block', $block); |
236 | upgrade_log(UPGRADE_LOG_NORMAL, "blocks/$blockname", 'Upgrade savepoint reached'); |
db9d4a3d |
237 | |
238 | // reset upgrade timeout to default |
239 | upgrade_set_timeout(); |
240 | |
241 | // this is a safe place to stop upgrades if user aborts page loading |
242 | if ($allowabort and connection_aborted()) { |
243 | die; |
244 | } |
245 | } |
246 | |
247 | /** |
248 | * Plugins upgrade savepoint, marks end of blocks upgrade blocks |
249 | * It stores plugin version, resets upgrade timeout |
250 | * and abort upgrade if user cancels page loading. |
251 | * |
252 | * @param bool $result false if upgrade step failed, true if completed |
253 | * @param string or float $version main version |
254 | * @param string $type name of plugin |
255 | * @param string $dir location of plugin |
256 | * @param bool $allowabort allow user to abort script execution here |
257 | * @return void |
258 | */ |
259 | function upgrade_plugin_savepoint($result, $version, $type, $dir, $allowabort=true) { |
795a08ad |
260 | if (!$result) { |
261 | throw new upgrade_exception("$type/$dir", $version); |
db9d4a3d |
262 | } |
263 | |
ba04999c |
264 | /// TODO: Check that $type is a correct type - based on get_plugin_types() |
265 | /// TODO: Check that $dir (that perhaps should be named $name) is an existing plugin |
266 | |
795a08ad |
267 | $fullname = $type.'_'.$dir; |
268 | $component = $type.'/'.$dir; |
269 | |
270 | $installedversion = get_config($fullname, 'version'); |
271 | if ($installedversion >= $version) { |
272 | // Something really wrong is going on in the upgrade script |
273 | throw new downgrade_exception($component, $installedversion, $version); |
274 | } |
275 | set_config('version', $version, $fullname); |
276 | upgrade_log(UPGRADE_LOG_NORMAL, $component, 'Upgrade savepoint reached'); |
277 | |
db9d4a3d |
278 | // Reset upgrade timeout to default |
279 | upgrade_set_timeout(); |
280 | |
281 | // This is a safe place to stop upgrades if user aborts page loading |
282 | if ($allowabort and connection_aborted()) { |
283 | die; |
284 | } |
285 | } |
286 | |
287 | |
288 | /** |
289 | * Upgrade plugins |
290 | * |
72fb21b6 |
291 | * @global object |
292 | * @global object |
db9d4a3d |
293 | * @param string $type The type of plugins that should be updated (e.g. 'enrol', 'qtype') |
294 | * @param string $dir The directory where the plugins are located (e.g. 'question/questiontypes') |
295 | * @param string $return The url to prompt the user to continue to |
296 | */ |
194cdca8 |
297 | function upgrade_plugins($type, $dir, $startcallback, $endcallback, $verbose) { |
db9d4a3d |
298 | global $CFG, $DB; |
299 | |
300 | /// special cases |
301 | if ($type === 'mod') { |
194cdca8 |
302 | return upgrade_plugins_modules($startcallback, $endcallback, $verbose); |
795a08ad |
303 | } else if ($type === 'block') { |
194cdca8 |
304 | return upgrade_plugins_blocks($startcallback, $endcallback, $verbose); |
db9d4a3d |
305 | } |
306 | |
307 | $plugs = get_list_of_plugins($dir); |
db9d4a3d |
308 | |
309 | foreach ($plugs as $plug) { |
310 | |
795a08ad |
311 | $fullplug = $CFG->dirroot.'/'.$dir.'/'.$plug; |
312 | $component = $type.'/'.$plug; // standardised plugin name |
db9d4a3d |
313 | |
795a08ad |
314 | if (!is_readable($fullplug.'/version.php')) { |
315 | continue; |
db9d4a3d |
316 | } |
317 | |
795a08ad |
318 | $plugin = new object(); |
319 | require($fullplug.'/version.php'); // defines $plugin with version etc |
db9d4a3d |
320 | |
795a08ad |
321 | if (empty($plugin->version)) { |
322 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); |
db9d4a3d |
323 | } |
324 | |
795a08ad |
325 | $plugin->name = $plug; // The name MUST match the directory |
326 | $plugin->fullname = $type.'_'.$plug; // The name MUST match the directory |
327 | |
328 | |
db9d4a3d |
329 | if (!empty($plugin->requires)) { |
330 | if ($plugin->requires > $CFG->version) { |
795a08ad |
331 | throw new upgrade_requires_exception($component, $plugin->version, $CFG->version, $plugin->requires); |
db9d4a3d |
332 | } |
333 | } |
334 | |
db9d4a3d |
335 | $installedversion = get_config($plugin->fullname, 'version'); |
336 | |
795a08ad |
337 | if (empty($installedversion)) { // new installation |
194cdca8 |
338 | $startcallback($component, true, $verbose); |
db9d4a3d |
339 | |
795a08ad |
340 | /// Install tables if defined |
341 | if (file_exists($fullplug.'/db/install.xml')) { |
342 | $DB->get_manager()->install_from_xmldb_file($fullplug.'/db/install.xml'); |
db9d4a3d |
343 | } |
795a08ad |
344 | /// execute post install file |
345 | if (file_exists($fullplug.'/db/install.php')) { |
346 | require_once($fullplug.'/db/install.php'); |
347 | $post_install_function = 'xmldb_'.$plugin->fullname.'_install';; |
348 | $post_install_function(); |
349 | } |
350 | |
351 | /// store version |
352 | upgrade_plugin_savepoint(true, $plugin->version, $type, $plug, false); |
353 | |
354 | /// Install various components |
355 | update_capabilities($component); |
356 | events_update_definition($component); |
357 | message_update_providers($component); |
358 | |
194cdca8 |
359 | $endcallback($component, true, $verbose); |
795a08ad |
360 | |
361 | } else if ($installedversion < $plugin->version) { // upgrade |
362 | /// Run the upgrade function for the plugin. |
194cdca8 |
363 | $startcallback($component, false, $verbose); |
795a08ad |
364 | |
365 | if (is_readable($fullplug.'/db/upgrade.php')) { |
366 | require_once($fullplug.'/db/upgrade.php'); // defines upgrading function |
367 | |
368 | $newupgrade_function = 'xmldb_'.$plugin->fullname.'_upgrade'; |
369 | $result = $newupgrade_function($installedversion); |
370 | } else { |
371 | $result = true; |
372 | } |
373 | |
374 | $installedversion = get_config($plugin->fullname, 'version'); |
375 | if ($installedversion < $plugin->version) { |
376 | // store version if not already there |
377 | upgrade_plugin_savepoint($result, $plugin->version, $type, $plug, false); |
378 | } |
379 | |
380 | /// Upgrade various components |
381 | update_capabilities($component); |
382 | events_update_definition($component); |
383 | message_update_providers($component); |
384 | |
194cdca8 |
385 | $endcallback($component, false, $verbose); |
795a08ad |
386 | |
387 | } else if ($installedversion > $plugin->version) { |
388 | throw new downgrade_exception($component, $installedversion, $plugin->version); |
db9d4a3d |
389 | } |
390 | } |
db9d4a3d |
391 | } |
392 | |
393 | /** |
394 | * Find and check all modules and load them up or upgrade them if necessary |
72fb21b6 |
395 | * |
396 | * @global object |
397 | * @global object |
db9d4a3d |
398 | */ |
194cdca8 |
399 | function upgrade_plugins_modules($startcallback, $endcallback, $verbose) { |
db9d4a3d |
400 | global $CFG, $DB; |
401 | |
795a08ad |
402 | $mods = get_list_of_plugins('mod'); |
db9d4a3d |
403 | |
404 | foreach ($mods as $mod) { |
405 | |
406 | if ($mod == 'NEWMODULE') { // Someone has unzipped the template, ignore it |
407 | continue; |
408 | } |
409 | |
795a08ad |
410 | $fullmod = $CFG->dirroot.'/mod/'.$mod; |
411 | $component = 'mod/'.$mod; |
db9d4a3d |
412 | |
795a08ad |
413 | if (!is_readable($fullmod.'/version.php')) { |
414 | throw new plugin_defective_exception($component, 'Missing version.php'); |
db9d4a3d |
415 | } |
416 | |
795a08ad |
417 | $module = new object(); |
418 | require($fullmod .'/version.php'); // defines $module with version etc |
db9d4a3d |
419 | |
795a08ad |
420 | if (empty($module->version)) { |
c1fe2368 |
421 | if (isset($module->version)) { |
422 | // Version is empty but is set - it means its value is 0 or ''. Let us skip such module. |
423 | // This is inteded for developers so they can work on the early stages of the module. |
424 | continue; |
425 | } |
795a08ad |
426 | throw new plugin_defective_exception($component, 'Missing version value in version.php'); |
db9d4a3d |
427 | } |
428 | |
429 | if (!empty($module->requires)) { |
430 | if ($module->requires > $CFG->version) { |
795a08ad |
431 | throw new upgrade_requires_exception($component, $module->version, $CFG->version, $module->requires); |
db9d4a3d |
432 | } |
433 | } |
434 | |
435 | $module->name = $mod; // The name MUST match the directory |
436 | |
795a08ad |
437 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); |
db9d4a3d |
438 | |
795a08ad |
439 | if (empty($currmodule->version)) { |
194cdca8 |
440 | $startcallback($component, true, $verbose); |
db9d4a3d |
441 | |
795a08ad |
442 | /// Execute install.xml (XMLDB) - must be present in all modules |
443 | $DB->get_manager()->install_from_xmldb_file($fullmod.'/db/install.xml'); |
db9d4a3d |
444 | |
445 | /// Post installation hook - optional |
446 | if (file_exists("$fullmod/db/install.php")) { |
447 | require_once("$fullmod/db/install.php"); |
448 | $post_install_function = 'xmldb_'.$module->name.'_install';; |
449 | $post_install_function(); |
450 | } |
451 | |
452 | /// Continue with the installation, roles and other stuff |
453 | $module->id = $DB->insert_record('modules', $module); |
454 | |
795a08ad |
455 | /// Install various components |
456 | update_capabilities($component); |
457 | events_update_definition($component); |
458 | message_update_providers($component); |
459 | |
194cdca8 |
460 | $endcallback($component, true, $verbose); |
db9d4a3d |
461 | |
795a08ad |
462 | } else if ($currmodule->version < $module->version) { |
463 | /// If versions say that we need to upgrade but no upgrade files are available, notify and continue |
194cdca8 |
464 | $startcallback($component, false, $verbose); |
795a08ad |
465 | |
466 | if (is_readable($fullmod.'/db/upgrade.php')) { |
467 | require_once($fullmod.'/db/upgrade.php'); // defines new upgrading function |
468 | $newupgrade_function = 'xmldb_'.$module->name.'_upgrade'; |
469 | $result = $newupgrade_function($currmodule->version, $module); |
470 | } else { |
471 | $result = true; |
472 | } |
473 | |
474 | $currmodule = $DB->get_record('modules', array('name'=>$module->name)); |
475 | if ($currmodule->version < $module->version) { |
476 | // store version if not already there |
477 | upgrade_mod_savepoint($result, $module->version, $mod, false); |
478 | } |
479 | |
480 | /// Upgrade various components |
481 | update_capabilities($component); |
482 | events_update_definition($component); |
483 | message_update_providers($component); |
484 | |
485 | remove_dir($CFG->dataroot.'/cache', true); // flush cache |
486 | |
194cdca8 |
487 | $endcallback($component, false, $verbose); |
795a08ad |
488 | |
489 | } else if ($currmodule->version > $module->version) { |
490 | throw new downgrade_exception($component, $currmodule->version, $module->version); |
491 | } |
492 | } |
493 | } |
db9d4a3d |
494 | |
db9d4a3d |
495 | |
795a08ad |
496 | /** |
497 | * This function finds all available blocks and install them |
498 | * into blocks table or do all the upgrade process if newer. |
72fb21b6 |
499 | * |
500 | * @global object |
501 | * @global object |
795a08ad |
502 | */ |
194cdca8 |
503 | function upgrade_plugins_blocks($startcallback, $endcallback, $verbose) { |
795a08ad |
504 | global $CFG, $DB; |
505 | |
795a08ad |
506 | require_once($CFG->dirroot.'/blocks/moodleblock.class.php'); |
507 | |
508 | $blocktitles = array(); // we do not want duplicate titles |
509 | |
510 | //Is this a first install |
511 | $first_install = null; |
512 | |
513 | $blocks = get_list_of_plugins('blocks'); |
514 | |
515 | foreach ($blocks as $blockname) { |
516 | |
517 | if (is_null($first_install)) { |
518 | $first_install = ($DB->count_records('block') == 0); |
519 | } |
520 | |
521 | if ($blockname == 'NEWBLOCK') { // Someone has unzipped the template, ignore it |
522 | continue; |
db9d4a3d |
523 | } |
524 | |
795a08ad |
525 | $fullblock = $CFG->dirroot.'/blocks/'.$blockname; |
526 | $component = 'block/'.$blockname; |
db9d4a3d |
527 | |
795a08ad |
528 | if (!is_readable($fullblock.'/block_'.$blockname.'.php')) { |
529 | throw new plugin_defective_exception('block/'.$blockname, 'Missing main block class file.'); |
db9d4a3d |
530 | } |
795a08ad |
531 | require_once($fullblock.'/block_'.$blockname.'.php'); |
db9d4a3d |
532 | |
795a08ad |
533 | $classname = 'block_'.$blockname; |
534 | |
535 | if (!class_exists($classname)) { |
536 | throw new plugin_defective_exception($component, 'Can not load main class.'); |
537 | } |
538 | |
539 | $blockobj = new $classname; // This is what we 'll be testing |
540 | $blocktitle = $blockobj->get_title(); |
541 | |
542 | // OK, it's as we all hoped. For further tests, the object will do them itself. |
543 | if (!$blockobj->_self_test()) { |
544 | throw new plugin_defective_exception($component, 'Self test failed.'); |
545 | } |
546 | |
547 | $block = new object(); // This may be used to update the db below |
548 | $block->name = $blockname; // The name MUST match the directory |
549 | $block->version = $blockobj->get_version(); |
550 | $block->cron = !empty($blockobj->cron) ? $blockobj->cron : 0; |
551 | $block->multiple = $blockobj->instance_allow_multiple() ? 1 : 0; |
552 | |
553 | if (empty($block->version)) { |
554 | throw new plugin_defective_exception($component, 'Missing block version.'); |
555 | } |
556 | |
557 | $currblock = $DB->get_record('block', array('name'=>$block->name)); |
558 | |
559 | if (empty($currblock->version)) { // block not installed yet, so install it |
560 | // If it allows multiples, start with it enabled |
561 | |
562 | $conflictblock = array_search($blocktitle, $blocktitles); |
563 | if ($conflictblock !== false) { |
564 | // Duplicate block titles are not allowed, they confuse people |
565 | // AND PHP's associative arrays ;) |
566 | throw new plugin_defective_exception($component, get_string('blocknameconflict', '', (object)array('name'=>$block->name, 'conflict'=>$conflictblock))); |
567 | } |
194cdca8 |
568 | $startcallback($component, true, $verbose); |
795a08ad |
569 | |
570 | if (file_exists($fullblock.'/db/install.xml')) { |
571 | $DB->get_manager()->install_from_xmldb_file($fullblock.'/db/install.xml'); |
572 | } |
573 | $block->id = $DB->insert_record('block', $block); |
574 | |
575 | if (file_exists($fullblock.'/db/install.php')) { |
576 | require_once($fullblock.'/db/install.php'); |
577 | $post_install_function = 'xmldb_block_'.$blockname.'_install';; |
578 | $post_install_function(); |
579 | } |
580 | |
581 | $blocktitles[$block->name] = $blocktitle; |
582 | |
583 | // Install various components |
584 | update_capabilities($component); |
585 | events_update_definition($component); |
586 | message_update_providers($component); |
587 | |
194cdca8 |
588 | $endcallback($component, true, $verbose); |
795a08ad |
589 | |
590 | } else if ($currblock->version < $block->version) { |
194cdca8 |
591 | $startcallback($component, false, $verbose); |
795a08ad |
592 | |
593 | if (is_readable($fullblock.'/db/upgrade.php')) { |
594 | require_once($fullblock.'/db/upgrade.php'); // defines new upgrading function |
595 | $newupgrade_function = 'xmldb_block_'.$blockname.'_upgrade'; |
596 | $result = $newupgrade_function($currblock->version, $block); |
597 | } else { |
598 | $result = true; |
599 | } |
600 | |
601 | $currblock = $DB->get_record('block', array('name'=>$block->name)); |
602 | if ($currblock->version < $block->version) { |
603 | // store version if not already there |
604 | upgrade_block_savepoint($result, $block->version, $block->name, false); |
605 | } |
606 | |
607 | if ($currblock->cron != $block->cron) { |
608 | // update cron flag if needed |
609 | $currblock->cron = $block->cron; |
610 | $DB->update_record('block', $currblock); |
611 | } |
612 | |
613 | // Upgrade various componebts |
614 | events_update_definition($component); |
615 | update_capabilities($component); |
616 | message_update_providers($component); |
617 | |
194cdca8 |
618 | $endcallback($component, false, $verbose); |
795a08ad |
619 | |
620 | } else if ($currblock->version > $block->version) { |
621 | throw new downgrade_exception($component, $currblock->version, $block->version); |
622 | } |
623 | } |
624 | |
625 | |
626 | // Finally, if we are in the first_install of BLOCKS setup frontpage and admin page blocks |
627 | if ($first_install) { |
795a08ad |
628 | //Iterate over each course - there should be only site course here now |
629 | if ($courses = $DB->get_records('course')) { |
630 | foreach ($courses as $course) { |
9d1d606e |
631 | blocks_add_default_course_blocks($course); |
db9d4a3d |
632 | } |
633 | } |
795a08ad |
634 | |
9d1d606e |
635 | blocks_add_default_system_blocks(); |
795a08ad |
636 | } |
637 | } |
638 | |
639 | /** |
640 | * This function checks to see whether local database customisations are up-to-date |
641 | * by comparing $CFG->local_version to the variable $local_version defined in |
642 | * local/version.php. If not, it looks for a function called 'xmldb_local_upgrade' |
643 | * in a file called 'local/db/upgrade.php', and if it's there calls it with the |
644 | * appropiate $oldversion parameter. Then it updates $CFG->local_version. |
645 | * |
72fb21b6 |
646 | * @global object |
647 | * @global object |
795a08ad |
648 | */ |
649 | function upgrade_local_db($startcallback, $endcallback) { |
650 | global $CFG, $DB; |
651 | |
652 | // if we don't have code version, just return false |
653 | if (!file_exists($CFG->dirroot.'/local/version.php')) { |
654 | return; |
655 | } |
656 | |
657 | $local_version = null; |
658 | require($CFG->dirroot.'/local/version.php'); // Get code versions |
659 | |
660 | if (empty($CFG->local_version)) { // install |
661 | $startcallback('local', true); |
662 | |
663 | if (file_exists($CFG->dirroot.'/local/db/install.php')) { |
664 | require_once($CFG->dirroot.'/local/db/install.php'); |
665 | xmldb_local_install(); |
666 | } |
667 | set_config('local_version', $local_version); |
668 | |
669 | /// Install various components |
670 | events_update_definition('local'); |
671 | update_capabilities('local'); |
672 | message_update_providers('local'); |
673 | |
674 | $endcallback('local', true); |
675 | |
676 | } else if ($local_version > $CFG->local_version) { // upgrade! |
677 | $startcallback('local', false); |
678 | |
679 | if (file_exists($CFG->dirroot.'/local/db/upgrade.php')) { |
680 | require_once($CFG->dirroot.'/local/db/upgrade.php'); |
681 | xmldb_local_upgrade($CFG->local_version); |
682 | } |
683 | set_config('local_version', $local_version); |
684 | |
685 | /// Upgrade various components |
686 | events_update_definition('local'); |
687 | update_capabilities('local'); |
688 | message_update_providers('local'); |
689 | |
690 | $endcallback('local', false); |
691 | |
692 | } else if ($local_version < $CFG->local_version) { |
693 | throw new downgrade_exception('local', $CFG->local_version, $local_version); |
db9d4a3d |
694 | } |
695 | } |
696 | |
697 | |
72fb21b6 |
698 | /** |
699 | * upgrade logging functions |
700 | * |
701 | * @global object |
702 | */ |
db9d4a3d |
703 | |
795a08ad |
704 | function upgrade_handle_exception($ex, $plugin=null) { |
705 | global $CFG; |
706 | |
707 | if ($ex instanceof moodle_exception) { |
708 | $details = get_string($ex->errorcode, $ex->module, $ex->a)."<br />debugging:".$ex->debuginfo; |
709 | } else { |
710 | $details = get_string('generalexceptionmessage', 'error', $ex->getMessage()); |
711 | } |
712 | $info = "Exception: ".get_class($ex); |
713 | $backtrace = $ex->getTrace(); |
714 | $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex)); |
715 | array_unshift($backtrace, $place); |
716 | |
717 | /// first log upgrade error |
718 | upgrade_log(UPGRADE_LOG_ERROR, $plugin, $info, $details, $backtrace); |
719 | |
720 | // always turn on debugging - admins need to know what is going on |
721 | $CFG->debug = DEBUG_DEVELOPER; |
722 | |
723 | // now print the exception info as usually |
724 | if ($ex instanceof moodle_exception) { |
725 | _print_normal_error($ex->errorcode, $ex->module, $ex->a, $ex->link, $backtrace, $ex->debuginfo); |
726 | } else { |
727 | _print_normal_error('generalexceptionmessage', 'error', $ex->getMessage(), '', $backtrace); |
728 | } |
729 | |
730 | die; // not reached |
db9d4a3d |
731 | } |
732 | |
733 | /** |
734 | * Adds log entry into upgrade_log table |
735 | * |
72fb21b6 |
736 | * @global object |
737 | * @global object |
738 | * @global object |
db9d4a3d |
739 | * @param int $type UPGRADE_LOG_NORMAL, UPGRADE_LOG_NOTICE or UPGRADE_LOG_ERROR |
740 | * @param string $plugin plugin or null if main |
741 | * @param string $info short description text of log entry |
742 | * @param string $details long problem description |
743 | * @param string $backtrace string used for errors only |
744 | * @return void |
745 | */ |
746 | function upgrade_log($type, $plugin, $info, $details=null, $backtrace=null) { |
747 | global $DB, $USER, $CFG; |
748 | |
795a08ad |
749 | $plugin = ($plugin==='moodle') ? null : $plugin; |
750 | |
b4154c2d |
751 | $backtrace = print_backtrace($backtrace, true, true); |
db9d4a3d |
752 | |
753 | $version = null; |
754 | |
755 | //first try to find out current version number |
795a08ad |
756 | if (empty($plugin) or $plugin === 'moodle') { |
db9d4a3d |
757 | //main |
758 | $version = $CFG->version; |
759 | |
795a08ad |
760 | } else if ($plugin === 'local') { |
761 | //customisation |
762 | $version = $CFG->local_version; |
763 | |
764 | } else if (strpos($plugin, 'mod/') === 0) { |
db9d4a3d |
765 | try { |
766 | $modname = substr($plugin, strlen('mod/')); |
767 | $version = $DB->get_field('modules', 'version', array('name'=>$modname)); |
795a08ad |
768 | $version = ($version === false) ? null : $version; |
db9d4a3d |
769 | } catch (Exception $ignored) { |
770 | } |
771 | |
795a08ad |
772 | } else if (strpos($plugin, 'block/') === 0) { |
db9d4a3d |
773 | try { |
795a08ad |
774 | $blockname = substr($plugin, strlen('block/')); |
db9d4a3d |
775 | if ($block = $DB->get_record('block', array('name'=>$blockname))) { |
776 | $version = $block->version; |
777 | } |
778 | } catch (Exception $ignored) { |
779 | } |
795a08ad |
780 | |
781 | } else { |
782 | $pluginversion = get_config(str_replace('/', '_', $plugin), 'version'); |
783 | if (!empty($pluginversion)) { |
784 | $version = $pluginversion; |
785 | } |
db9d4a3d |
786 | } |
787 | |
788 | $log = new object(); |
789 | $log->type = $type; |
790 | $log->plugin = $plugin; |
791 | $log->version = $version; |
792 | $log->info = $info; |
793 | $log->details = $details; |
794 | $log->backtrace = $backtrace; |
795 | $log->userid = $USER->id; |
796 | $log->timemodified = time(); |
db9d4a3d |
797 | try { |
798 | $DB->insert_record('upgrade_log', $log); |
799 | } catch (Exception $ignored) { |
795a08ad |
800 | // possible during install or 2.0 upgrade |
db9d4a3d |
801 | } |
802 | } |
803 | |
804 | /** |
805 | * Marks start of upgrade, blocks any other access to site. |
806 | * The upgrade is finished at the end of script or after timeout. |
72fb21b6 |
807 | * |
808 | * @global object |
809 | * @global object |
810 | * @global object |
db9d4a3d |
811 | */ |
812 | function upgrade_started($preinstall=false) { |
c13a5e71 |
813 | global $CFG, $DB, $PAGE; |
db9d4a3d |
814 | |
815 | static $started = false; |
816 | |
817 | if ($preinstall) { |
818 | ignore_user_abort(true); |
819 | upgrade_setup_debug(true); |
820 | |
821 | } else if ($started) { |
822 | upgrade_set_timeout(120); |
823 | |
824 | } else { |
c13a5e71 |
825 | if (!CLI_SCRIPT and !$PAGE->headerprinted) { |
db9d4a3d |
826 | $strupgrade = get_string('upgradingversion', 'admin'); |
827 | |
9ace5094 |
828 | print_header($strupgrade.' - Moodle '.$CFG->target_release, $strupgrade, |
db9d4a3d |
829 | build_navigation(array(array('name' => $strupgrade, 'link' => null, 'type' => 'misc'))), '', |
830 | upgrade_get_javascript(), false, ' ', ' '); |
831 | } |
832 | |
833 | ignore_user_abort(true); |
834 | register_shutdown_function('upgrade_finished_handler'); |
835 | upgrade_setup_debug(true); |
836 | set_config('upgraderunning', time()+300); |
837 | $started = true; |
838 | } |
839 | } |
840 | |
841 | /** |
842 | * Internal function - executed if upgrade interruped. |
843 | */ |
844 | function upgrade_finished_handler() { |
845 | upgrade_finished(); |
846 | } |
847 | |
848 | /** |
849 | * Indicates upgrade is finished. |
850 | * |
851 | * This function may be called repeatedly. |
72fb21b6 |
852 | * |
853 | * @global object |
854 | * @global object |
db9d4a3d |
855 | */ |
856 | function upgrade_finished($continueurl=null) { |
857 | global $CFG, $DB; |
858 | |
859 | if (!empty($CFG->upgraderunning)) { |
860 | unset_config('upgraderunning'); |
861 | upgrade_setup_debug(false); |
862 | ignore_user_abort(false); |
863 | if ($continueurl) { |
864 | print_continue($continueurl); |
9ace5094 |
865 | print_footer('upgrade'); |
db9d4a3d |
866 | die; |
867 | } |
868 | } |
869 | } |
870 | |
72fb21b6 |
871 | /** |
872 | * @global object |
873 | * @global object |
874 | */ |
db9d4a3d |
875 | function upgrade_setup_debug($starting) { |
876 | global $CFG, $DB; |
877 | |
878 | static $originaldebug = null; |
879 | |
880 | if ($starting) { |
881 | if ($originaldebug === null) { |
882 | $originaldebug = $DB->get_debug(); |
883 | } |
884 | if (!empty($CFG->upgradeshowsql)) { |
885 | $DB->set_debug(true); |
886 | } |
887 | } else { |
888 | $DB->set_debug($originaldebug); |
889 | } |
890 | } |
891 | |
72fb21b6 |
892 | /** |
893 | * @global object |
894 | */ |
90509582 |
895 | function print_upgrade_reload($url) { |
896 | global $CFG; |
897 | |
898 | echo "<br />"; |
899 | echo '<div class="continuebutton">'; |
900 | echo '<a href="'.$url.'" title="'.get_string('reload').'" ><img src="'.$CFG->pixpath.'/i/reload.gif" alt="" /> '.get_string('reload').'</a>'; |
901 | echo '</div><br />'; |
902 | } |
903 | |
db9d4a3d |
904 | function print_upgrade_separator() { |
905 | if (!CLI_SCRIPT) { |
906 | echo '<hr />'; |
907 | } |
908 | } |
909 | |
795a08ad |
910 | /** |
911 | * Default start upgrade callback |
912 | * @param string $plugin |
913 | * @param bool $installation true if installation, false menas upgrade |
914 | */ |
194cdca8 |
915 | function print_upgrade_part_start($plugin, $installation, $verbose) { |
795a08ad |
916 | if (empty($plugin) or $plugin == 'moodle') { |
917 | upgrade_started($installation); // does not store upgrade running flag yet |
194cdca8 |
918 | if ($verbose) { |
919 | print_heading(get_string('coresystem')); |
920 | } |
795a08ad |
921 | } else { |
922 | upgrade_started(); |
194cdca8 |
923 | if ($verbose) { |
924 | print_heading($plugin); |
925 | } |
795a08ad |
926 | } |
927 | if ($installation) { |
928 | if (empty($plugin) or $plugin == 'moodle') { |
929 | // no need to log - log table not yet there ;-) |
930 | } else { |
931 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin installation'); |
932 | } |
933 | } else { |
934 | if (empty($plugin) or $plugin == 'moodle') { |
935 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting core upgrade'); |
936 | } else { |
937 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Starting plugin upgrade'); |
938 | } |
939 | } |
940 | } |
941 | |
942 | /** |
943 | * Default end upgrade callback |
944 | * @param string $plugin |
945 | * @param bool $installation true if installation, false menas upgrade |
946 | */ |
194cdca8 |
947 | function print_upgrade_part_end($plugin, $installation, $verbose) { |
795a08ad |
948 | upgrade_started(); |
949 | if ($installation) { |
950 | if (empty($plugin) or $plugin == 'moodle') { |
951 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core installed'); |
952 | } else { |
953 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin installed'); |
954 | } |
955 | } else { |
956 | if (empty($plugin) or $plugin == 'moodle') { |
957 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Core upgraded'); |
958 | } else { |
959 | upgrade_log(UPGRADE_LOG_NORMAL, $plugin, 'Plugin upgraded'); |
960 | } |
961 | } |
194cdca8 |
962 | if ($verbose) { |
963 | notify(get_string('success'), 'notifysuccess'); |
964 | print_upgrade_separator(); |
96db14f9 |
965 | } |
966 | } |
967 | |
72fb21b6 |
968 | /** |
969 | * @global object |
970 | */ |
db9d4a3d |
971 | function upgrade_get_javascript() { |
972 | global $CFG; |
2d6b0b04 |
973 | return '<script type="text/javascript" src="'.$CFG->wwwroot.'/lib/scroll_to_page_end.js"></script>'; |
db9d4a3d |
974 | } |
975 | |
976 | |
977 | /** |
978 | * Try to upgrade the given language pack (or current language) |
72fb21b6 |
979 | * @global object |
db9d4a3d |
980 | */ |
981 | function upgrade_language_pack($lang='') { |
982 | global $CFG; |
983 | |
984 | if (empty($lang)) { |
985 | $lang = current_language(); |
986 | } |
987 | |
988 | if ($lang == 'en_utf8') { |
989 | return true; // Nothing to do |
990 | } |
991 | |
551fe0e5 |
992 | upgrade_started(false); |
993 | print_heading(get_string('langimport', 'admin').': '.$lang); |
db9d4a3d |
994 | |
995 | @mkdir ($CFG->dataroot.'/temp/'); //make it in case it's a fresh install, it might not be there |
996 | @mkdir ($CFG->dataroot.'/lang/'); |
997 | |
998 | require_once($CFG->libdir.'/componentlib.class.php'); |
999 | |
1000 | if ($cd = new component_installer('http://download.moodle.org', 'lang16', $lang.'.zip', 'languages.md5', 'lang')) { |
1001 | $status = $cd->install(); //returns COMPONENT_(ERROR | UPTODATE | INSTALLED) |
1002 | |
1003 | if ($status == COMPONENT_INSTALLED) { |
db9d4a3d |
1004 | @unlink($CFG->dataroot.'/cache/languages'); |
551fe0e5 |
1005 | if ($parentlang = get_parent_language($lang)) { |
1006 | if ($cd = new component_installer('http://download.moodle.org', 'lang16', $parentlang.'.zip', 'languages.md5', 'lang')) { |
1007 | $cd->install(); |
1008 | } |
1009 | } |
1010 | notify(get_string('success'), 'notifysuccess'); |
db9d4a3d |
1011 | } |
1012 | } |
1013 | |
551fe0e5 |
1014 | print_upgrade_separator(); |
db9d4a3d |
1015 | } |
8580535b |
1016 | |
1017 | /** |
1018 | * Install core moodle tables and initialize |
1019 | * @param float $version target version |
1020 | * @param bool $verbose |
1021 | * @return void, may throw exception |
1022 | */ |
1023 | function install_core($version, $verbose) { |
1024 | global $CFG, $DB; |
1025 | |
1026 | try { |
c3d0e149 |
1027 | set_time_limit(600); |
194cdca8 |
1028 | print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag |
8580535b |
1029 | |
1030 | $DB->get_manager()->install_from_xmldb_file("$CFG->libdir/db/install.xml"); |
1031 | upgrade_started(); // we want the flag to be stored in config table ;-) |
1032 | |
1033 | // set all core default records and default settings |
1034 | require_once("$CFG->libdir/db/install.php"); |
1035 | xmldb_main_install(); |
1036 | |
1037 | // store version |
1038 | upgrade_main_savepoint(true, $version, false); |
1039 | |
1040 | // Continue with the instalation |
1041 | events_update_definition('moodle'); |
1042 | message_update_providers('moodle'); |
1043 | message_update_providers('message'); |
1044 | |
1045 | // Write default settings unconditionlly |
1046 | admin_apply_default_settings(NULL, true); |
1047 | |
194cdca8 |
1048 | print_upgrade_part_end(null, true, $verbose); |
8580535b |
1049 | } catch (exception $ex) { |
1050 | upgrade_handle_exception($ex); |
1051 | } |
1052 | } |
1053 | |
1054 | /** |
1055 | * Upgrade moodle core |
1056 | * @param float $version target version |
1057 | * @param bool $verbose |
1058 | * @return void, may throw exception |
1059 | */ |
1060 | function upgrade_core($version, $verbose) { |
1061 | global $CFG; |
1062 | |
3316fe24 |
1063 | require_once($CFG->libdir.'/db/upgrade.php'); // Defines upgrades |
3316fe24 |
1064 | |
8580535b |
1065 | try { |
1066 | // Upgrade current language pack if we can |
1067 | if (empty($CFG->skiplangupgrade)) { |
1068 | upgrade_language_pack(false); |
1069 | } |
1070 | |
194cdca8 |
1071 | print_upgrade_part_start('moodle', false, $verbose); |
8580535b |
1072 | |
1073 | $result = xmldb_main_upgrade($CFG->version); |
1074 | if ($version > $CFG->version) { |
1075 | // store version if not already there |
1076 | upgrade_main_savepoint($result, $version, false); |
1077 | } |
1078 | |
1079 | // perform all other component upgrade routines |
1080 | update_capabilities('moodle'); |
1081 | events_update_definition('moodle'); |
1082 | message_update_providers('moodle'); |
1083 | message_update_providers('message'); |
1084 | |
1085 | remove_dir($CFG->dataroot . '/cache', true); // flush cache |
1086 | |
194cdca8 |
1087 | print_upgrade_part_end('moodle', false, $verbose); |
8580535b |
1088 | } catch (Exception $ex) { |
1089 | upgrade_handle_exception($ex); |
1090 | } |
1091 | } |
1092 | |
1093 | /** |
1094 | * Upgrade/install other parts of moodle |
1095 | * @param bool $verbose |
1096 | * @return void, may throw exception |
1097 | */ |
1098 | function upgrade_noncore($verbose) { |
1099 | global $CFG; |
1100 | |
8580535b |
1101 | // upgrade all plugins types |
1102 | try { |
1103 | $plugintypes = get_plugin_types(); |
1104 | foreach ($plugintypes as $type=>$location) { |
194cdca8 |
1105 | upgrade_plugins($type, $location, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose); |
8580535b |
1106 | } |
1107 | } catch (Exception $ex) { |
1108 | upgrade_handle_exception($ex); |
1109 | } |
1110 | |
1111 | // Check for changes to RPC functions |
1112 | if ($CFG->mnet_dispatcher_mode != 'off') { |
1113 | try { |
1114 | // this needs a full rewrite, sorry to mention that :-( |
1115 | // we have to make it part of standard WS framework |
1116 | require_once("$CFG->dirroot/$CFG->admin/mnet/adminlib.php"); |
1117 | upgrade_RPC_functions(); // Return here afterwards |
1118 | } catch (Exception $ex) { |
1119 | upgrade_handle_exception($ex); |
1120 | } |
1121 | } |
1122 | |
1123 | // Check for local database customisations |
1124 | try { |
1125 | require_once("$CFG->dirroot/lib/locallib.php"); |
194cdca8 |
1126 | upgrade_local_db('print_upgrade_part_start', 'print_upgrade_part_end', $verbose); |
8580535b |
1127 | } catch (Exception $ex) { |
1128 | upgrade_handle_exception($ex); |
1129 | } |
1130 | } |
3316fe24 |
1131 | |
1132 | /** |
1133 | * Checks if the main tables have been installed yet or not. |
1134 | * @return bool |
1135 | */ |
1136 | function core_tables_exist() { |
1137 | global $DB; |
1138 | |
1139 | if (!$tables = $DB->get_tables() ) { // No tables yet at all. |
1140 | return false; |
1141 | |
1142 | } else { // Check for missing main tables |
1143 | $mtables = array('config', 'course', 'groupings'); // some tables used in 1.9 and 2.0, preferable something from the start and end of install.xml |
1144 | foreach ($mtables as $mtable) { |
1145 | if (!in_array($mtable, $tables)) { |
1146 | return false; |
1147 | } |
1148 | } |
1149 | return true; |
1150 | } |
ba04999c |
1151 | } |