3 declare(strict_types
=1);
5 namespace Shaarli\Front\Controller\Visitor
;
7 use Shaarli\Container\ShaarliContainer
;
8 use Shaarli\Front\Exception\AlreadyInstalledException
;
9 use Shaarli\Front\Exception\ResourcePermissionException
;
10 use Shaarli\Helper\ApplicationUtils
;
11 use Shaarli\Languages
;
12 use Shaarli\Security\SessionManager
;
13 use Slim\Http\Request
;
14 use Slim\Http\Response
;
17 * Slim controller used to render install page, and create initial configuration file.
19 class InstallController
extends ShaarliVisitorController
21 public const SESSION_TEST_KEY
= 'session_tested';
22 public const SESSION_TEST_VALUE
= 'Working';
24 public function __construct(ShaarliContainer
$container)
26 parent
::__construct($container);
28 if (is_file($this->container
->conf
->getConfigFileExt())) {
29 throw new AlreadyInstalledException();
34 * Display the install template page.
35 * Also test file permissions and sessions beforehand.
37 public function index(Request
$request, Response
$response): Response
39 // Before installation, we'll make sure that permissions are set properly, and sessions are working.
40 $this->checkPermissions();
42 if (static::SESSION_TEST_VALUE
43 !== $this->container
->sessionManager
->getSessionParameter(static::SESSION_TEST_KEY
)
45 $this->container
->sessionManager
->setSessionParameter(static::SESSION_TEST_KEY
, static::SESSION_TEST_VALUE
);
47 return $this->redirect($response, '/install/session-test');
50 [$continents, $cities] = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get());
52 $this->assignView('continents', $continents);
53 $this->assignView('cities', $cities);
54 $this->assignView('languages', Languages
::getAvailableLanguages());
56 $phpEol = new \
DateTimeImmutable(ApplicationUtils
::getPhpEol(PHP_VERSION
));
58 $this->assignView('php_version', PHP_VERSION
);
59 $this->assignView('php_eol', format_date($phpEol, false));
60 $this->assignView('php_has_reached_eol', $phpEol < new \
DateTimeImmutable());
61 $this->assignView('php_extensions', ApplicationUtils
::getPhpExtensionsRequirement());
62 $this->assignView('permissions', ApplicationUtils
::checkResourcePermissions($this->container
->conf
));
64 $this->assignView('pagetitle', t('Install Shaarli'));
66 return $response->write($this->render('install'));
70 * Route checking that the session parameter has been properly saved between two distinct requests.
71 * If the session parameter is preserved, redirect to install template page, otherwise displays error.
73 public function sessionTest(Request
$request, Response
$response): Response
75 // This part makes sure sessions works correctly.
76 // (Because on some hosts, session.save_path may not be set correctly,
77 // or we may not have write access to it.)
78 if (static::SESSION_TEST_VALUE
79 !== $this->container
->sessionManager
->getSessionParameter(static::SESSION_TEST_KEY
)
81 // Step 2: Check if data in session is correct.
83 '<pre>Sessions do not seem to work correctly on your server.<br>'.
84 'Make sure the variable "session.save_path" is set correctly in your PHP config, '.
85 'and that you have write access to it.<br>'.
86 'It currently points to %s.<br>'.
87 'On some browsers, accessing your server via a hostname like \'localhost\' '.
88 'or any custom hostname without a dot causes cookie storage to fail. '.
89 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'
91 $msg = sprintf($msg, $this->container
->sessionManager
->getSavePath());
93 $this->assignView('message', $msg);
95 return $response->write($this->render('error'));
98 return $this->redirect($response, '/install');
102 * Save installation form and initialize config file and datastore if necessary.
104 public function save(Request
$request, Response
$response): Response
107 if (!empty($request->getParam('continent'))
108 && !empty($request->getParam('city'))
109 && isTimeZoneValid($request->getParam('continent'), $request->getParam('city'))
111 $timezone = $request->getParam('continent') . '/' . $request->getParam('city');
113 $this->container
->conf
->set('general.timezone', $timezone);
115 $login = $request->getParam('setlogin');
116 $this->container
->conf
->set('credentials.login', $login);
117 $salt = sha1(uniqid('', true) .'_'. mt_rand());
118 $this->container
->conf
->set('credentials.salt', $salt);
119 $this->container
->conf
->set('credentials.hash', sha1($request->getParam('setpassword') . $login . $salt));
121 if (!empty($request->getParam('title'))) {
122 $this->container
->conf
->set('general.title', escape($request->getParam('title')));
124 $this->container
->conf
->set(
126 'Shared bookmarks on '.escape(index_url($this->container
->environment
))
130 $this->container
->conf
->set('translation.language', escape($request->getParam('language')));
131 $this->container
->conf
->set('updates.check_updates', !empty($request->getParam('updateCheck')));
132 $this->container
->conf
->set('api.enabled', !empty($request->getParam('enableApi')));
133 $this->container
->conf
->set(
136 $this->container
->conf
->get('credentials.login'),
137 $this->container
->conf
->get('credentials.salt')
140 $this->container
->conf
->set('general.header_link', $this->container
->basePath
. '/');
143 // Everything is ok, let's create config file.
144 $this->container
->conf
->write($this->container
->loginManager
->isLoggedIn());
145 } catch (\Exception
$e) {
146 $this->assignView('message', t('Error while writing config file after configuration update.'));
147 $this->assignView('stacktrace', $e->getMessage() . PHP_EOL
. $e->getTraceAsString());
149 return $response->write($this->render('error'));
152 $this->container
->sessionManager
->setSessionParameter(
153 SessionManager
::KEY_SUCCESS_MESSAGES
,
154 [t('Shaarli is now configured. Please login and start shaaring your bookmarks!')]
157 return $this->redirect($response, '/login');
160 protected function checkPermissions(): bool
162 // Ensure Shaarli has proper access to its resources
163 $errors = ApplicationUtils
::checkResourcePermissions($this->container
->conf
, true);
164 if (empty($errors)) {
168 $message = t('Insufficient permissions:') . PHP_EOL
;
169 foreach ($errors as $error) {
170 $message .= PHP_EOL
. $error;
173 throw new ResourcePermissionException($message);