|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
require_once CORE . "/class.administration.php"; |
|
4
|
|
|
|
|
5
|
|
|
class Installer extends Administration |
|
6
|
|
|
{ |
|
7
|
|
|
private static $POST = array(); |
|
8
|
|
|
|
|
9
|
|
|
/** |
|
10
|
|
|
* Override the default Symphony constructor to initialise the Log, Config |
|
11
|
|
|
* and Database objects for installation/update. This allows us to use the |
|
12
|
|
|
* normal accessors. |
|
13
|
|
|
*/ |
|
14
|
|
|
protected function __construct() |
|
15
|
|
|
{ |
|
16
|
|
|
self::$Profiler = Profiler::instance(); |
|
17
|
|
|
self::$Profiler->sample('Engine Initialisation'); |
|
18
|
|
|
|
|
19
|
|
|
if (get_magic_quotes_gpc()) { |
|
20
|
|
|
General::cleanArray($_SERVER); |
|
21
|
|
|
General::cleanArray($_COOKIE); |
|
22
|
|
|
General::cleanArray($_GET); |
|
23
|
|
|
General::cleanArray($_POST); |
|
24
|
|
|
} |
|
25
|
|
|
|
|
26
|
|
|
// Include the default Config for installation. |
|
27
|
|
|
include(INSTALL . '/includes/config_default.php'); |
|
28
|
|
|
static::initialiseConfiguration($settings); |
|
29
|
|
|
|
|
30
|
|
|
// Initialize date/time |
|
31
|
|
|
define_safe('__SYM_DATE_FORMAT__', self::Configuration()->get('date_format', 'region')); |
|
32
|
|
|
define_safe('__SYM_TIME_FORMAT__', self::Configuration()->get('time_format', 'region')); |
|
33
|
|
|
define_safe('__SYM_DATETIME_FORMAT__', __SYM_DATE_FORMAT__ . self::Configuration()->get('datetime_separator', 'region') . __SYM_TIME_FORMAT__); |
|
34
|
|
|
DateTimeObj::setSettings(self::Configuration()->get('region')); |
|
35
|
|
|
|
|
36
|
|
|
// Initialize Language, Logs and Database |
|
37
|
|
|
static::initialiseLang(); |
|
38
|
|
|
static::initialiseLog(INSTALL_LOGS . '/install'); |
|
39
|
|
|
static::initialiseDatabase(); |
|
40
|
|
|
|
|
41
|
|
|
// Initialize error handlers |
|
42
|
|
|
GenericExceptionHandler::initialise(Symphony::Log()); |
|
43
|
|
|
GenericErrorHandler::initialise(Symphony::Log()); |
|
44
|
|
|
|
|
45
|
|
|
// Copy POST |
|
46
|
|
|
self::$POST = $_POST; |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* This function returns an instance of the Installer |
|
51
|
|
|
* class. It is the only way to create a new Installer, as |
|
52
|
|
|
* it implements the Singleton interface |
|
53
|
|
|
* |
|
54
|
|
|
* @return Installer |
|
55
|
|
|
*/ |
|
56
|
|
|
public static function instance() |
|
57
|
|
|
{ |
|
58
|
|
|
if (!(self::$_instance instanceof Installer)) { |
|
59
|
|
|
self::$_instance = new Installer; |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
return self::$_instance; |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Initialises the language by looking at the `lang` key, |
|
67
|
|
|
* passed via GET or POST |
|
68
|
|
|
*/ |
|
69
|
|
|
public static function initialiseLang() |
|
70
|
|
|
{ |
|
71
|
|
|
$lang = !empty($_REQUEST['lang']) ? preg_replace('/[^a-zA-Z\-]/', null, $_REQUEST['lang']) : 'en'; |
|
72
|
|
|
Lang::initialize(); |
|
73
|
|
|
Lang::set($lang, false); |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* Overrides the default `initialiseLog()` method and writes |
|
78
|
|
|
* logs to manifest/logs/install |
|
79
|
|
|
*/ |
|
80
|
|
|
public static function initialiseLog($filename = null) |
|
81
|
|
|
{ |
|
82
|
|
View Code Duplication |
if (is_dir(INSTALL_LOGS) || General::realiseDirectory(INSTALL_LOGS, self::Configuration()->get('write_mode', 'directory'))) { |
|
83
|
|
|
parent::initialiseLog($filename); |
|
84
|
|
|
} |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Overrides the default `initialiseDatabase()` method |
|
89
|
|
|
* This allows us to still use the normal accessor |
|
90
|
|
|
*/ |
|
91
|
|
|
public static function initialiseDatabase() |
|
92
|
|
|
{ |
|
93
|
|
|
self::setDatabase(); |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
public function run() |
|
97
|
|
|
{ |
|
98
|
|
|
// Make sure a log file is available |
|
99
|
|
View Code Duplication |
if (is_null(Symphony::Log()) || !file_exists(Symphony::Log()->getLogPath())) { |
|
100
|
|
|
self::__render(new InstallerPage('missing-log')); |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
// Check essential server requirements |
|
104
|
|
|
$errors = self::__checkRequirements(); |
|
105
|
|
|
if (!empty($errors)) { |
|
106
|
|
|
Symphony::Log()->pushToLog( |
|
107
|
|
|
sprintf('Installer - Missing requirements.'), |
|
108
|
|
|
E_ERROR, true |
|
109
|
|
|
); |
|
110
|
|
|
|
|
111
|
|
|
foreach ($errors as $err) { |
|
112
|
|
|
Symphony::Log()->pushToLog( |
|
113
|
|
|
sprintf('Requirement - %s', $err['msg']), |
|
114
|
|
|
E_ERROR, true |
|
115
|
|
|
); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
self::__render(new InstallerPage('requirements', array( |
|
119
|
|
|
'errors'=> $errors |
|
120
|
|
|
))); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
// Check for unattended installation |
|
124
|
|
|
$unattended = self::__checkUnattended(); |
|
125
|
|
|
if (!empty($unattended)) { |
|
126
|
|
|
// Merge unattended information with the POST |
|
127
|
|
|
self::$POST = array_replace_recursive($unattended, self::$POST); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
// If language is not set and there is language packs available, show language selection pages |
|
131
|
|
|
if (!isset(self::$POST['lang']) && count(Lang::getAvailableLanguages(false)) > 1) { |
|
132
|
|
|
self::__render(new InstallerPage('languages')); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
// Check for configuration errors and, if there are no errors, install Symphony! |
|
136
|
|
|
if (isset(self::$POST['fields'])) { |
|
137
|
|
|
$errors = self::__checkConfiguration(); |
|
138
|
|
|
if (!empty($errors)) { |
|
139
|
|
|
Symphony::Log()->pushToLog( |
|
140
|
|
|
sprintf('Installer - Wrong configuration.'), |
|
141
|
|
|
E_ERROR, true |
|
142
|
|
|
); |
|
143
|
|
|
|
|
144
|
|
|
foreach ($errors as $err) { |
|
145
|
|
|
Symphony::Log()->pushToLog( |
|
146
|
|
|
sprintf('Configuration - %s', $err['msg']), |
|
147
|
|
|
E_ERROR, true |
|
148
|
|
|
); |
|
149
|
|
|
} |
|
150
|
|
|
} elseif (isset(self::$POST['action']['install'])) { |
|
151
|
|
|
$disabled_extensions = self::__install(); |
|
152
|
|
|
|
|
153
|
|
|
self::__render(new InstallerPage('success', array( |
|
154
|
|
|
'disabled-extensions' => $disabled_extensions |
|
155
|
|
|
))); |
|
156
|
|
|
} |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
// Display the Installation page |
|
160
|
|
|
self::__render(new InstallerPage('configuration', array( |
|
161
|
|
|
'errors' => $errors, |
|
162
|
|
|
'default-config' => !empty($unattended) ? $unattended['fields'] : Symphony::Configuration()->get() |
|
163
|
|
|
))); |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* This function checks the server can support a Symphony installation. |
|
168
|
|
|
* It checks that PHP is 5.2+, MySQL, Zlib, LibXML, XSLT modules are enabled |
|
169
|
|
|
* and a `install.sql` file exists. |
|
170
|
|
|
* If any of these requirements fail the installation will not proceed. |
|
171
|
|
|
* |
|
172
|
|
|
* @return array |
|
173
|
|
|
* An associative array of errors, with `msg` and `details` keys |
|
174
|
|
|
*/ |
|
175
|
|
|
private static function __checkRequirements() |
|
176
|
|
|
{ |
|
177
|
|
|
$errors = array(); |
|
178
|
|
|
|
|
179
|
|
|
// Check for PHP 5.2+ |
|
180
|
|
|
if (version_compare(phpversion(), '5.3', '<=')) { |
|
181
|
|
|
$errors[] = array( |
|
182
|
|
|
'msg' => __('PHP Version is not correct'), |
|
183
|
|
|
'details' => __('Symphony requires %1$s or greater to work, however version %2$s was detected.', array('<code><abbr title="PHP: Hypertext Pre-processor">PHP</abbr> 5.3</code>', '<code>' . phpversion() . '</code>')) |
|
184
|
|
|
); |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
// Make sure the install.sql file exists |
|
188
|
|
|
if (!file_exists(INSTALL . '/includes/install.sql') || !is_readable(INSTALL . '/includes/install.sql')) { |
|
189
|
|
|
$errors[] = array( |
|
190
|
|
|
'msg' => __('Missing install.sql file'), |
|
191
|
|
|
'details' => __('It appears that %s is either missing or not readable. This is required to populate the database and must be uploaded before installation can commence. Ensure that PHP has read permissions.', array('<code>install.sql</code>')) |
|
192
|
|
|
); |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
// Is MySQL available? |
|
196
|
|
View Code Duplication |
if (!function_exists('mysqli_connect')) { |
|
197
|
|
|
$errors[] = array( |
|
198
|
|
|
'msg' => __('MySQLi extension not present'), |
|
199
|
|
|
'details' => __('Symphony requires PHP to be configured with MySQLi to work.') |
|
200
|
|
|
); |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
// Is ZLib available? |
|
204
|
|
View Code Duplication |
if (!extension_loaded('zlib')) { |
|
205
|
|
|
$errors[] = array( |
|
206
|
|
|
'msg' => __('ZLib extension not present'), |
|
207
|
|
|
'details' => __('Symphony uses the ZLib compression library for log rotation.') |
|
208
|
|
|
); |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
// Is libxml available? |
|
212
|
|
View Code Duplication |
if (!extension_loaded('xml') && !extension_loaded('libxml')) { |
|
213
|
|
|
$errors[] = array( |
|
214
|
|
|
'msg' => __('XML extension not present'), |
|
215
|
|
|
'details' => __('Symphony needs the XML extension to pass data to the site frontend.') |
|
216
|
|
|
); |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
// Is libxslt available? |
|
220
|
|
|
if (!extension_loaded('xsl') && !extension_loaded('xslt') && !function_exists('domxml_xslt_stylesheet')) { |
|
221
|
|
|
$errors[] = array( |
|
222
|
|
|
'msg' => __('XSLT extension not present'), |
|
223
|
|
|
'details' => __('Symphony needs an XSLT processor such as %s or Sablotron to build pages.', array('Lib<abbr title="eXtensible Stylesheet Language Transformation">XSLT</abbr>')) |
|
224
|
|
|
); |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
// Is json_encode available? |
|
228
|
|
View Code Duplication |
if (!function_exists('json_decode')) { |
|
229
|
|
|
$errors[] = array( |
|
230
|
|
|
'msg' => __('JSON functionality is not present'), |
|
231
|
|
|
'details' => __('Symphony uses JSON functionality throughout the backend for translations and the interface.') |
|
232
|
|
|
); |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
// Cannot write to root folder. |
|
236
|
|
|
if (!is_writable(DOCROOT)) { |
|
237
|
|
|
$errors['no-write-permission-root'] = array( |
|
238
|
|
|
'msg' => 'Root folder not writable: ' . DOCROOT, |
|
239
|
|
|
'details' => __('Symphony does not have write permission to the root directory. Please modify permission settings on %s. This can be reverted once installation is complete.', array('<code>' . DOCROOT . '</code>')) |
|
240
|
|
|
); |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
// Cannot write to workspace |
|
244
|
|
|
if (is_dir(DOCROOT . '/workspace') && !is_writable(DOCROOT . '/workspace')) { |
|
245
|
|
|
$errors['no-write-permission-workspace'] = array( |
|
246
|
|
|
'msg' => 'Workspace folder not writable: ' . DOCROOT . '/workspace', |
|
247
|
|
|
'details' => __('Symphony does not have write permission to the existing %1$s directory. Please modify permission settings on this directory and its contents to allow this, such as with a recursive %2$s command.', array('<code>/workspace</code>', '<code>chmod -R</code>')) |
|
248
|
|
|
); |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
return $errors; |
|
252
|
|
|
} |
|
253
|
|
|
|
|
254
|
|
|
/** |
|
255
|
|
|
* This function checks the current Configuration (which is the values entered |
|
256
|
|
|
* by the user on the installation form) to ensure that `/symphony` and `/workspace` |
|
257
|
|
|
* folders exist and are writable and that the Database credentials are correct. |
|
258
|
|
|
* Once those initial checks pass, the rest of the form values are validated. |
|
259
|
|
|
* |
|
260
|
|
|
* @return |
|
261
|
|
|
* An associative array of errors if something went wrong, otherwise an empty array. |
|
262
|
|
|
*/ |
|
263
|
|
|
private static function __checkConfiguration() |
|
264
|
|
|
{ |
|
265
|
|
|
$errors = array(); |
|
266
|
|
|
$fields = self::$POST['fields']; |
|
267
|
|
|
|
|
268
|
|
|
// Testing the database connection |
|
269
|
|
|
try { |
|
270
|
|
|
Symphony::Database()->connect( |
|
271
|
|
|
$fields['database']['host'], |
|
272
|
|
|
$fields['database']['user'], |
|
273
|
|
|
$fields['database']['password'], |
|
274
|
|
|
$fields['database']['port'], |
|
275
|
|
|
$fields['database']['db'] |
|
276
|
|
|
); |
|
277
|
|
|
} catch (DatabaseException $e) { |
|
278
|
|
|
// Invalid credentials |
|
279
|
|
|
// @link http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html |
|
280
|
|
|
if ($e->getDatabaseErrorCode() === 1044 || $e->getDatabaseErrorCode() === 1045) { |
|
281
|
|
|
$errors['database-invalid-credentials'] = array( |
|
282
|
|
|
'msg' => 'Database credentials were denied', |
|
283
|
|
|
'details' => __('Symphony was unable to access the database with these credentials.') |
|
284
|
|
|
); |
|
285
|
|
|
} |
|
286
|
|
|
// Connection related |
|
287
|
|
|
else { |
|
288
|
|
|
$errors['no-database-connection'] = array( |
|
289
|
|
|
'msg' => 'Could not establish database connection.', |
|
290
|
|
|
'details' => __('Symphony was unable to establish a valid database connection. You may need to modify host or port settings.') |
|
291
|
|
|
); |
|
292
|
|
|
} |
|
293
|
|
|
} |
|
294
|
|
|
|
|
295
|
|
|
try { |
|
296
|
|
|
// Check the database table prefix is legal. #1815 |
|
297
|
|
|
if (!preg_match('/^[0-9a-zA-Z\$_]*$/', $fields['database']['tbl_prefix'])) { |
|
298
|
|
|
$errors['database-table-prefix'] = array( |
|
299
|
|
|
'msg' => 'Invalid database table prefix: ‘' . $fields['database']['tbl_prefix'] . '’', |
|
300
|
|
|
'details' => __('The table prefix %s is invalid. The table prefix must only contain numbers, letters or underscore characters.', array('<code>' . $fields['database']['tbl_prefix'] . '</code>')) |
|
301
|
|
|
); |
|
302
|
|
|
} |
|
303
|
|
|
// Check the database credentials |
|
304
|
|
|
elseif (Symphony::Database()->isConnected()) { |
|
305
|
|
|
// Incorrect MySQL version |
|
306
|
|
|
$version = Symphony::Database()->fetchVar('version', 0, "SELECT VERSION() AS `version`;"); |
|
307
|
|
|
if (version_compare($version, '5.5', '<')) { |
|
308
|
|
|
$errors['database-incorrect-version'] = array( |
|
309
|
|
|
'msg' => 'MySQL Version is not correct. '. $version . ' detected.', |
|
310
|
|
|
'details' => __('Symphony requires %1$s or greater to work, however version %2$s was detected. This requirement must be met before installation can proceed.', array('<code>MySQL 5.5</code>', '<code>' . $version . '</code>')) |
|
311
|
|
|
); |
|
312
|
|
|
} else { |
|
313
|
|
|
// Existing table prefix |
|
314
|
|
|
$tables = Symphony::Database()->fetch(sprintf( |
|
315
|
|
|
"SHOW TABLES FROM `%s` LIKE '%s'", |
|
316
|
|
|
mysqli_real_escape_string(Symphony::Database()->getConnectionResource(), $fields['database']['db']), |
|
317
|
|
|
mysqli_real_escape_string(Symphony::Database()->getConnectionResource(), $fields['database']['tbl_prefix']) . '%' |
|
318
|
|
|
)); |
|
319
|
|
|
|
|
320
|
|
|
if (is_array($tables) && !empty($tables)) { |
|
321
|
|
|
$errors['database-table-prefix'] = array( |
|
322
|
|
|
'msg' => 'Database table prefix clash with ‘' . $fields['database']['db'] . '’', |
|
323
|
|
|
'details' => __('The table prefix %s is already in use. Please choose a different prefix to use with Symphony.', array('<code>' . $fields['database']['tbl_prefix'] . '</code>')) |
|
324
|
|
|
); |
|
325
|
|
|
} |
|
326
|
|
|
} |
|
327
|
|
|
} |
|
328
|
|
|
} catch (DatabaseException $e) { |
|
329
|
|
|
$errors['unknown-database'] = array( |
|
330
|
|
|
'msg' => 'Database ‘' . $fields['database']['db'] . '’ not found.', |
|
331
|
|
|
'details' => __('Symphony was unable to connect to the specified database.') |
|
332
|
|
|
); |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
// Website name not entered |
|
336
|
|
|
if (trim($fields['general']['sitename']) == '') { |
|
337
|
|
|
$errors['general-no-sitename'] = array( |
|
338
|
|
|
'msg' => 'No sitename entered.', |
|
339
|
|
|
'details' => __('You must enter a Site name. This will be shown at the top of your backend.') |
|
340
|
|
|
); |
|
341
|
|
|
} |
|
342
|
|
|
|
|
343
|
|
|
// Username Not Entered |
|
344
|
|
|
if (trim($fields['user']['username']) == '') { |
|
345
|
|
|
$errors['user-no-username'] = array( |
|
346
|
|
|
'msg' => 'No username entered.', |
|
347
|
|
|
'details' => __('You must enter a Username. This will be your Symphony login information.') |
|
348
|
|
|
); |
|
349
|
|
|
} |
|
350
|
|
|
|
|
351
|
|
|
// Password Not Entered |
|
352
|
|
|
if (trim($fields['user']['password']) == '') { |
|
353
|
|
|
$errors['user-no-password'] = array( |
|
354
|
|
|
'msg' => 'No password entered.', |
|
355
|
|
|
'details' => __('You must enter a Password. This will be your Symphony login information.') |
|
356
|
|
|
); |
|
357
|
|
|
} |
|
358
|
|
|
|
|
359
|
|
|
// Password mismatch |
|
360
|
|
|
elseif ($fields['user']['password'] != $fields['user']['confirm-password']) { |
|
361
|
|
|
$errors['user-password-mismatch'] = array( |
|
362
|
|
|
'msg' => 'Passwords did not match.', |
|
363
|
|
|
'details' => __('The password and confirmation did not match. Please retype your password.') |
|
364
|
|
|
); |
|
365
|
|
|
} |
|
366
|
|
|
|
|
367
|
|
|
// No Name entered |
|
368
|
|
|
if (trim($fields['user']['firstname']) == '' || trim($fields['user']['lastname']) == '') { |
|
369
|
|
|
$errors['user-no-name'] = array( |
|
370
|
|
|
'msg' => 'Did not enter First and Last names.', |
|
371
|
|
|
'details' => __('You must enter your name.') |
|
372
|
|
|
); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
// Invalid Email |
|
376
|
|
|
if (!preg_match('/^\w(?:\.?[\w%+-]+)*@\w(?:[\w-]*\.)+?[a-z]{2,}$/i', $fields['user']['email'])) { |
|
377
|
|
|
$errors['user-invalid-email'] = array( |
|
378
|
|
|
'msg' => 'Invalid email address supplied.', |
|
379
|
|
|
'details' => __('This is not a valid email address. You must provide an email address since you will need it if you forget your password.') |
|
380
|
|
|
); |
|
381
|
|
|
} |
|
382
|
|
|
|
|
383
|
|
|
// Admin path not entered |
|
384
|
|
|
if (trim($fields['symphony']['admin-path']) == '') { |
|
385
|
|
|
$errors['no-symphony-path'] = array( |
|
386
|
|
|
'msg' => 'No Symphony path entered.', |
|
387
|
|
|
'details' => __('You must enter a path for accessing Symphony, or leave the default. This will be used to access Symphony\'s backend.') |
|
388
|
|
|
); |
|
389
|
|
|
} |
|
390
|
|
|
|
|
391
|
|
|
return $errors; |
|
392
|
|
|
} |
|
393
|
|
|
|
|
394
|
|
|
/** |
|
395
|
|
|
* This function checks if there is a unattend.php file in the MANIFEST folder. |
|
396
|
|
|
* If it finds one, it will load it and check for the $settings variable. |
|
397
|
|
|
* It will also merge the default config values into the 'fields' array. |
|
398
|
|
|
* |
|
399
|
|
|
* You can find an empty version at install/include/unattend.php |
|
400
|
|
|
* |
|
401
|
|
|
* @return array |
|
402
|
|
|
* An associative array of values, as if it was submitted by a POST |
|
403
|
|
|
*/ |
|
404
|
|
|
private static function __checkUnattended() |
|
405
|
|
|
{ |
|
406
|
|
|
$filepath = MANIFEST . '/unattend.php'; |
|
407
|
|
|
if (!@file_exists($filepath) || !@is_readable($filepath)) { |
|
408
|
|
|
return false; |
|
409
|
|
|
} |
|
410
|
|
|
try { |
|
411
|
|
|
include $filepath; |
|
412
|
|
|
if (!isset($settings) || !is_array($settings) || !isset($settings['fields'])) { |
|
|
|
|
|
|
413
|
|
|
return false; |
|
414
|
|
|
} |
|
415
|
|
|
// Merge with default values |
|
416
|
|
|
$settings['fields'] = array_replace_recursive(Symphony::Configuration()->get(), $settings['fields']); |
|
417
|
|
|
// Special case for the password |
|
418
|
|
|
if (isset($settings['fields']['user']) && isset($settings['fields']['user']['password'])) { |
|
419
|
|
|
$settings['fields']['user']['confirm-password'] = $settings['fields']['user']['password']; |
|
420
|
|
|
} |
|
421
|
|
|
return $settings; |
|
422
|
|
|
} catch (Exception $ex) { |
|
423
|
|
|
Symphony::Log()->pushExceptionToLog($ex, true); |
|
|
|
|
|
|
424
|
|
|
} |
|
425
|
|
|
return false; |
|
426
|
|
|
} |
|
427
|
|
|
|
|
428
|
|
|
/** |
|
429
|
|
|
* If something went wrong, the `__abort` function will write an entry to the Log |
|
430
|
|
|
* file and display the failure page to the user. |
|
431
|
|
|
* @todo: Resume installation after an error has been fixed. |
|
432
|
|
|
*/ |
|
433
|
|
|
protected static function __abort($message, $start) |
|
434
|
|
|
{ |
|
435
|
|
|
$result = Symphony::Log()->pushToLog($message, E_ERROR, true); |
|
436
|
|
|
|
|
437
|
|
|
if ($result) { |
|
438
|
|
|
Symphony::Log()->writeToLog('============================================', true); |
|
439
|
|
|
Symphony::Log()->writeToLog(sprintf('INSTALLATION ABORTED: Execution Time - %d sec (%s)', |
|
440
|
|
|
max(1, time() - $start), |
|
441
|
|
|
date('d.m.y H:i:s') |
|
442
|
|
|
), true); |
|
443
|
|
|
Symphony::Log()->writeToLog('============================================' . PHP_EOL . PHP_EOL . PHP_EOL, true); |
|
444
|
|
|
} |
|
445
|
|
|
|
|
446
|
|
|
self::__render(new InstallerPage('failure')); |
|
447
|
|
|
} |
|
448
|
|
|
|
|
449
|
|
|
private static function __install() |
|
450
|
|
|
{ |
|
451
|
|
|
$fields = self::$POST['fields']; |
|
452
|
|
|
$start = time(); |
|
453
|
|
|
|
|
454
|
|
|
Symphony::Log()->writeToLog(PHP_EOL . '============================================', true); |
|
455
|
|
|
Symphony::Log()->writeToLog('INSTALLATION PROCESS STARTED (' . DateTimeObj::get('c') . ')', true); |
|
456
|
|
|
Symphony::Log()->writeToLog('============================================', true); |
|
457
|
|
|
|
|
458
|
|
|
// MySQL: Establishing connection |
|
459
|
|
|
Symphony::Log()->pushToLog('MYSQL: Establishing Connection', E_NOTICE, true, true); |
|
460
|
|
|
|
|
461
|
|
|
try { |
|
462
|
|
|
Symphony::Database()->connect( |
|
463
|
|
|
$fields['database']['host'], |
|
464
|
|
|
$fields['database']['user'], |
|
465
|
|
|
$fields['database']['password'], |
|
466
|
|
|
$fields['database']['port'], |
|
467
|
|
|
$fields['database']['db'] |
|
468
|
|
|
); |
|
469
|
|
|
} catch (DatabaseException $e) { |
|
470
|
|
|
self::__abort( |
|
471
|
|
|
'There was a problem while trying to establish a connection to the MySQL server. Please check your settings.', |
|
472
|
|
|
$start); |
|
473
|
|
|
} |
|
474
|
|
|
|
|
475
|
|
|
// MySQL: Setting prefix & character encoding |
|
476
|
|
|
Symphony::Database()->setPrefix($fields['database']['tbl_prefix']); |
|
477
|
|
|
Symphony::Database()->setCharacterEncoding(); |
|
478
|
|
|
Symphony::Database()->setCharacterSet(); |
|
479
|
|
|
|
|
480
|
|
|
// MySQL: Importing schema |
|
481
|
|
|
Symphony::Log()->pushToLog('MYSQL: Importing Table Schema', E_NOTICE, true, true); |
|
482
|
|
|
|
|
483
|
|
|
try { |
|
484
|
|
|
Symphony::Database()->import(file_get_contents(INSTALL . '/includes/install.sql'), true); |
|
485
|
|
|
} catch (DatabaseException $e) { |
|
486
|
|
|
self::__abort( |
|
487
|
|
|
'There was an error while trying to import data to the database. MySQL returned: ' . $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(), |
|
488
|
|
|
$start); |
|
489
|
|
|
} |
|
490
|
|
|
|
|
491
|
|
|
// MySQL: Creating default author |
|
492
|
|
|
Symphony::Log()->pushToLog('MYSQL: Creating Default Author', E_NOTICE, true, true); |
|
493
|
|
|
|
|
494
|
|
|
try { |
|
495
|
|
|
Symphony::Database()->insert(array( |
|
496
|
|
|
'id' => 1, |
|
497
|
|
|
'username' => Symphony::Database()->cleanValue($fields['user']['username']), |
|
498
|
|
|
'password' => Cryptography::hash(Symphony::Database()->cleanValue($fields['user']['password'])), |
|
499
|
|
|
'first_name' => Symphony::Database()->cleanValue($fields['user']['firstname']), |
|
500
|
|
|
'last_name' => Symphony::Database()->cleanValue($fields['user']['lastname']), |
|
501
|
|
|
'email' => Symphony::Database()->cleanValue($fields['user']['email']), |
|
502
|
|
|
'last_seen' => null, |
|
503
|
|
|
'user_type' => 'developer', |
|
504
|
|
|
'primary' => 'yes', |
|
505
|
|
|
'default_area' => '/blueprints/sections/', |
|
506
|
|
|
'auth_token_active' => 'no' |
|
507
|
|
|
), 'tbl_authors'); |
|
508
|
|
|
} catch (DatabaseException $e) { |
|
509
|
|
|
self::__abort( |
|
510
|
|
|
'There was an error while trying create the default author. MySQL returned: ' . $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(), |
|
511
|
|
|
$start); |
|
512
|
|
|
} |
|
513
|
|
|
|
|
514
|
|
|
// Configuration: Populating array |
|
515
|
|
|
$conf = Symphony::Configuration()->get(); |
|
516
|
|
|
|
|
517
|
|
|
if (!is_array($conf)) { |
|
518
|
|
|
self::__abort('The configuration is not an array, can not continue', $start); |
|
519
|
|
|
} |
|
520
|
|
|
foreach ($conf as $group => $settings) { |
|
|
|
|
|
|
521
|
|
|
if (!is_array($settings)) { |
|
522
|
|
|
continue; |
|
523
|
|
|
} |
|
524
|
|
|
foreach ($settings as $key => $value) { |
|
525
|
|
|
if (isset($fields[$group]) && isset($fields[$group][$key])) { |
|
526
|
|
|
$conf[$group][$key] = $fields[$group][$key]; |
|
527
|
|
|
} |
|
528
|
|
|
} |
|
529
|
|
|
} |
|
530
|
|
|
|
|
531
|
|
|
// Create manifest folder structure |
|
532
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘manifest’ folder (/manifest)', E_NOTICE, true, true); |
|
533
|
|
|
if (!General::realiseDirectory(MANIFEST, $conf['directory']['write_mode'])) { |
|
534
|
|
|
self::__abort( |
|
535
|
|
|
'Could not create ‘manifest’ directory. Check permission on the root folder.', |
|
536
|
|
|
$start); |
|
537
|
|
|
} |
|
538
|
|
|
|
|
539
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘logs’ folder (/manifest/logs)', E_NOTICE, true, true); |
|
540
|
|
|
if (!General::realiseDirectory(LOGS, $conf['directory']['write_mode'])) { |
|
541
|
|
|
self::__abort( |
|
542
|
|
|
'Could not create ‘logs’ directory. Check permission on /manifest.', |
|
543
|
|
|
$start); |
|
544
|
|
|
} |
|
545
|
|
|
|
|
546
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘cache’ folder (/manifest/cache)', E_NOTICE, true, true); |
|
547
|
|
|
if (!General::realiseDirectory(CACHE, $conf['directory']['write_mode'])) { |
|
548
|
|
|
self::__abort( |
|
549
|
|
|
'Could not create ‘cache’ directory. Check permission on /manifest.', |
|
550
|
|
|
$start); |
|
551
|
|
|
} |
|
552
|
|
|
|
|
553
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘tmp’ folder (/manifest/tmp)', E_NOTICE, true, true); |
|
554
|
|
|
if (!General::realiseDirectory(MANIFEST . '/tmp', $conf['directory']['write_mode'])) { |
|
555
|
|
|
self::__abort( |
|
556
|
|
|
'Could not create ‘tmp’ directory. Check permission on /manifest.', |
|
557
|
|
|
$start); |
|
558
|
|
|
} |
|
559
|
|
|
|
|
560
|
|
|
// Writing configuration file |
|
561
|
|
|
Symphony::Log()->pushToLog('WRITING: Configuration File', E_NOTICE, true, true); |
|
562
|
|
|
|
|
563
|
|
|
Symphony::Configuration()->setArray($conf); |
|
564
|
|
|
|
|
565
|
|
|
if (!Symphony::Configuration()->write(CONFIG, $conf['file']['write_mode'])) { |
|
566
|
|
|
self::__abort( |
|
567
|
|
|
'Could not create config file ‘' . CONFIG . '’. Check permission on /manifest.', |
|
568
|
|
|
$start); |
|
569
|
|
|
} |
|
570
|
|
|
|
|
571
|
|
|
// Writing htaccess file |
|
572
|
|
|
Symphony::Log()->pushToLog('CONFIGURING: Frontend', E_NOTICE, true, true); |
|
573
|
|
|
|
|
574
|
|
|
$rewrite_base = ltrim(preg_replace('/\/install$/i', null, dirname($_SERVER['PHP_SELF'])), '/'); |
|
575
|
|
|
$htaccess = str_replace( |
|
576
|
|
|
'<!-- REWRITE_BASE -->', $rewrite_base, |
|
577
|
|
|
file_get_contents(INSTALL . '/includes/htaccess.txt') |
|
578
|
|
|
); |
|
579
|
|
|
|
|
580
|
|
|
if (!General::writeFile(DOCROOT . "/.htaccess", $htaccess, $conf['file']['write_mode'], 'a')) { |
|
581
|
|
|
self::__abort( |
|
582
|
|
|
'Could not write ‘.htaccess’ file. Check permission on ' . DOCROOT, |
|
583
|
|
|
$start); |
|
584
|
|
|
} |
|
585
|
|
|
|
|
586
|
|
|
// Writing /workspace folder |
|
587
|
|
|
if (!is_dir(DOCROOT . '/workspace')) { |
|
588
|
|
|
// Create workspace folder structure |
|
589
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘workspace’ folder (/workspace)', E_NOTICE, true, true); |
|
590
|
|
|
if (!General::realiseDirectory(WORKSPACE, $conf['directory']['write_mode'])) { |
|
591
|
|
|
self::__abort( |
|
592
|
|
|
'Could not create ‘workspace’ directory. Check permission on the root folder.', |
|
593
|
|
|
$start); |
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘data-sources’ folder (/workspace/data-sources)', E_NOTICE, true, true); |
|
597
|
|
|
if (!General::realiseDirectory(DATASOURCES, $conf['directory']['write_mode'])) { |
|
598
|
|
|
self::__abort( |
|
599
|
|
|
'Could not create ‘workspace/data-sources’ directory. Check permission on the root folder.', |
|
600
|
|
|
$start); |
|
601
|
|
|
} |
|
602
|
|
|
|
|
603
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘events’ folder (/workspace/events)', E_NOTICE, true, true); |
|
604
|
|
|
if (!General::realiseDirectory(EVENTS, $conf['directory']['write_mode'])) { |
|
605
|
|
|
self::__abort( |
|
606
|
|
|
'Could not create ‘workspace/events’ directory. Check permission on the root folder.', |
|
607
|
|
|
$start); |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘pages’ folder (/workspace/pages)', E_NOTICE, true, true); |
|
611
|
|
|
if (!General::realiseDirectory(PAGES, $conf['directory']['write_mode'])) { |
|
612
|
|
|
self::__abort( |
|
613
|
|
|
'Could not create ‘workspace/pages’ directory. Check permission on the root folder.', |
|
614
|
|
|
$start); |
|
615
|
|
|
} |
|
616
|
|
|
|
|
617
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘utilities’ folder (/workspace/utilities)', E_NOTICE, true, true); |
|
618
|
|
|
if (!General::realiseDirectory(UTILITIES, $conf['directory']['write_mode'])) { |
|
619
|
|
|
self::__abort( |
|
620
|
|
|
'Could not create ‘workspace/utilities’ directory. Check permission on the root folder.', |
|
621
|
|
|
$start); |
|
622
|
|
|
} |
|
623
|
|
|
} else { |
|
624
|
|
|
Symphony::Log()->pushToLog('An existing ‘workspace’ directory was found at this location. Symphony will use this workspace.', E_NOTICE, true, true); |
|
625
|
|
|
|
|
626
|
|
|
// MySQL: Importing workspace data |
|
627
|
|
|
Symphony::Log()->pushToLog('MYSQL: Importing Workspace Data...', E_NOTICE, true, true); |
|
628
|
|
|
|
|
629
|
|
|
if (is_file(WORKSPACE . '/install.sql')) { |
|
630
|
|
|
try { |
|
631
|
|
|
Symphony::Database()->import( |
|
632
|
|
|
file_get_contents(WORKSPACE . '/install.sql'), |
|
633
|
|
|
true |
|
634
|
|
|
); |
|
635
|
|
|
} catch (DatabaseException $e) { |
|
636
|
|
|
self::__abort( |
|
637
|
|
|
'There was an error while trying to import data to the database. MySQL returned: ' . $e->getDatabaseErrorCode() . ': ' . $e->getDatabaseErrorMessage(), |
|
638
|
|
|
$start); |
|
639
|
|
|
} |
|
640
|
|
|
} |
|
641
|
|
|
} |
|
642
|
|
|
|
|
643
|
|
|
// Write extensions folder |
|
644
|
|
|
if (!is_dir(EXTENSIONS)) { |
|
645
|
|
|
// Create extensions folder |
|
646
|
|
|
Symphony::Log()->pushToLog('WRITING: Creating ‘extensions’ folder (/extensions)', E_NOTICE, true, true); |
|
647
|
|
|
if (!General::realiseDirectory(EXTENSIONS, $conf['directory']['write_mode'])) { |
|
648
|
|
|
self::__abort( |
|
649
|
|
|
'Could not create ‘extension’ directory. Check permission on the root folder.', |
|
650
|
|
|
$start); |
|
651
|
|
|
} |
|
652
|
|
|
} |
|
653
|
|
|
|
|
654
|
|
|
// Install existing extensions |
|
655
|
|
|
Symphony::Log()->pushToLog('CONFIGURING: Installing existing extensions', E_NOTICE, true, true); |
|
656
|
|
|
$disabled_extensions = array(); |
|
657
|
|
|
foreach (new DirectoryIterator(EXTENSIONS) as $e) { |
|
658
|
|
|
if ($e->isDot() || $e->isFile() || !is_file($e->getRealPath() . '/extension.driver.php')) { |
|
659
|
|
|
continue; |
|
660
|
|
|
} |
|
661
|
|
|
|
|
662
|
|
|
$handle = $e->getBasename(); |
|
663
|
|
|
try { |
|
664
|
|
|
if (!ExtensionManager::enable($handle)) { |
|
665
|
|
|
$disabled_extensions[] = $handle; |
|
666
|
|
|
Symphony::Log()->pushToLog('Could not enable the extension ‘' . $handle . '’.', E_NOTICE, true, true); |
|
667
|
|
|
} |
|
668
|
|
|
} catch (Exception $ex) { |
|
669
|
|
|
$disabled_extensions[] = $handle; |
|
670
|
|
|
Symphony::Log()->pushToLog('Could not enable the extension ‘' . $handle . '’. '. $ex->getMessage(), E_NOTICE, true, true); |
|
671
|
|
|
} |
|
672
|
|
|
} |
|
673
|
|
|
|
|
674
|
|
|
// Loading default language |
|
675
|
|
|
if (isset($_REQUEST['lang']) && $_REQUEST['lang'] != 'en') { |
|
676
|
|
|
Symphony::Log()->pushToLog('CONFIGURING: Default language', E_NOTICE, true, true); |
|
677
|
|
|
|
|
678
|
|
|
$language = Lang::Languages(); |
|
679
|
|
|
$language = $language[$_REQUEST['lang']]; |
|
680
|
|
|
|
|
681
|
|
|
// Is the language extension enabled? |
|
682
|
|
|
if (in_array('lang_' . $language['handle'], ExtensionManager::listInstalledHandles())) { |
|
683
|
|
|
Symphony::Configuration()->set('lang', $_REQUEST['lang'], 'symphony'); |
|
684
|
|
|
if (!Symphony::Configuration()->write(CONFIG, $conf['file']['write_mode'])) { |
|
685
|
|
|
Symphony::Log()->pushToLog('Could not write default language ‘' . $language['name'] . '’ to config file.', E_NOTICE, true, true); |
|
686
|
|
|
} |
|
687
|
|
|
} else { |
|
688
|
|
|
Symphony::Log()->pushToLog('Could not enable the desired language ‘' . $language['name'] . '’.', E_NOTICE, true, true); |
|
689
|
|
|
} |
|
690
|
|
|
} |
|
691
|
|
|
|
|
692
|
|
|
// Installation completed. Woo-hoo! |
|
693
|
|
|
Symphony::Log()->writeToLog('============================================', true); |
|
694
|
|
|
Symphony::Log()->writeToLog(sprintf('INSTALLATION COMPLETED: Execution Time - %d sec (%s)', |
|
695
|
|
|
max(1, time() - $start), |
|
696
|
|
|
date('d.m.y H:i:s') |
|
697
|
|
|
), true); |
|
698
|
|
|
Symphony::Log()->writeToLog('============================================' . PHP_EOL . PHP_EOL . PHP_EOL, true); |
|
699
|
|
|
|
|
700
|
|
|
return $disabled_extensions; |
|
701
|
|
|
} |
|
702
|
|
|
|
|
703
|
|
|
protected static function __render(InstallerPage $page) |
|
704
|
|
|
{ |
|
705
|
|
|
$output = $page->generate(); |
|
706
|
|
|
|
|
707
|
|
|
header('Content-Type: text/html; charset=utf-8'); |
|
708
|
|
|
echo $output; |
|
709
|
|
|
exit; |
|
|
|
|
|
|
710
|
|
|
} |
|
711
|
|
|
} |
|
712
|
|
|
|
This check marks calls to
isset(...)orempty(...)that are found before the variable itself is defined. These will always have the same result.This is likely the result of code being shifted around. Consider removing these calls.