PageRenderTime 29ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/Installer/src/Controller/StartupController.php

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