GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

Installer   F

Complexity

Total Complexity 104

Size/Duplication

Total Lines 707
Duplicated Lines 5.94 %

Coupling/Cohesion

Components 2
Dependencies 15

Importance

Changes 0
Metric Value
dl 42
loc 707
rs 1.263
c 0
b 0
f 0
wmc 104
lcom 2
cbo 15

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Installer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Installer, and based on these observations, apply Extract Interface, too.

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