Completed
Push — master ( 3c1969...5b5792 )
by Schlaefer
03:16 queued 10s
created

InstallController::connected()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 0
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Installer\Controller;
14
15
use Cake\Core\Configure;
16
use Cake\Datasource\ConnectionManager;
17
use Cake\Http\Response;
18
use Cake\ORM\TableRegistry;
19
use Cake\Utility\Security;
20
use Installer\Lib\DbVersion;
21
use Installer\Lib\InstallerState;
22
use Psr\Log\LogLevel;
23
24
/**
25
 * Install Controller
26
 */
27
class InstallController extends AppController
28
{
29
    /**
30
     * {@inheritdoc}
31
     */
32
    public function initialize()
33
    {
34
        parent::initialize();
35
        $this->loadComponent('Referer');
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function beforeFilter(\Cake\Event\Event $event)
42
    {
43
        $this->set('titleForLayout', __d('installer', 'title'));
44
    }
45
46
    /**
47
     * Starting point
48
     *
49
     * @return \Cake\Http\Response|void
50
     */
51
    public function index()
52
    {
53
        $this->log('Start installer.');
54
        InstallerState::reset();
55
56
        return $this->redirect('/install/dbconnection');
57
    }
58
59
    /**
60
     * Check if connection to DB is working
61
     *
62
     * @return \Cake\Http\Response|void
63
     */
64
    public function dbconnection()
65
    {
66
        try {
67
            $connection = ConnectionManager::get('default');
68
            if ($connection->connect()) {
69
                $this->log('Database connection found.');
70
71
                return $this->installerRedirect('salt');
72
            }
73
        } catch (\Throwable $connectionError) {
74
            // connection manager will throw error if no connection
75
        }
76
77
        $this->log('No database connection.');
78
        $this->set('database', false);
79
    }
80
81
    /**
82
     * Check if security salt is set
83
     *
84
     * @return \Cake\Http\Response|void
85
     */
86
    public function salt()
87
    {
88
        if (!InstallerState::check('salt')) {
89
            return $this->redirect('/');
90
        }
91
92
        $secured = (Security::getSalt() !== '__SALT__')
93
            && (Configure::read('Security.cookieSalt') !== '__SALT__');
94
95
        if ($secured) {
96
            $this->log('Security salt is set.');
97
98
            return $this->installerRedirect('connected');
99
        }
100
101
        $this->log('Security salt is not set.');
102
        $this->set('secured', $secured);
103
    }
104
105
    /**
106
     * Check if there's an existing installation
107
     *
108
     * User uses new software version but hit the Installer accidentally.
109
     *
110
     * @return \Cake\Http\Response|void
111
     */
112
    public function connected()
113
    {
114
        if (!InstallerState::check('connected')) {
115
            return $this->redirect('/');
116
        }
117
118
        try {
119
            (new DbVersion($this->loadModel('Settings')))->get();
0 ignored issues
show
Compatibility introduced by
$this->loadModel('Settings') of type object<Cake\Datasource\RepositoryInterface> is not a sub-type of object<App\Model\Table\SettingsTable>. It seems like you assume a concrete implementation of the interface Cake\Datasource\RepositoryInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
120
            $this->log('Installer found Settings-table.');
121
122
            return;
123
        } catch (\Throwable $e) {
124
            // Settings-table doesn't exist yet
125
        }
126
127
        $this->log('Installer didn\'t find Settings-table.');
128
129
        return $this->installerRedirect('migrate');
130
    }
131
132
    /**
133
     * Insert tables and seed data
134
     *
135
     * @return \Cake\Http\Response|void
136
     */
137
    public function migrate()
138
    {
139
        if (!InstallerState::check('migrate')) {
140
            return $this->redirect('/');
141
        }
142
143
        $this->log('Installer checking migration status.');
144
145
        if ($this->getRequest()->isPost()) {
146
            $this->set('tables', false);
147
148
            $this->log('Installer starting initial migrate.');
149
            // Initial layout
150
            $this->migrations->migrate(['target' => 'Saitox5x0x0']);
151
            $this->log('Installer starting seed.');
152
            // The seed is meant for the initial layout
153
            $this->migrations->seed();
154
            $this->log('Installer starting follow-up migrate.');
155
            // Apply migration changes which applies to DB and seed-data (esp. settings-table)
156
            $this->migrations->migrate();
157
        }
158
159
        $status = $this->migrations->status();
160
        if (empty($status[0]) || empty($status[0]['status']) || $status[0]['status'] !== 'down') {
161
            $this->log('Installer migration has run.');
162
163
            return $this->installerRedirect('data');
164
        }
165
    }
166
167
    /**
168
     * Insert data from user-input
169
     *
170
     * @return \Cake\Http\Response|void
171
     */
172
    public function data()
173
    {
174
        if (!InstallerState::check('data')) {
175
            return $this->redirect('/');
176
        }
177
178
        $Users = TableRegistry::getTableLocator()->get('Users');
179
180
        if ($this->getRequest()->isGet()) {
181
            $this->set('admin', $Users->newEntity());
182
183
            return;
184
        }
185
186
        //// setting admin user
187
        $this->log('Installer setting admin user.');
188
        $admin = $Users->get(1);
189
        $data = $this->getRequest()->getData();
190
        $Users->patchEntity($admin, $data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->getRequest()->getData() on line 189 can also be of type null or string; however, Cake\ORM\Table::patchEntity() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
191
        if (!$Users->save($admin)) {
192
            $this->log('Installer failed saving admin-data.');
193
            $this->set('admin', $admin);
194
195
            return;
196
        }
197
        $this->log('Installer admin-data is saved.');
198
199
        //// setting forum-default email
200
        $this->log('Installer setting forum email.');
201
        $Settings = TableRegistry::getTableLocator()->get('Settings');
202
        $forumEmail = $Settings->findByName('forum_email')->first();
203
        $forumEmail->set('value', $data['user_email']);
204
        $Settings->save($forumEmail);
205
        $this->log('Installer forum email set.');
206
207
        $this->log('Marking installed.');
208
        (new DbVersion($this->loadModel('Settings')))->set(Configure::read('Saito.v'));
0 ignored issues
show
Compatibility introduced by
$this->loadModel('Settings') of type object<Cake\Datasource\RepositoryInterface> is not a sub-type of object<App\Model\Table\SettingsTable>. It seems like you assume a concrete implementation of the interface Cake\Datasource\RepositoryInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
209
210
        return $this->installerRedirect('finished');
211
    }
212
213
    /**
214
     * Installer finished
215
     *
216
     * @return \Cake\Http\Response|void
217
     */
218
    public function finished()
219
    {
220
        if (!InstallerState::check('finished')) {
221
            return $this->redirect('/');
222
        }
223
224
        $this->log('Installer finished.');
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function log($msg, $level = LogLevel::INFO, $context = ['saito.install'])
231
    {
232
        parent::log($msg, $level, $context);
233
    }
234
235
    /**
236
     * Redirect to a different installer stage
237
     *
238
     * @param string $action controller-action to redirect to
239
     * @return Response redirect
240
     */
241
    private function installerRedirect(string $action): Response
242
    {
243
        InstallerState::set($action);
244
245
        return $this->redirect('/install/' . $action);
246
    }
247
}
248