1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Licensed under The GPL-3.0 License |
4
|
|
|
* For full copyright and license information, please see the LICENSE.txt |
5
|
|
|
* Redistributions of files must retain the above copyright notice. |
6
|
|
|
* |
7
|
|
|
* @since 2.0.0 |
8
|
|
|
* @author Christopher Castro <[email protected]> |
9
|
|
|
* @link http://www.quickappscms.org |
10
|
|
|
* @license http://opensource.org/licenses/gpl-3.0.html GPL-3.0 License |
11
|
|
|
*/ |
12
|
|
|
namespace Installer\Controller; |
13
|
|
|
|
14
|
|
|
use Cake\Controller\Controller; |
15
|
|
|
use Cake\Event\Event; |
16
|
|
|
use Cake\Filesystem\Folder; |
17
|
|
|
use Cake\I18n\I18n; |
18
|
|
|
use Cake\Routing\Router; |
19
|
|
|
use CMS\Core\Plugin; |
20
|
|
|
use Installer\Utility\DatabaseInstaller; |
21
|
|
|
use Installer\Utility\ServerTest; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Controller for handling new QuickAppsCMS installations. |
25
|
|
|
* |
26
|
|
|
* This controller starts the installation process for a new QuickAppsCMS setup. |
27
|
|
|
* |
28
|
|
|
* @property \User\Model\Table\UsersTable $Users |
29
|
|
|
*/ |
30
|
|
|
class StartupController extends Controller |
31
|
|
|
{ |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* {@inheritDoc} |
35
|
|
|
* |
36
|
|
|
* @var string |
37
|
|
|
*/ |
38
|
|
|
public $components = ['Flash']; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* {@inheritDoc} |
42
|
|
|
* |
43
|
|
|
* @param \Cake\Event\Event $event The event that was triggered |
44
|
|
|
* @return void |
45
|
|
|
*/ |
46
|
|
|
public function beforeFilter(Event $event) |
47
|
|
|
{ |
48
|
|
|
if (is_readable(ROOT . '/config/settings.php')) { |
49
|
|
|
$this->redirect('/'); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
$this->_prepareLayout(); |
53
|
|
|
$this->viewBuilder() |
54
|
|
|
->className('CMS\View\View') |
55
|
|
|
->theme(false) |
56
|
|
|
->layout('Installer.startup') |
57
|
|
|
->helpers(['Menu.Menu']); |
58
|
|
|
|
59
|
|
|
if (!empty($this->request->query['locale']) && !in_array($this->request->params['action'], ['language', 'index'])) { |
60
|
|
|
I18n::locale($this->request->query['locale']); |
61
|
|
|
$this->request->session()->write('installation.language', I18n::locale()); |
62
|
|
|
} elseif ($this->request->session()->read('installation.language')) { |
63
|
|
|
I18n::locale($this->request->session()->read('installation.language')); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
Router::addUrlFilter(function ($params, $request) { |
67
|
|
|
if (!in_array($request->params['action'], ['language', 'index'])) { |
68
|
|
|
$params['locale'] = I18n::locale(); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return $params; |
72
|
|
|
}); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Main action. |
77
|
|
|
* |
78
|
|
|
* We redirect to first step of the installation process: `language`. |
79
|
|
|
* |
80
|
|
|
* @return void |
81
|
|
|
*/ |
82
|
|
|
public function index() |
83
|
|
|
{ |
84
|
|
|
$this->redirect([ |
85
|
|
|
'plugin' => 'Installer', |
86
|
|
|
'controller' => 'startup', |
87
|
|
|
'action' => 'language' |
88
|
|
|
]); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* First step of the installation process. |
93
|
|
|
* |
94
|
|
|
* User must select the language they want to use for the installation process. |
95
|
|
|
* |
96
|
|
|
* @return void |
97
|
|
|
*/ |
98
|
|
|
public function language() |
99
|
|
|
{ |
100
|
|
|
$languages = [ |
101
|
|
|
'en_US' => [ |
102
|
|
|
'url' => '/installer/startup/requirements?locale=en_US', |
103
|
|
|
'welcome' => 'Welcome to QuickAppsCMS', |
104
|
|
|
'action' => 'Click here to install in English' |
105
|
|
|
] |
106
|
|
|
]; |
107
|
|
|
|
108
|
|
|
$Folder = new Folder(Plugin::classPath('Installer') . 'Locale'); |
109
|
|
|
foreach ($Folder->read(false, true, true)[0] as $path) { |
110
|
|
|
$code = basename($path); |
111
|
|
|
$file = $path . '/installer.po'; |
112
|
|
|
|
113
|
|
|
if (is_readable($file)) { |
114
|
|
|
I18n::locale($code); // trick for __d() |
115
|
|
|
$languages[$code] = [ |
116
|
|
|
'url' => "/installer/startup/requirements?locale={$code}", |
117
|
|
|
'welcome' => __d('installer', 'Welcome to QuickAppsCMS'), |
118
|
|
|
'action' => __d('installer', 'Click here to install in English') |
119
|
|
|
]; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
I18n::locale('en_US'); |
124
|
|
|
$this->title('Welcome to QuickAppsCMS'); |
125
|
|
|
$this->set('languages', $languages); |
126
|
|
|
$this->_step(); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Second step of the installation process. |
131
|
|
|
* |
132
|
|
|
* We check server requirements here. |
133
|
|
|
* |
134
|
|
|
* @return void |
135
|
|
|
*/ |
136
|
|
|
public function requirements() |
137
|
|
|
{ |
138
|
|
View Code Duplication |
if (!$this->_step('language')) { |
139
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'language']); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$tests = $this->_getTester(); |
143
|
|
|
$errors = $tests->errors(); |
144
|
|
|
|
145
|
|
|
if (empty($errors)) { |
146
|
|
|
$this->_step(); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$this->title(__d('installer', 'Server Requirements')); |
150
|
|
|
$this->set('errors', $errors); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Third step of the installation process. |
155
|
|
|
* |
156
|
|
|
* License agreement. |
157
|
|
|
* |
158
|
|
|
* @return void |
159
|
|
|
*/ |
160
|
|
|
public function license() |
161
|
|
|
{ |
162
|
|
View Code Duplication |
if (!$this->_step('requirements')) { |
163
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'requirements']); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$this->title(__d('installer', 'License Agreement')); |
167
|
|
|
$this->_step(); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Fourth step of the installation process. |
172
|
|
|
* |
173
|
|
|
* User must introduce database connection information. |
174
|
|
|
* |
175
|
|
|
* @return void |
176
|
|
|
*/ |
177
|
|
|
public function database() |
178
|
|
|
{ |
179
|
|
View Code Duplication |
if (!$this->_step('license')) { |
180
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'license']); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
if (!empty($this->request->data)) { |
184
|
|
|
$dbInstaller = new DatabaseInstaller(); |
185
|
|
|
if ($dbInstaller->install($this->request->data())) { |
186
|
|
|
$this->_step(); |
187
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'account']); |
188
|
|
|
} else { |
189
|
|
|
$errors = ''; |
190
|
|
|
foreach ($dbInstaller->errors() as $error) { |
191
|
|
|
$errors .= "\t<li>{$error}</li>\n"; |
192
|
|
|
} |
193
|
|
|
$this->Flash->danger("<ul>\n{$errors}</ul>\n"); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
$this->title(__d('installer', 'Database Configuration')); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Fifth step of the installation process. |
202
|
|
|
* |
203
|
|
|
* Create a new administrator user account. |
204
|
|
|
* |
205
|
|
|
* @return void |
206
|
|
|
*/ |
207
|
|
|
public function account() |
208
|
|
|
{ |
209
|
|
View Code Duplication |
if (!$this->_step('database')) { |
210
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'database']); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
$this->loadModel('User.Users'); |
214
|
|
|
$user = $this->Users->newEntity(); |
215
|
|
|
|
216
|
|
|
if ($this->request->data()) { |
217
|
|
|
$data = $this->request->data; |
218
|
|
|
$data['roles'] = ['_ids' => [1]]; |
219
|
|
|
$user = $this->Users->newEntity($data); |
220
|
|
|
|
221
|
|
|
if ($this->Users->save($user)) { |
222
|
|
|
$this->Flash->success(__d('installer', 'Account created you can now login!')); |
223
|
|
|
$this->_step(); |
224
|
|
|
$this->redirect(['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'finish']); |
225
|
|
|
} else { |
226
|
|
|
$this->Flash->danger(__d('installer', 'Account could not be created, please check your information.')); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
$this->title(__d('installer', 'Create New Account')); |
231
|
|
|
$this->set('user', $user); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Last step of the installation process. |
236
|
|
|
* |
237
|
|
|
* Here we say "thanks" and redirect to site's frontend or backend. |
238
|
|
|
* |
239
|
|
|
* @return void |
240
|
|
|
*/ |
241
|
|
|
public function finish() |
242
|
|
|
{ |
243
|
|
|
if ($this->request->data()) { |
244
|
|
|
if (rename(ROOT . '/config/settings.php.tmp', ROOT . '/config/settings.php')) { |
245
|
|
|
snapshot(); |
246
|
|
|
$this->request->session()->delete('Startup'); |
247
|
|
|
|
248
|
|
|
if (!empty($this->request->data['home'])) { |
249
|
|
|
$this->redirect('/'); |
250
|
|
|
} else { |
251
|
|
|
$this->redirect('/admin'); |
252
|
|
|
} |
253
|
|
|
} else { |
254
|
|
|
$this->Flash->danger(__d('installer', 'Unable to continue, check write permission for the "/config" directory.')); |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$this->title(__d('installer', 'Finish Installation')); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
// @codingStandardsIgnoreStart |
262
|
|
|
/** |
263
|
|
|
* Shortcut for Controller::set('title_for_layout', ...) |
264
|
|
|
* |
265
|
|
|
* @param string $titleForLayout Page's title |
266
|
|
|
* @return void |
267
|
|
|
*/ |
268
|
|
|
protected function title($titleForLayout) |
269
|
|
|
{ |
270
|
|
|
$this->set('title_for_layout', $titleForLayout); |
271
|
|
|
} |
272
|
|
|
// @codingStandardsIgnoreEnd |
273
|
|
|
|
274
|
|
|
// @codingStandardsIgnoreStart |
275
|
|
|
/** |
276
|
|
|
* Shortcut for Controller::set('description_for_layout', ...) |
277
|
|
|
* |
278
|
|
|
* @param string $descriptionForLayout Page's description |
279
|
|
|
* @return void |
280
|
|
|
*/ |
281
|
|
|
protected function description($descriptionForLayout) |
282
|
|
|
{ |
283
|
|
|
$this->set('description_for_layout', $descriptionForLayout); |
284
|
|
|
} |
285
|
|
|
// @codingStandardsIgnoreEnd |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Gets an instance of ServerTest class. |
289
|
|
|
* |
290
|
|
|
* @return \Installer\Utility\ServerTest |
291
|
|
|
*/ |
292
|
|
|
protected function _getTester() |
293
|
|
|
{ |
294
|
|
|
$tests = new ServerTest(); |
295
|
|
|
$tests |
296
|
|
|
->add('php', (bool)version_compare(PHP_VERSION, '5.4.19', '>='), __d('installer', 'Your php version is not supported. check that your version is 5.4.19 or newer.')) |
297
|
|
|
->add('mbstring', (bool)extension_loaded('mbstring'), __d('installer', 'Missing extension: {0}', 'mbstring')) |
298
|
|
|
->add('mcrypt', (bool)extension_loaded('mcrypt'), __d('installer', 'Missing extension: {0}', 'mcrypt')) |
299
|
|
|
->add('iconv', (bool)extension_loaded('iconv'), __d('installer', 'Missing extension: {0}', 'iconv')) |
300
|
|
|
->add('intl', (bool)extension_loaded('intl'), __d('installer', 'Missing extension: {0}', 'intl')) |
301
|
|
|
->add('fileinfo', (bool)extension_loaded('fileinfo'), __d('installer', 'Missing extension: {0}', 'fileinfo')) |
302
|
|
|
->add('tmp_writable', is_writable(TMP), __d('installer', '"{0}" folder is not writable.', 'tmp/')) |
303
|
|
|
->add('cache_writable', is_writable(TMP . 'cache'), __d('installer', '"{0}" folder is not writable.', 'tmp/cache/')) |
304
|
|
|
->add('models_writable', is_writable(TMP . 'cache/models'), __d('installer', '"{0}" folder is not writable.', 'tmp/cache/models/')) |
305
|
|
|
->add('persistent_writable', is_writable(TMP . 'cache/persistent'), __d('installer', '"{0}" folder is not writable.', 'tmp/cache/persistent/')) |
306
|
|
|
->add('config_writable', is_writable(ROOT . '/config'), __d('installer', '"{0}" folder is not writable.', 'config/')) |
307
|
|
|
->add('pdo', [ |
308
|
|
|
'rule' => function () { |
309
|
|
|
return |
310
|
|
|
extension_loaded('pdo') && |
311
|
|
|
defined('PDO::ATTR_DEFAULT_FETCH_MODE'); |
312
|
|
|
}, |
313
|
|
|
'message' => __d('installer', 'Missing extension: {0}', 'PDO'), |
314
|
|
|
]) |
315
|
|
|
->add('no_safe_mode', [ |
316
|
|
|
'rule' => function () { |
317
|
|
|
return |
318
|
|
|
ini_get('safe_mode') === false || |
319
|
|
|
ini_get('safe_mode') === '' || |
320
|
|
|
strtolower(ini_get('safe_mode')) == 'off'; |
321
|
|
|
}, |
322
|
|
|
'message' => __d('installer', 'Your server has SafeMode on, please turn it off before continuing.'), |
323
|
|
|
]); |
324
|
|
|
|
325
|
|
|
return $tests; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Check if the given step name was completed. Or marks current step as |
330
|
|
|
* completed. |
331
|
|
|
* |
332
|
|
|
* If $check is set to NULL, it marks current step (controller's action name) |
333
|
|
|
* as completed. If $check is set to a string, it checks if that step was |
334
|
|
|
* completed before. |
335
|
|
|
* |
336
|
|
|
* This allows steps to control user navigation, so users can not pass to the |
337
|
|
|
* next step without completing all previous steps. |
338
|
|
|
* |
339
|
|
|
* @param null|string $check Name of the step to check, or false to mark as |
340
|
|
|
* completed current step |
341
|
|
|
* @return bool |
342
|
|
|
*/ |
343
|
|
|
protected function _step($check = null) |
344
|
|
|
{ |
345
|
|
|
$_steps = (array)$this->request->session()->read('Startup._steps'); |
346
|
|
|
if ($check === null) { |
347
|
|
|
$_steps[] = $this->request->params['action']; |
348
|
|
|
$_steps = array_unique($_steps); |
349
|
|
|
$this->request->session()->write('Startup._steps', $_steps); |
350
|
|
|
} elseif (is_string($check)) { |
351
|
|
|
return in_array($check, $_steps); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
return false; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Sets some view-variables used across all steps. |
359
|
|
|
* |
360
|
|
|
* @return void |
361
|
|
|
*/ |
362
|
|
|
protected function _prepareLayout() |
363
|
|
|
{ |
364
|
|
|
$menu = [ |
365
|
|
|
__d('installer', 'Welcome') => [ |
366
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'language'], |
367
|
|
|
'active' => ($this->request->action === 'language') |
368
|
|
|
], |
369
|
|
|
__d('installer', 'System Requirements') => [ |
370
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'requirements'], |
371
|
|
|
'active' => ($this->request->action === 'requirements') |
372
|
|
|
], |
373
|
|
|
__d('installer', 'License Agreement') => [ |
374
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'license'], |
375
|
|
|
'active' => ($this->request->action === 'license') |
376
|
|
|
], |
377
|
|
|
__d('installer', 'Database Setup') => [ |
378
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'database'], |
379
|
|
|
'active' => ($this->request->action === 'database') |
380
|
|
|
], |
381
|
|
|
__d('installer', 'Your Account') => [ |
382
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'account'], |
383
|
|
|
'active' => ($this->request->action === 'account') |
384
|
|
|
], |
385
|
|
|
__d('installer', 'Finish') => [ |
386
|
|
|
'url' => ['plugin' => 'Installer', 'controller' => 'startup', 'action' => 'finish'], |
387
|
|
|
'active' => ($this->request->action === 'finish') |
388
|
|
|
], |
389
|
|
|
]; |
390
|
|
|
|
391
|
|
|
$this->set('menu', $menu); |
392
|
|
|
} |
393
|
|
|
} |
394
|
|
|
|