Completed
Push — 2.0.0 ( d56a3c...82ee41 )
by John
08:47
created

InstallController::createApplicationDirs()   F

Complexity

Conditions 11
Paths 1024

Size

Total Lines 131
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 131
rs 3.1765
cc 11
eloc 67
nc 1024
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Alpha\Controller;
4
5
use Alpha\Util\Logging\Logger;
6
use Alpha\Util\Config\ConfigProvider;
7
use Alpha\Util\Http\Response;
8
use Alpha\Util\Http\Session\SessionProviderFactory;
9
use Alpha\Model\ActiveRecord;
10
use Alpha\Model\Rights;
11
use Alpha\Model\Person;
12
use Alpha\Model\Type\DEnum;
13
use Alpha\Model\Type\DEnumItem;
14
use Alpha\Exception\FailedIndexCreateException;
15
use Alpha\Exception\FailedLookupCreateException;
16
use Alpha\Controller\Front\FrontController;
17
use Alpha\View\View;
18
19
/**
20
 * Controller used install the database.
21
 *
22
 * @since 1.0
23
 *
24
 * @author John Collins <[email protected]>
25
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
26
 * @copyright Copyright (c) 2015, John Collins (founder of Alpha Framework).
27
 * All rights reserved.
28
 *
29
 * <pre>
30
 * Redistribution and use in source and binary forms, with or
31
 * without modification, are permitted provided that the
32
 * following conditions are met:
33
 *
34
 * * Redistributions of source code must retain the above
35
 *   copyright notice, this list of conditions and the
36
 *   following disclaimer.
37
 * * Redistributions in binary form must reproduce the above
38
 *   copyright notice, this list of conditions and the
39
 *   following disclaimer in the documentation and/or other
40
 *   materials provided with the distribution.
41
 * * Neither the name of the Alpha Framework nor the names
42
 *   of its contributors may be used to endorse or promote
43
 *   products derived from this software without specific
44
 *   prior written permission.
45
 *
46
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
47
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
48
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
49
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
50
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
51
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
57
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
58
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59
 * </pre>
60
 */
61
class InstallController extends Controller implements ControllerInterface
62
{
63
    /**
64
     * Trace logger.
65
     *
66
     * @var Alpha\Util\Logging\Logger
67
     *
68
     * @since 1.0
69
     */
70
    private static $logger = null;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
71
72
    /**
73
     * the constructor.
74
     *
75
     * @since 1.0
76
     */
77 View Code Duplication
    public function __construct()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
78
    {
79
        self::$logger = new Logger('InstallController');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...er('InstallController') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Controller\...ha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
80
        self::$logger->debug('>>__construct()');
81
82
        $config = ConfigProvider::getInstance();
83
84
        parent::__construct('Public');
85
86
        // set up the title and meta details
87
        $this->setTitle('Installing '.$config->get('app.title'));
88
89
        self::$logger->debug('<<__construct');
90
    }
91
92
    /**
93
     * Handle GET requests.
94
     *
95
     * @param Alpha\Util\Http\Request $request
96
     *
97
     * @return Alpha\Util\Http\Response
98
     *
99
     * @since 1.0
100
     */
101
    public function doGET($request)
102
    {
103
        self::$logger->debug('>>doGET($request=['.var_export($request, true).'])');
104
105
        $config = ConfigProvider::getInstance();
106
107
        $sessionProvider = $config->get('session.provider.name');
108
        $session = SessionProviderFactory::getInstance($sessionProvider);
109
110
        // if there is nobody logged in, we will send them off to the Login controller to do so before coming back here
111
        if ($session->get('currentUser') === false) {
112
            self::$logger->info('Nobody logged in, invoking Login controller...');
113
114
            $controller = new LoginController();
115
            $controller->setName('LoginController');
116
            $controller->setRequest($request);
117
            $controller->setUnitOfWork(array('Alpha\Controller\LoginController', 'Alpha\Controller\InstallController'));
118
119
            self::$logger->debug('<<__construct');
120
121
            return $controller->doGET($request);
122
        }
123
124
        $params = $request->getParams();
0 ignored issues
show
Unused Code introduced by
$params is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
125
126
        $sessionProvider = $config->get('session.provider.name');
127
        $session = SessionProviderFactory::getInstance($sessionProvider);
128
129
        $body = View::displayPageHead($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Alpha\Controller\InstallController>, but the function expects a object<Alpha\View\Alpha\Controller\Controller>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
130
131
        $body .= '<h1>Installing the '.$config->get('app.title').' application</h1>';
132
133
        try {
134
            $body .= $this->createApplicationDirs();
135
        } catch (\Exception $e) {
136
            $body .= View::displayErrorMessage($e->getMessage());
137
            $body .= View::displayErrorMessage('Aborting.');
138
139
            return new Response(500, $body, array('Content-Type' => 'text/html'));
140
        }
141
142
        // start a new database transaction
143
        ActiveRecord::begin();
144
145
        /*
146
         * Create DEnum tables
147
         */
148
        $DEnum = new DEnum();
149
        $DEnumItem = new DEnumItem();
150
151
        try {
152
            $body .= '<p>Attempting to create the DEnum tables...';
153
            if (!$DEnum->checkTableExists()) {
154
                $DEnum->makeTable();
155
            }
156
            self::$logger->info('Created the ['.$DEnum->getTableName().'] table successfully');
157
158
            if (!$DEnumItem->checkTableExists()) {
159
                $DEnumItem->makeTable();
160
            }
161
            self::$logger->info('Created the ['.$DEnumItem->getTableName().'] table successfully');
162
163
            // create a default article DEnum category
164
            $DEnum = new DEnum('Alpha\Model\Article::section');
0 ignored issues
show
Documentation introduced by
'Alpha\\Model\\Article::section' is of type string, but the function expects a object<Alpha\Model\Type\...Model\Type\String>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
165
            $DEnumItem = new DEnumItem();
166
            $DEnumItem->set('value', 'Main');
167
            $DEnumItem->set('DEnumID', $DEnum->getID());
168
            $DEnumItem->save();
169
170
            $body .= View::displayUpdateMessage('DEnums set up successfully.');
171
        } catch (\Exception $e) {
172
            $body .= View::displayErrorMessage($e->getMessage());
173
            $body .= View::displayErrorMessage('Aborting.');
174
            self::$logger->error($e->getMessage());
175
            ActiveRecord::rollback();
176
177
            return new Response(500, $body, array('Content-Type' => 'text/html'));
178
        }
179
180
        /*
181
         * Loop over each business object in the system, and create a table for it
182
         */
183
        $classNames = ActiveRecord::getBOClassNames();
184
        $loadedClasses = array();
185
186
        foreach ($classNames as $classname) {
187
            array_push($loadedClasses, $classname);
188
        }
189
190
        foreach ($loadedClasses as $classname) {
191
            try {
192
                $body .= '<p>Attempting to create the table for the class ['.$classname.']...';
193
194
                try {
195
                    $BO = new $classname();
196
197
                    if (!$BO->checkTableExists()) {
198
                        $BO->makeTable();
199
                    } else {
200
                        if ($BO->checkTableNeedsUpdate()) {
201
                            $missingFields = $BO->findMissingFields();
202
203
                            $count = count($missingFields);
204
205
                            for ($i = 0; $i < $count; ++$i) {
206
                                $BO->addProperty($missingFields[$i]);
207
                            }
208
                        }
209
                    }
210
                } catch (FailedIndexCreateException $eice) {
211
                    // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
212
                    self::$logger->warn($eice->getMessage());
213
                } catch (FailedLookupCreateException $elce) {
214
                    // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
215
                    self::$logger->warn($elce->getMessage());
216
                }
217
218
                self::$logger->info('Created the ['.$BO->getTableName().'] table successfully');
219
                $body .= View::displayUpdateMessage('Created the ['.$BO->getTableName().'] table successfully');
220
            } catch (\Exception $e) {
221
                $body .= View::displayErrorMessage($e->getMessage());
222
                $body .= View::displayErrorMessage('Aborting.');
223
                self::$logger->error($e->getMessage());
224
                ActiveRecord::rollback();
225
226
                return new Response(500, $body, array('Content-Type' => 'text/html'));
227
            }
228
        }
229
230
        $body .= View::displayUpdateMessage('All business object tables created successfully!');
231
232
        /*
233
         * Create the Admin and Standard groups
234
         */
235
        $adminGroup = new Rights();
236
        $adminGroup->set('name', 'Admin');
237
        $standardGroup = new Rights();
238
        $standardGroup->set('name', 'Standard');
239
240
        try {
241
            try {
242
                $body .= '<p>Attempting to create the Admin and Standard groups...';
243
                $adminGroup->save();
244
                $standardGroup->save();
245
246
                self::$logger->info('Created the Admin and Standard rights groups successfully');
247
                $body .= View::displayUpdateMessage('Created the Admin and Standard rights groups successfully');
248
            } catch (FailedIndexCreateException $eice) {
249
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
250
                self::$logger->warn($eice->getMessage());
251
            } catch (FailedLookupCreateException $elce) {
252
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
253
                self::$logger->warn($elce->getMessage());
254
            }
255
        } catch (\Exception $e) {
256
            $body .= View::displayErrorMessage($e->getMessage());
257
            $body .= View::displayErrorMessage('Aborting.');
258
            self::$logger->error($e->getMessage());
259
            ActiveRecord::rollback();
260
261
            return new Response(500, $body, array('Content-Type' => 'text/html'));
262
        }
263
264
        /*
265
         * Save the admin user to the database in the right group
266
         */
267
        try {
268
            try {
269
                $body .= '<p>Attempting to save the Admin account...';
270
                $admin = new Person();
271
                $admin->set('displayName', 'Admin');
272
                $admin->set('email', $session->get('currentUser')->get('email'));
273
                $admin->set('password', $session->get('currentUser')->get('password'));
274
                $admin->save();
275
                self::$logger->info('Created the admin user account ['.$session->get('currentUser')->get('email').'] successfully');
276
277
                $adminGroup->loadByAttribute('name', 'Admin');
278
279
                $lookup = $adminGroup->getMembers()->getLookup();
280
                $lookup->setValue(array($admin->getID(), $adminGroup->getID()));
281
                $lookup->save();
282
283
                self::$logger->info('Added the admin account to the Admin group successfully');
284
                $body .= View::displayUpdateMessage('Added the admin account to the Admin group successfully');
285
            } catch (FailedIndexCreateException $eice) {
286
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
287
                self::$logger->warn($eice->getMessage());
288
            } catch (FailedLookupCreateException $elce) {
289
                // this are safe to ignore for now as they will be auto-created later once all of the tables are in place
290
                self::$logger->warn($elce->getMessage());
291
            }
292
        } catch (\Exception $e) {
293
            $body .= View::displayErrorMessage($e->getMessage());
294
            $body .= View::displayErrorMessage('Aborting.');
295
            self::$logger->error($e->getMessage());
296
            ActiveRecord::rollback();
297
298
            return new Response(500, $body, array('Content-Type' => 'text/html'));
299
        }
300
301
        $body .= '<br><p align="center"><a href="'.FrontController::generateSecureURL('act=Alpha\Controller\ListActiveRecordsController').'">Administration Home Page</a></p><br>';
302
        $body .= View::displayPageFoot($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Alpha\Controller\InstallController>, but the function expects a object<Alpha\View\Alpha\Aonctoller\Controller>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
303
304
        // commit
305
        ActiveRecord::commit();
306
307
        self::$logger->info('Finished installation!');
308
        self::$logger->action('Installed the application');
309
        self::$logger->debug('<<doGET');
310
311
        return new Response(200, $body, array('Content-Type' => 'text/html'));
312
    }
313
314
    /**
315
     * Create the directories required by the application.
316
     *
317
     * @return string
318
     *
319
     * @since 2.0
320
     */
321
    public function createApplicationDirs()
322
    {
323
        self::$logger->debug('>>createApplicationDirs()');
324
325
        $config = ConfigProvider::getInstance();
326
327
        $body = '';
328
329
        // set the umask first before attempt mkdir
330
        umask(0);
331
332
        /*
333
         * Create the logs directory, then instantiate a new logger
334
         */
335
        $logsDir = $config->get('app.file.store.dir').'logs';
336
337
        $body .= '<p>Attempting to create the logs directory <em>'.$logsDir.'</em>...';
338
339
        if (!file_exists($logsDir)) {
340
            var_dump(mkdir($logsDir, 0774));
0 ignored issues
show
Security Debugging Code introduced by
var_dump(mkdir($logsDir, 508)); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
341
        }
342
343
        self::$logger = new Logger('InstallController');
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Alpha\Util\Logging\...er('InstallController') of type object<Alpha\Util\Logging\Logger> is incompatible with the declared type object<Alpha\Controller\...ha\Util\Logging\Logger> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
344
        self::$logger->info('Started installation process!');
345
        self::$logger->info('Logs directory ['.$logsDir.'] successfully created');
346
        $body .= View::displayUpdateMessage('Logs directory ['.$logsDir.'] successfully created');
347
348
        /*
349
         * Create the src directory and sub-directories
350
         */
351
        $srcDir = $config->get('app.root').'src';
352
353
        $body .= '<p>Attempting to create the src directory <em>'.$srcDir.'</em>...';
354
355
        if (!file_exists($srcDir)) {
356
            mkdir($srcDir, 0774);
357
        }
358
359
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
360
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
361
362
        $srcDir = $config->get('app.root').'src/Model';
363
364
        if (!file_exists($srcDir)) {
365
            mkdir($srcDir, 0774);
366
        }
367
368
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
369
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
370
371
        $srcDir = $config->get('app.root').'src/View';
372
373
        if (!file_exists($srcDir)) {
374
            mkdir($srcDir, 0774);
375
        }
376
377
        self::$logger->info('Source directory ['.$srcDir.'] successfully created');
378
        $body .= View::displayUpdateMessage('Source directory ['.$srcDir.'] successfully created');
379
380
        /*
381
         * Create the attachments directory
382
         */
383
        $attachmentsDir = $config->get('app.file.store.dir').'attachments';
384
385
        $body .= '<p>Attempting to create the attachments directory <em>'.$attachmentsDir.'</em>...';
386
387
        if (!file_exists($attachmentsDir)) {
388
            mkdir($attachmentsDir, 0774);
389
        }
390
391
        self::$logger->info('Attachments directory ['.$attachmentsDir.'] successfully created');
392
        $body .= View::displayUpdateMessage('Attachments directory ['.$attachmentsDir.'] successfully created');
393
394
        /*
395
         * Create the cache directory and sub-directories
396
         */
397
        $cacheDir = $config->get('app.file.store.dir').'cache';
398
        $htmlDir = $config->get('app.file.store.dir').'cache/html';
399
        $imagesDir = $config->get('app.file.store.dir').'cache/images';
400
        $pdfDir = $config->get('app.file.store.dir').'cache/pdf';
401
        $xlsDir = $config->get('app.file.store.dir').'cache/xls';
402
403
        // cache
404
        $body .= '<p>Attempting to create the cache directory <em>'.$cacheDir.'</em>...';
405
        if (!file_exists($cacheDir)) {
406
            mkdir($cacheDir, 0774);
407
        }
408
409
        self::$logger->info('Cache directory ['.$cacheDir.'] successfully created');
410
        $body .= View::displayUpdateMessage('Cache directory ['.$cacheDir.'] successfully created');
411
412
        // cache/html
413
        $body .= '<p>Attempting to create the HTML cache directory <em>'.$htmlDir.'</em>...';
414
        if (!file_exists($htmlDir)) {
415
            mkdir($htmlDir, 0774);
416
        }
417
418
        self::$logger->info('Cache directory ['.$htmlDir.'] successfully created');
419
        $body .= View::displayUpdateMessage('Cache directory ['.$htmlDir.'] successfully created');
420
421
        // cache/images
422
        $body .= '<p>Attempting to create the cache directory <em>'.$imagesDir.'</em>...';
423
        if (!file_exists($imagesDir)) {
424
            mkdir($imagesDir, 0774);
425
        }
426
427
        self::$logger->info('Cache directory ['.$imagesDir.'] successfully created');
428
        $body .= View::displayUpdateMessage('Cache directory ['.$imagesDir.'] successfully created');
429
430
        // cache/pdf
431
        $body .= '<p>Attempting to create the cache directory <em>'.$pdfDir.'</em>...';
432
        if (!file_exists($pdfDir)) {
433
            mkdir($pdfDir, 0774);
434
        }
435
436
        self::$logger->info('Cache directory ['.$pdfDir.'] successfully created');
437
        $body .= View::displayUpdateMessage('Cache directory ['.$pdfDir.'] successfully created');
438
439
        // cache/xls
440
        $body .= '<p>Attempting to create the cache directory <em>'.$xlsDir.'</em>...';
441
        if (!file_exists($xlsDir)) {
442
            mkdir($xlsDir, 0774);
443
        }
444
445
        self::$logger->info('Cache directory ['.$xlsDir.'] successfully created');
446
        $body .= View::displayUpdateMessage('Cache directory ['.$xlsDir.'] successfully created');
447
448
        self::$logger->debug('<<createApplicationDirs');
449
450
        return $body;
451
    }
452
453
    /**
454
     * Custom version of the check rights method that only checks for a session for the config admin username/password,
455
     * when the system database is not set-up.
456
     *
457
     * @return bool
458
     *
459
     * @since 1.0
460
     */
461
    public function checkRights()
462
    {
463
        self::$logger->debug('>>checkRights()');
464
465
        $config = ConfigProvider::getInstance();
466
        $sessionProvider = $config->get('session.provider.name');
467
        $session = SessionProviderFactory::getInstance($sessionProvider);
468
469
        if ($this->getVisibility() == 'Public') {
470
            self::$logger->debug('<<checkRights [true]');
471
472
            return true;
473
        }
474
475
        if (ActiveRecord::isInstalled()) {
476
            self::$logger->debug('<<checkRights [false]');
477
478
            return false;
479
        }
480
481
        // the person is logged in?
482
        if ($session->get('currentUser') !== false) {
483
            if ($session->get('currentUser')->get('email') == $config->get('app.install.username')) {
484
                self::$logger->debug('<<checkRights [true]');
485
486
                return true;
487
            }
488
        }
489
    }
490
}
491