Commit | Line | Data |
---|---|---|
95feaf96 | 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/>. | |
2f13f94c | 17 | |
2f13f94c | 18 | /** |
9d068cd6 | 19 | * Functions to support installation process |
95feaf96 | 20 | * |
b7315f35 | 21 | * @package core |
95feaf96 | 22 | * @subpackage install |
23 | * @copyright 2009 Petr Skoda (http://skodak.org) | |
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
2f13f94c | 25 | */ |
2f13f94c | 26 | |
460ebd6c PS |
27 | defined('MOODLE_INTERNAL') || die(); |
28 | ||
226617ea | 29 | /** INSTALL_WELCOME = 0 */ |
3b093310 | 30 | define('INSTALL_WELCOME', 0); |
226617ea | 31 | /** INSTALL_ENVIRONMENT = 1 */ |
64c368e3 | 32 | define('INSTALL_ENVIRONMENT', 1); |
226617ea | 33 | /** INSTALL_PATHS = 2 */ |
64c368e3 | 34 | define('INSTALL_PATHS', 2); |
226617ea | 35 | /** INSTALL_DOWNLOADLANG = 3 */ |
64c368e3 | 36 | define('INSTALL_DOWNLOADLANG', 3); |
226617ea | 37 | /** INSTALL_DATABASETYPE = 4 */ |
64c368e3 | 38 | define('INSTALL_DATABASETYPE', 4); |
226617ea | 39 | /** INSTALL_DATABASE = 5 */ |
64c368e3 | 40 | define('INSTALL_DATABASE', 5); |
226617ea | 41 | /** INSTALL_SAVE = 6 */ |
64c368e3 | 42 | define('INSTALL_SAVE', 6); |
3b093310 | 43 | |
11e7b506 | 44 | /** |
226617ea | 45 | * Tries to detect the right www root setting. |
11e7b506 | 46 | * @return string detected www root |
47 | */ | |
48 | function install_guess_wwwroot() { | |
49 | $wwwroot = ''; | |
50 | if (empty($_SERVER['HTTPS']) or $_SERVER['HTTPS'] == 'off') { | |
51 | $wwwroot .= 'http://'; | |
52 | } else { | |
53 | $wwwroot .= 'https://'; | |
54 | } | |
55 | $hostport = explode(':', $_SERVER['HTTP_HOST']); | |
56 | $wwwroot .= reset($hostport); | |
57 | if ($_SERVER['SERVER_PORT'] != 80 and $_SERVER['SERVER_PORT'] != '443') { | |
58 | $wwwroot .= ':'.$_SERVER['SERVER_PORT']; | |
59 | } | |
60 | $wwwroot .= $_SERVER['SCRIPT_NAME']; | |
61 | ||
62 | list($wwwroot, $xtra) = explode('/install.php', $wwwroot); | |
63 | ||
64 | return $wwwroot; | |
65 | } | |
66 | ||
95feaf96 | 67 | /** |
68 | * Copy of @see{ini_get_bool()} | |
69 | * @param string $ini_get_arg | |
70 | * @return bool | |
71 | */ | |
3b093310 | 72 | function install_ini_get_bool($ini_get_arg) { |
73 | $temp = ini_get($ini_get_arg); | |
74 | ||
75 | if ($temp == '1' or strtolower($temp) == 'on') { | |
76 | return true; | |
77 | } | |
78 | return false; | |
79 | } | |
80 | ||
95feaf96 | 81 | /** |
82 | * Print help button | |
83 | * @param string $url | |
84 | * @param string $titel | |
85 | * @return void | |
86 | */ | |
3b093310 | 87 | function install_helpbutton($url, $title='') { |
88 | if ($title == '') { | |
89 | $title = get_string('help'); | |
90 | } | |
91 | echo "<a href=\"javascript:void(0)\" "; | |
92 | echo "onclick=\"return window.open('$url','Help','menubar=0,location=0,scrollbars,resizable,width=500,height=400')\""; | |
93 | echo ">"; | |
94 | echo "<img src=\"pix/help.gif\" class=\"iconhelp\" alt=\"$title\" title=\"$title\"/>"; | |
95 | echo "</a>\n"; | |
96 | } | |
97 | ||
95feaf96 | 98 | /** |
99 | * This is in function because we want the /install.php to parse in PHP4 | |
226617ea | 100 | * |
101 | * @param object $database | |
102 | * @param string $dbhsot | |
103 | * @param string $dbuser | |
104 | * @param string $dbpass | |
105 | * @param string $dbname | |
106 | * @param string $prefix | |
107 | * @param mixed $dboptions | |
108 | * @return string | |
95feaf96 | 109 | */ |
768408e8 | 110 | function install_db_validate($database, $dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions) { |
3b093310 | 111 | try { |
112 | try { | |
113 | $database->connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); | |
114 | } catch (moodle_exception $e) { | |
115 | // let's try to create new database | |
116 | if ($database->create_database($dbhost, $dbuser, $dbpass, $dbname, $dboptions)) { | |
117 | $database->connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, $dboptions); | |
118 | } else { | |
119 | throw $e; | |
120 | } | |
121 | } | |
122 | return ''; | |
123 | } catch (dml_exception $ex) { | |
124 | return get_string($ex->errorcode, $ex->module, $ex->a).'<br />'.$ex->debuginfo; | |
125 | } | |
126 | } | |
127 | ||
dc47d704 | 128 | /** |
129 | * Returns content of config.php file. | |
6189e63d DM |
130 | * |
131 | * Uses PHP_EOL for generating proper end of lines for the given platform. | |
132 | * | |
dc47d704 | 133 | * @param moodle_database $database database instance |
134 | * @param object $cfg copy of $CFG | |
dc47d704 | 135 | * @return string |
136 | */ | |
3b5ff37f | 137 | function install_generate_configphp($database, $cfg) { |
59ba2bb0 | 138 | $configphp = '<?php // Moodle configuration file' . PHP_EOL . PHP_EOL; |
dc47d704 | 139 | |
6189e63d DM |
140 | $configphp .= 'unset($CFG);' . PHP_EOL; |
141 | $configphp .= '$CFG = new stdClass();' . PHP_EOL . PHP_EOL; // prevent PHP5 strict warnings | |
dc47d704 | 142 | |
143 | $dbconfig = $database->export_dbconfig(); | |
144 | ||
145 | foreach ($dbconfig as $key=>$value) { | |
146 | $key = str_pad($key, 9); | |
6189e63d | 147 | $configphp .= '$CFG->'.$key.' = '.var_export($value, true) . ';' . PHP_EOL; |
dc47d704 | 148 | } |
6189e63d | 149 | $configphp .= PHP_EOL; |
dc47d704 | 150 | |
6189e63d | 151 | $configphp .= '$CFG->wwwroot = '.var_export($cfg->wwwroot, true) . ';' . PHP_EOL ; |
dc47d704 | 152 | |
3b5ff37f | 153 | $configphp .= '$CFG->dataroot = '.var_export($cfg->dataroot, true) . ';' . PHP_EOL; |
dc47d704 | 154 | |
6189e63d | 155 | $configphp .= '$CFG->admin = '.var_export($cfg->admin, true) . ';' . PHP_EOL . PHP_EOL; |
dc47d704 | 156 | |
7be67681 DM |
157 | if (empty($cfg->directorypermissions)) { |
158 | $chmod = '02777'; | |
159 | } else { | |
160 | $chmod = '0' . decoct($cfg->directorypermissions); | |
161 | } | |
162 | $configphp .= '$CFG->directorypermissions = ' . $chmod . ';' . PHP_EOL . PHP_EOL; | |
dc47d704 | 163 | |
6189e63d | 164 | $configphp .= '$CFG->passwordsaltmain = '.var_export(complex_random_string(), true) . ';' . PHP_EOL . PHP_EOL; |
4c1ba3ff | 165 | |
3b5ff37f | 166 | $configphp .= 'require_once(dirname(__FILE__) . \'/lib/setup.php\');' . PHP_EOL . PHP_EOL; |
6189e63d DM |
167 | $configphp .= '// There is no php closing tag in this file,' . PHP_EOL; |
168 | $configphp .= '// it is intentional because it prevents trailing whitespace problems!' . PHP_EOL; | |
dc47d704 | 169 | |
170 | return $configphp; | |
171 | } | |
172 | ||
95feaf96 | 173 | /** |
174 | * Prints complete help page used during installation. | |
175 | * Does not return. | |
226617ea | 176 | * |
177 | * @global object | |
95feaf96 | 178 | * @param string $help |
179 | */ | |
3b093310 | 180 | function install_print_help_page($help) { |
3a915b06 | 181 | global $CFG, $OUTPUT; //TODO: MUST NOT USE $OUTPUT HERE!!! |
3b093310 | 182 | |
183 | @header('Content-Type: text/html; charset=UTF-8'); | |
184 | @header('Cache-Control: no-store, no-cache, must-revalidate'); | |
185 | @header('Cache-Control: post-check=0, pre-check=0', false); | |
186 | @header('Pragma: no-cache'); | |
187 | @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); | |
188 | @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); | |
189 | ||
190 | echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; | |
191 | echo '<html dir="'.(right_to_left() ? 'rtl' : 'ltr').'"> | |
192 | <head> | |
78946b9b | 193 | <link rel="shortcut icon" href="theme/standard/pix/favicon.ico" /> |
3b093310 | 194 | <link rel="stylesheet" type="text/css" href="'.$CFG->wwwroot.'/install.php?css=1" /> |
195 | <title>'.get_string('installation','install').'</title> | |
196 | <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> | |
197 | <meta http-equiv="pragma" content="no-cache" /> | |
198 | <meta http-equiv="expires" content="0" />'; | |
199 | ||
200 | echo '</head><body>'; | |
201 | switch ($help) { | |
202 | case 'phpversionhelp': | |
203 | print_string($help, 'install', phpversion()); | |
204 | break; | |
205 | case 'memorylimithelp': | |
206 | print_string($help, 'install', get_memory_limit()); | |
207 | break; | |
208 | default: | |
209 | print_string($help, 'install'); | |
2f13f94c | 210 | } |
3a915b06 | 211 | echo $OUTPUT->close_window_button(); //TODO: MUST NOT USE $OUTPUT HERE!!! |
3b093310 | 212 | echo '</body></html>'; |
213 | die; | |
2f13f94c | 214 | } |
215 | ||
95feaf96 | 216 | /** |
8bb70111 | 217 | * Prints installation page header, we can no use weblib yet in installer. |
226617ea | 218 | * |
219 | * @global object | |
95feaf96 | 220 | * @param array $config |
221 | * @param string $stagename | |
222 | * @param string $heading | |
223 | * @param string $stagetext | |
224 | * @return void | |
225 | */ | |
3b093310 | 226 | function install_print_header($config, $stagename, $heading, $stagetext) { |
227 | global $CFG; | |
2f13f94c | 228 | |
3b093310 | 229 | @header('Content-Type: text/html; charset=UTF-8'); |
230 | @header('Cache-Control: no-store, no-cache, must-revalidate'); | |
231 | @header('Cache-Control: post-check=0, pre-check=0', false); | |
232 | @header('Pragma: no-cache'); | |
233 | @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); | |
234 | @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); | |
235 | ||
236 | echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; | |
237 | echo '<html dir="'.(right_to_left() ? 'rtl' : 'ltr').'"> | |
238 | <head> | |
78946b9b | 239 | <link rel="shortcut icon" href="theme/standard/pix/favicon.ico" />'; |
3b093310 | 240 | |
90723839 | 241 | $sheets = array('pagelayout','core'); |
3b093310 | 242 | $csss = array(); |
243 | foreach ($sheets as $sheet) { | |
90723839 | 244 | $csss[] = $CFG->wwwroot.'/theme/base/style/'.$sheet.'.css'; |
3b093310 | 245 | } |
90723839 | 246 | $sheets = array('core', 'css3'); |
3b093310 | 247 | foreach ($sheets as $sheet) { |
90723839 | 248 | $csss[] = $CFG->wwwroot.'/theme/standard/style/'.$sheet.'.css'; |
2f13f94c | 249 | } |
3b093310 | 250 | foreach ($csss as $css) { |
251 | echo '<link rel="stylesheet" type="text/css" href="'.$css.'" />'."\n"; | |
252 | } | |
253 | ||
254 | echo '<link rel="stylesheet" type="text/css" href="'.$CFG->wwwroot.'/install.php?css=1" /> | |
9ace5094 | 255 | <title>'.get_string('installation','install').' - Moodle '.$CFG->target_release.'</title> |
3b093310 | 256 | <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> |
257 | <meta http-equiv="pragma" content="no-cache" /> | |
258 | <meta http-equiv="expires" content="0" />'; | |
259 | ||
260 | echo '</head><body class="notloggedin"> | |
261 | <div id="page" class="stage'.$config->stage.'"> | |
90723839 SH |
262 | <div id="page-header"> |
263 | <div id="header" class=" clearfix"> | |
264 | <h1 class="headermain">'.get_string('installation','install').'</h1> | |
265 | <div class="headermenu"> </div> | |
266 | </div> | |
267 | <div class="navbar clearfix"> | |
268 | <div class="breadcrumb"> | |
269 | <ul><li class="first">'.$stagename.'</li></ul> | |
270 | </div> | |
271 | <div class="navbutton"> </div> | |
3b093310 | 272 | </div> |
3b093310 | 273 | </div> |
274 | <!-- END OF HEADER --> | |
275 | <div id="installdiv">'; | |
2f13f94c | 276 | |
3b093310 | 277 | echo '<h2>'.$heading.'</h2>'; |
278 | ||
279 | if ($stagetext !== '') { | |
280 | echo '<div class="stage generalbox box">'; | |
281 | echo $stagetext; | |
282 | echo '</div>'; | |
283 | } | |
284 | // main | |
285 | echo '<form id="installform" method="post" action="install.php"><fieldset>'; | |
286 | foreach ($config as $name=>$value) { | |
287 | echo '<input type="hidden" name="'.$name.'" value="'.s($value).'" />'; | |
288 | } | |
2f13f94c | 289 | } |
290 | ||
95feaf96 | 291 | /** |
292 | * Prints installation page header, we can no use weblib yet in isntaller. | |
226617ea | 293 | * |
294 | * @global object | |
95feaf96 | 295 | * @param array $config |
296 | * @param bool $reload print reload button instead of next | |
297 | * @return void | |
298 | */ | |
3b093310 | 299 | function install_print_footer($config, $reload=false) { |
300 | global $CFG; | |
301 | ||
302 | if ($config->stage > INSTALL_WELCOME) { | |
4d928665 | 303 | $first = '<input type="submit" id="previousbutton" name="previous" value="« '.s(get_string('previous')).'" />'; |
3b093310 | 304 | } else { |
85ed076b | 305 | $first = '<input type="submit" id="previousbutton" name="next" value="'.s(get_string('reload')).'" />'; |
4d928665 | 306 | $first .= '<script type="text/javascript"> |
307 | //<![CDATA[ | |
308 | var first = document.getElementById("previousbutton"); | |
309 | first.style.visibility = "hidden"; | |
310 | //]]> | |
311 | </script> | |
312 | '; | |
3b093310 | 313 | } |
314 | ||
315 | if ($reload) { | |
85ed076b | 316 | $next = '<input type="submit" id="nextbutton" name="next" value="'.s(get_string('reload')).'" />'; |
3b093310 | 317 | } else { |
4d928665 | 318 | $next = '<input type="submit" id="nextbutton" name="next" value="'.s(get_string('next')).' »" />'; |
3b093310 | 319 | } |
320 | ||
321 | echo '</fieldset><fieldset id="nav_buttons">'.$first.$next.'</fieldset>'; | |
322 | ||
323 | $homelink = '<div class="sitelink">'. | |
9ace5094 | 324 | '<a title="Moodle '. $CFG->target_release .'" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">'. |
3b093310 | 325 | '<img style="width:100px;height:30px" src="pix/moodlelogo.gif" alt="moodlelogo" /></a></div>'; |
326 | ||
327 | echo '</form></div>'; | |
328 | echo '<div id="footer"><hr />'.$homelink.'</div>'; | |
329 | echo '</div></body></html>'; | |
330 | } | |
331 | ||
332 | ||
95feaf96 | 333 | /** |
334 | * Prints css needed on installation page, tries to look like the rest of installation. | |
335 | * Does not return. | |
226617ea | 336 | * |
337 | * @global object | |
95feaf96 | 338 | */ |
3b093310 | 339 | function install_css_styles() { |
340 | global $CFG; | |
341 | ||
342 | @header('Content-type: text/css'); // Correct MIME type | |
343 | @header('Cache-Control: no-store, no-cache, must-revalidate'); | |
344 | @header('Cache-Control: post-check=0, pre-check=0', false); | |
345 | @header('Pragma: no-cache'); | |
346 | @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); | |
347 | @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); | |
348 | ||
da0c0e25 | 349 | //TODO: add rtl support here, make it match new default theme MDL-21149 |
3b093310 | 350 | |
351 | echo ' | |
352 | ||
353 | h2 { | |
354 | text-align:center; | |
355 | } | |
356 | ||
357 | #installdiv { | |
358 | width: 800px; | |
359 | margin-left:auto; | |
360 | margin-right:auto; | |
361 | } | |
362 | ||
363 | #installdiv dt { | |
364 | font-weight: bold; | |
365 | } | |
366 | ||
367 | #installdiv dd { | |
368 | padding-bottom: 0.5em; | |
369 | } | |
370 | ||
371 | .stage { | |
372 | margin-top: 2em; | |
373 | margin-bottom: 2em; | |
374 | width: 100%; | |
375 | padding:25px; | |
376 | } | |
377 | ||
378 | #installform { | |
379 | width: 100%; | |
380 | } | |
381 | ||
382 | #nav_buttons input { | |
383 | margin: 5px; | |
384 | } | |
385 | ||
386 | #envresult { | |
387 | text-align:left; | |
388 | width: auto; | |
389 | margin-left:10em; | |
390 | } | |
391 | ||
392 | #envresult dd { | |
393 | color: red; | |
394 | } | |
395 | ||
396 | .formrow { | |
397 | clear:both; | |
398 | text-align:left; | |
399 | padding: 8px; | |
400 | } | |
401 | ||
402 | .formrow label.formlabel { | |
403 | display:block; | |
404 | float:left; | |
405 | width: 260px; | |
406 | margin-right:5px; | |
407 | text-align:right; | |
408 | } | |
409 | ||
410 | .formrow .forminput { | |
411 | display:block; | |
412 | float:left; | |
413 | } | |
414 | ||
415 | fieldset { | |
416 | text-align:center; | |
417 | border:none; | |
418 | } | |
419 | ||
420 | .hint { | |
421 | display:block; | |
422 | clear:both; | |
423 | padding-left: 265px; | |
424 | color: red; | |
425 | } | |
426 | ||
427 | .configphp { | |
428 | text-align:left; | |
429 | background-color:white; | |
430 | padding:1em; | |
431 | width:95%; | |
432 | } | |
433 | ||
64c368e3 | 434 | .stage6 .stage { |
3b093310 | 435 | font-weight: bold; |
436 | color: red; | |
437 | } | |
438 | ||
439 | '; | |
440 | ||
441 | die; | |
2f13f94c | 442 | } |
b7315f35 PS |
443 | |
444 | /** | |
445 | * Install Moodle DB, | |
446 | * config.php must exist, there must not be any tables in db yet. | |
447 | * | |
448 | * @param array $options adminpass is mandatory | |
449 | * @param bool $interactive | |
450 | * @return void | |
451 | */ | |
452 | function install_cli_database(array $options, $interactive) { | |
453 | global $CFG, $DB; | |
454 | require_once($CFG->libdir.'/environmentlib.php'); | |
455 | require_once($CFG->libdir.'/upgradelib.php'); | |
456 | ||
457 | // show as much debug as possible | |
458 | @error_reporting(1023); | |
459 | @ini_set('display_errors', '1'); | |
460 | $CFG->debug = 38911; | |
461 | $CFG->debugdisplay = true; | |
462 | ||
463 | $CFG->version = ''; | |
464 | $CFG->release = ''; | |
465 | ||
466 | // read $version and $release | |
467 | require($CFG->dirroot.'/version.php'); | |
468 | ||
469 | if ($DB->get_tables() ) { | |
470 | cli_error(get_string('clitablesexist', 'install')); | |
471 | } | |
472 | ||
473 | if (empty($options['adminpass'])) { | |
474 | cli_error('Missing required admin password'); | |
475 | } | |
476 | ||
477 | // test environment first | |
478 | if (!check_moodle_environment($version, $environment_results, false, ENV_SELECT_RELEASE)) { | |
479 | $errors = environment_get_errors($environment_results); | |
480 | cli_heading(get_string('environment', 'admin')); | |
481 | foreach ($errors as $error) { | |
482 | list($info, $report) = $error; | |
483 | echo "!! $info !!\n$report\n\n"; | |
484 | } | |
485 | //remove config.php, we do not want half finished upgrades! | |
486 | unlink($configfile); | |
487 | exit(1); | |
488 | } | |
489 | ||
490 | if (!$DB->setup_is_unicodedb()) { | |
491 | if (!$DB->change_db_encoding()) { | |
492 | // If could not convert successfully, throw error, and prevent installation | |
493 | cli_error(get_string('unicoderequired', 'admin')); | |
494 | } | |
495 | } | |
496 | ||
497 | if ($interactive) { | |
498 | cli_separator(); | |
499 | cli_heading(get_string('databasesetup')); | |
500 | } | |
501 | ||
502 | // install core | |
503 | install_core($version, true); | |
504 | set_config('release', $release); | |
505 | ||
506 | // install all plugins types, local, etc. | |
507 | upgrade_noncore(true); | |
508 | ||
509 | // set up admin user password | |
510 | $DB->set_field('user', 'password', hash_internal_user_password($options['adminpass']), array('username' => 'admin')); | |
511 | ||
512 | // rename admin username if needed | |
513 | if (isset($options['adminuser']) and $options['adminuser'] !== 'admin' and $options['adminuser'] !== 'guest') { | |
514 | $DB->set_field('user', 'username', $options['adminuser'], array('username' => 'admin')); | |
515 | } | |
516 | ||
517 | // indicate that this site is fully configured | |
518 | set_config('rolesactive', 1); | |
519 | upgrade_finished(); | |
520 | ||
521 | // log in as admin - we need do anything when applying defaults | |
522 | $admins = get_admins(); | |
523 | $admin = reset($admins); | |
524 | session_set_user($admin); | |
525 | message_set_default_message_preferences($admin); | |
526 | ||
527 | // apply all default settings, do it twice to fill all defaults - some settings depend on other setting | |
528 | admin_apply_default_settings(NULL, true); | |
529 | admin_apply_default_settings(NULL, true); | |
530 | set_config('registerauth', ''); | |
531 | ||
532 | } |