diff options
author | ArthurHoaro <arthur@hoa.ro> | 2018-07-28 11:07:55 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2018-07-28 11:07:55 +0200 |
commit | 83faedadff76c5bdca036f39f13943f63b27e164 (patch) | |
tree | 6f44cede16ec6a60f10b9699e211e0818f06d2c8 /index.php | |
parent | 1d9eb22a3df85b67fe6652c0876cd7382c2fb525 (diff) | |
parent | 658988f3aeba7a5a938783249ccf2765251e5597 (diff) | |
download | Shaarli-83faedadff76c5bdca036f39f13943f63b27e164.tar.gz Shaarli-83faedadff76c5bdca036f39f13943f63b27e164.tar.zst Shaarli-83faedadff76c5bdca036f39f13943f63b27e164.zip |
Merge tag 'v0.9.7' into stable
Release v0.9.7
Diffstat (limited to 'index.php')
-rw-r--r-- | index.php | 823 |
1 files changed, 466 insertions, 357 deletions
@@ -1,8 +1,12 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | 2 | /** |
3 | <<<<<<< HEAD | ||
3 | * Shaarli v0.8.7 - Shaare your links... | 4 | * Shaarli v0.8.7 - Shaare your links... |
4 | * | 5 | * |
5 | * The personal, minimalist, super-fast, database free, bookmarking service. | 6 | * The personal, minimalist, super-fast, database free, bookmarking service. |
7 | ======= | ||
8 | * Shaarli - The personal, minimalist, super-fast, database free, bookmarking service. | ||
9 | >>>>>>> v0.9.7 | ||
6 | * | 10 | * |
7 | * Friendly fork by the Shaarli community: | 11 | * Friendly fork by the Shaarli community: |
8 | * - https://github.com/shaarli/Shaarli | 12 | * - https://github.com/shaarli/Shaarli |
@@ -13,7 +17,7 @@ | |||
13 | * | 17 | * |
14 | * Licence: http://www.opensource.org/licenses/zlib-license.php | 18 | * Licence: http://www.opensource.org/licenses/zlib-license.php |
15 | * | 19 | * |
16 | * Requires: PHP 5.3.x | 20 | * Requires: PHP 5.5.x |
17 | */ | 21 | */ |
18 | 22 | ||
19 | // Set 'UTC' as the default timezone if it is not defined in php.ini | 23 | // Set 'UTC' as the default timezone if it is not defined in php.ini |
@@ -25,7 +29,6 @@ if (date_default_timezone_get() == '') { | |||
25 | /* | 29 | /* |
26 | * PHP configuration | 30 | * PHP configuration |
27 | */ | 31 | */ |
28 | define('shaarli_version', '0.8.7'); | ||
29 | 32 | ||
30 | // http://server.com/x/shaarli --> /shaarli/ | 33 | // http://server.com/x/shaarli --> /shaarli/ |
31 | define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); | 34 | define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); |
@@ -51,8 +54,8 @@ if (! file_exists(__DIR__ . '/vendor/autoload.php')) { | |||
51 | ."If you installed Shaarli through Git or using the development branch,\n" | 54 | ."If you installed Shaarli through Git or using the development branch,\n" |
52 | ."please refer to the installation documentation to install PHP" | 55 | ."please refer to the installation documentation to install PHP" |
53 | ." dependencies using Composer:\n" | 56 | ." dependencies using Composer:\n" |
54 | ."- https://github.com/shaarli/Shaarli/wiki/Server-requirements\n" | 57 | ."- https://shaarli.readthedocs.io/en/master/Server-requirements/\n" |
55 | ."- https://github.com/shaarli/Shaarli/wiki/Download-and-Installation"; | 58 | ."- https://shaarli.readthedocs.io/en/master/Download-and-Installation/"; |
56 | exit; | 59 | exit; |
57 | } | 60 | } |
58 | require_once 'inc/rain.tpl.class.php'; | 61 | require_once 'inc/rain.tpl.class.php'; |
@@ -62,12 +65,11 @@ require_once __DIR__ . '/vendor/autoload.php'; | |||
62 | require_once 'application/ApplicationUtils.php'; | 65 | require_once 'application/ApplicationUtils.php'; |
63 | require_once 'application/Cache.php'; | 66 | require_once 'application/Cache.php'; |
64 | require_once 'application/CachedPage.php'; | 67 | require_once 'application/CachedPage.php'; |
65 | require_once 'application/config/ConfigManager.php'; | ||
66 | require_once 'application/config/ConfigPlugin.php'; | 68 | require_once 'application/config/ConfigPlugin.php'; |
67 | require_once 'application/FeedBuilder.php'; | 69 | require_once 'application/FeedBuilder.php'; |
68 | require_once 'application/FileUtils.php'; | 70 | require_once 'application/FileUtils.php'; |
71 | require_once 'application/History.php'; | ||
69 | require_once 'application/HttpUtils.php'; | 72 | require_once 'application/HttpUtils.php'; |
70 | require_once 'application/Languages.php'; | ||
71 | require_once 'application/LinkDB.php'; | 73 | require_once 'application/LinkDB.php'; |
72 | require_once 'application/LinkFilter.php'; | 74 | require_once 'application/LinkFilter.php'; |
73 | require_once 'application/LinkUtils.php'; | 75 | require_once 'application/LinkUtils.php'; |
@@ -79,16 +81,22 @@ require_once 'application/Utils.php'; | |||
79 | require_once 'application/PluginManager.php'; | 81 | require_once 'application/PluginManager.php'; |
80 | require_once 'application/Router.php'; | 82 | require_once 'application/Router.php'; |
81 | require_once 'application/Updater.php'; | 83 | require_once 'application/Updater.php'; |
84 | use \Shaarli\Languages; | ||
85 | use \Shaarli\ThemeUtils; | ||
86 | use \Shaarli\Config\ConfigManager; | ||
87 | use \Shaarli\SessionManager; | ||
82 | 88 | ||
83 | // Ensure the PHP version is supported | 89 | // Ensure the PHP version is supported |
84 | try { | 90 | try { |
85 | ApplicationUtils::checkPHPVersion('5.3', PHP_VERSION); | 91 | ApplicationUtils::checkPHPVersion('5.5', PHP_VERSION); |
86 | } catch(Exception $exc) { | 92 | } catch(Exception $exc) { |
87 | header('Content-Type: text/plain; charset=utf-8'); | 93 | header('Content-Type: text/plain; charset=utf-8'); |
88 | echo $exc->getMessage(); | 94 | echo $exc->getMessage(); |
89 | exit; | 95 | exit; |
90 | } | 96 | } |
91 | 97 | ||
98 | define('SHAARLI_VERSION', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); | ||
99 | |||
92 | // Force cookie path (but do not change lifetime) | 100 | // Force cookie path (but do not change lifetime) |
93 | $cookie = session_get_cookie_params(); | 101 | $cookie = session_get_cookie_params(); |
94 | $cookiedir = ''; | 102 | $cookiedir = ''; |
@@ -114,15 +122,29 @@ if (session_id() == '') { | |||
114 | } | 122 | } |
115 | 123 | ||
116 | // Regenerate session ID if invalid or not defined in cookie. | 124 | // Regenerate session ID if invalid or not defined in cookie. |
117 | if (isset($_COOKIE['shaarli']) && !is_session_id_valid($_COOKIE['shaarli'])) { | 125 | if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli'])) { |
118 | session_regenerate_id(true); | 126 | session_regenerate_id(true); |
119 | $_COOKIE['shaarli'] = session_id(); | 127 | $_COOKIE['shaarli'] = session_id(); |
120 | } | 128 | } |
121 | 129 | ||
122 | $conf = new ConfigManager(); | 130 | $conf = new ConfigManager(); |
131 | $sessionManager = new SessionManager($_SESSION, $conf); | ||
132 | |||
133 | // LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. | ||
134 | if (! defined('LC_MESSAGES')) { | ||
135 | define('LC_MESSAGES', LC_COLLATE); | ||
136 | } | ||
137 | |||
138 | // Sniff browser language and set date format accordingly. | ||
139 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { | ||
140 | autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']); | ||
141 | } | ||
142 | |||
143 | new Languages(setlocale(LC_MESSAGES, 0), $conf); | ||
144 | |||
123 | $conf->setEmpty('general.timezone', date_default_timezone_get()); | 145 | $conf->setEmpty('general.timezone', date_default_timezone_get()); |
124 | $conf->setEmpty('general.title', 'Shared links on '. escape(index_url($_SERVER))); | 146 | $conf->setEmpty('general.title', t('Shared links on '). escape(index_url($_SERVER))); |
125 | RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl'); // template directory | 147 | RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory |
126 | RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory | 148 | RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory |
127 | 149 | ||
128 | $pluginManager = new PluginManager($conf); | 150 | $pluginManager = new PluginManager($conf); |
@@ -132,15 +154,6 @@ date_default_timezone_set($conf->get('general.timezone', 'UTC')); | |||
132 | 154 | ||
133 | ob_start(); // Output buffering for the page cache. | 155 | ob_start(); // Output buffering for the page cache. |
134 | 156 | ||
135 | // In case stupid admin has left magic_quotes enabled in php.ini: | ||
136 | if (get_magic_quotes_gpc()) | ||
137 | { | ||
138 | function stripslashes_deep($value) { $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); return $value; } | ||
139 | $_POST = array_map('stripslashes_deep', $_POST); | ||
140 | $_GET = array_map('stripslashes_deep', $_GET); | ||
141 | $_COOKIE = array_map('stripslashes_deep', $_COOKIE); | ||
142 | } | ||
143 | |||
144 | // Prevent caching on client side or proxy: (yes, it's ugly) | 157 | // Prevent caching on client side or proxy: (yes, it's ugly) |
145 | header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); | 158 | header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); |
146 | header("Cache-Control: no-store, no-cache, must-revalidate"); | 159 | header("Cache-Control: no-store, no-cache, must-revalidate"); |
@@ -152,7 +165,7 @@ if (! is_file($conf->getConfigFileExt())) { | |||
152 | $errors = ApplicationUtils::checkResourcePermissions($conf); | 165 | $errors = ApplicationUtils::checkResourcePermissions($conf); |
153 | 166 | ||
154 | if ($errors != array()) { | 167 | if ($errors != array()) { |
155 | $message = '<p>Insufficient permissions:</p><ul>'; | 168 | $message = '<p>'. t('Insufficient permissions:') .'</p><ul>'; |
156 | 169 | ||
157 | foreach ($errors as $error) { | 170 | foreach ($errors as $error) { |
158 | $message .= '<li>'.$error.'</li>'; | 171 | $message .= '<li>'.$error.'</li>'; |
@@ -165,18 +178,12 @@ if (! is_file($conf->getConfigFileExt())) { | |||
165 | } | 178 | } |
166 | 179 | ||
167 | // Display the installation form if no existing config is found | 180 | // Display the installation form if no existing config is found |
168 | install($conf); | 181 | install($conf, $sessionManager); |
169 | } | 182 | } |
170 | 183 | ||
171 | // a token depending of deployment salt, user password, and the current ip | 184 | // a token depending of deployment salt, user password, and the current ip |
172 | define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); | 185 | define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); |
173 | 186 | ||
174 | // Sniff browser language and set date format accordingly. | ||
175 | if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { | ||
176 | autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']); | ||
177 | } | ||
178 | header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. | ||
179 | |||
180 | /** | 187 | /** |
181 | * Checking session state (i.e. is the user still logged in) | 188 | * Checking session state (i.e. is the user still logged in) |
182 | * | 189 | * |
@@ -186,65 +193,44 @@ header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper int | |||
186 | */ | 193 | */ |
187 | function setup_login_state($conf) | 194 | function setup_login_state($conf) |
188 | { | 195 | { |
189 | if ($conf->get('security.open_shaarli')) { | 196 | if ($conf->get('security.open_shaarli')) { |
190 | return true; | 197 | return true; |
191 | } | 198 | } |
192 | $userIsLoggedIn = false; // By default, we do not consider the user as logged in; | 199 | $userIsLoggedIn = false; // By default, we do not consider the user as logged in; |
193 | $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met. | 200 | $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met. |
194 | if (! $conf->exists('credentials.login')) { | 201 | if (! $conf->exists('credentials.login')) { |
195 | $userIsLoggedIn = false; // Shaarli is not configured yet. | 202 | $userIsLoggedIn = false; // Shaarli is not configured yet. |
196 | $loginFailure = true; | 203 | $loginFailure = true; |
197 | } | 204 | } |
198 | if (isset($_COOKIE['shaarli_staySignedIn']) && | 205 | if (isset($_COOKIE['shaarli_staySignedIn']) && |
199 | $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN && | 206 | $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN && |
200 | !$loginFailure) | 207 | !$loginFailure) |
201 | { | 208 | { |
202 | fillSessionInfo($conf); | 209 | fillSessionInfo($conf); |
203 | $userIsLoggedIn = true; | 210 | $userIsLoggedIn = true; |
204 | } | 211 | } |
205 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. | 212 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. |
206 | if (empty($_SESSION['uid']) | 213 | if (empty($_SESSION['uid']) |
207 | || ($conf->get('security.session_protection_disabled') == false && $_SESSION['ip'] != allIPs()) | 214 | || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != allIPs()) |
208 | || time() >= $_SESSION['expires_on']) | 215 | || time() >= $_SESSION['expires_on']) |
209 | { | ||
210 | logout(); | ||
211 | $userIsLoggedIn = false; | ||
212 | $loginFailure = true; | ||
213 | } | ||
214 | if (!empty($_SESSION['longlastingsession'])) { | ||
215 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked. | ||
216 | } | ||
217 | else { | ||
218 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date. | ||
219 | } | ||
220 | if (!$loginFailure) { | ||
221 | $userIsLoggedIn = true; | ||
222 | } | ||
223 | |||
224 | return $userIsLoggedIn; | ||
225 | } | ||
226 | $userIsLoggedIn = setup_login_state($conf); | ||
227 | |||
228 | /** | ||
229 | * PubSubHubbub protocol support (if enabled) [UNTESTED] | ||
230 | * (Source: http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/ ) | ||
231 | * | ||
232 | * @param ConfigManager $conf Configuration Manager instance. | ||
233 | */ | ||
234 | function pubsubhub($conf) | ||
235 | { | ||
236 | $pshUrl = $conf->get('config.PUBSUBHUB_URL'); | ||
237 | if (!empty($pshUrl)) | ||
238 | { | 216 | { |
239 | include_once './publisher.php'; | 217 | logout(); |
240 | $p = new Publisher($pshUrl); | 218 | $userIsLoggedIn = false; |
241 | $topic_url = array ( | 219 | $loginFailure = true; |
242 | index_url($_SERVER).'?do=atom', | 220 | } |
243 | index_url($_SERVER).'?do=rss' | 221 | if (!empty($_SESSION['longlastingsession'])) { |
244 | ); | 222 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked. |
245 | $p->publish_update($topic_url); | 223 | } |
224 | else { | ||
225 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date. | ||
246 | } | 226 | } |
227 | if (!$loginFailure) { | ||
228 | $userIsLoggedIn = true; | ||
229 | } | ||
230 | |||
231 | return $userIsLoggedIn; | ||
247 | } | 232 | } |
233 | $userIsLoggedIn = setup_login_state($conf); | ||
248 | 234 | ||
249 | // ------------------------------------------------------------------------------------------ | 235 | // ------------------------------------------------------------------------------------------ |
250 | // Session management | 236 | // Session management |
@@ -266,10 +252,10 @@ function allIPs() | |||
266 | */ | 252 | */ |
267 | function fillSessionInfo($conf) | 253 | function fillSessionInfo($conf) |
268 | { | 254 | { |
269 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) | 255 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) |
270 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | 256 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. |
271 | $_SESSION['username']= $conf->get('credentials.login'); | 257 | $_SESSION['username']= $conf->get('credentials.login'); |
272 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | 258 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. |
273 | } | 259 | } |
274 | 260 | ||
275 | /** | 261 | /** |
@@ -286,7 +272,7 @@ function check_auth($login, $password, $conf) | |||
286 | $hash = sha1($password . $login . $conf->get('credentials.salt')); | 272 | $hash = sha1($password . $login . $conf->get('credentials.salt')); |
287 | if ($login == $conf->get('credentials.login') && $hash == $conf->get('credentials.hash')) | 273 | if ($login == $conf->get('credentials.login') && $hash == $conf->get('credentials.hash')) |
288 | { // Login/password is correct. | 274 | { // Login/password is correct. |
289 | fillSessionInfo($conf); | 275 | fillSessionInfo($conf); |
290 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login successful'); | 276 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login successful'); |
291 | return true; | 277 | return true; |
292 | } | 278 | } |
@@ -308,6 +294,7 @@ function logout() { | |||
308 | unset($_SESSION['ip']); | 294 | unset($_SESSION['ip']); |
309 | unset($_SESSION['username']); | 295 | unset($_SESSION['username']); |
310 | unset($_SESSION['privateonly']); | 296 | unset($_SESSION['privateonly']); |
297 | unset($_SESSION['untaggedonly']); | ||
311 | } | 298 | } |
312 | setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); | 299 | setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); |
313 | } | 300 | } |
@@ -405,18 +392,19 @@ function ban_canLogin($conf) | |||
405 | // Process login form: Check if login/password is correct. | 392 | // Process login form: Check if login/password is correct. |
406 | if (isset($_POST['login'])) | 393 | if (isset($_POST['login'])) |
407 | { | 394 | { |
408 | if (!ban_canLogin($conf)) die('I said: NO. You are banned for the moment. Go away.'); | 395 | if (!ban_canLogin($conf)) die(t('I said: NO. You are banned for the moment. Go away.')); |
409 | if (isset($_POST['password']) | 396 | if (isset($_POST['password']) |
410 | && tokenOk($_POST['token']) | 397 | && $sessionManager->checkToken($_POST['token']) |
411 | && (check_auth($_POST['login'], $_POST['password'], $conf)) | 398 | && (check_auth($_POST['login'], $_POST['password'], $conf)) |
412 | ) { // Login/password is OK. | 399 | ) { // Login/password is OK. |
413 | ban_loginOk($conf); | 400 | ban_loginOk($conf); |
414 | // If user wants to keep the session cookie even after the browser closes: | 401 | // If user wants to keep the session cookie even after the browser closes: |
415 | if (!empty($_POST['longlastingsession'])) | 402 | if (!empty($_POST['longlastingsession'])) |
416 | { | 403 | { |
417 | setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, time()+31536000, WEB_PATH); | 404 | $_SESSION['longlastingsession'] = 31536000; // (31536000 seconds = 1 year) |
418 | $_SESSION['longlastingsession']=31536000; // (31536000 seconds = 1 year) | 405 | $expiration = time() + $_SESSION['longlastingsession']; // calculate relative cookie expiration (1 year from now) |
419 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // Set session expiration on server-side. | 406 | setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, $expiration, WEB_PATH); |
407 | $_SESSION['expires_on'] = $expiration; // Set session expiration on server-side. | ||
420 | 408 | ||
421 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; | 409 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; |
422 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['SERVER_NAME']); // Set session cookie expiration on client side | 410 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['SERVER_NAME']); // Set session cookie expiration on client side |
@@ -433,7 +421,7 @@ if (isset($_POST['login'])) | |||
433 | // Optional redirect after login: | 421 | // Optional redirect after login: |
434 | if (isset($_GET['post'])) { | 422 | if (isset($_GET['post'])) { |
435 | $uri = '?post='. urlencode($_GET['post']); | 423 | $uri = '?post='. urlencode($_GET['post']); |
436 | foreach (array('description', 'source', 'title') as $param) { | 424 | foreach (array('description', 'source', 'title', 'tags') as $param) { |
437 | if (!empty($_GET[$param])) { | 425 | if (!empty($_GET[$param])) { |
438 | $uri .= '&'.$param.'='.urlencode($_GET[$param]); | 426 | $uri .= '&'.$param.'='.urlencode($_GET[$param]); |
439 | } | 427 | } |
@@ -462,77 +450,24 @@ if (isset($_POST['login'])) | |||
462 | $redir = '&username='. urlencode($_POST['login']); | 450 | $redir = '&username='. urlencode($_POST['login']); |
463 | if (isset($_GET['post'])) { | 451 | if (isset($_GET['post'])) { |
464 | $redir .= '&post=' . urlencode($_GET['post']); | 452 | $redir .= '&post=' . urlencode($_GET['post']); |
465 | foreach (array('description', 'source', 'title') as $param) { | 453 | foreach (array('description', 'source', 'title', 'tags') as $param) { |
466 | if (!empty($_GET[$param])) { | 454 | if (!empty($_GET[$param])) { |
467 | $redir .= '&' . $param . '=' . urlencode($_GET[$param]); | 455 | $redir .= '&' . $param . '=' . urlencode($_GET[$param]); |
468 | } | 456 | } |
469 | } | 457 | } |
470 | } | 458 | } |
471 | echo '<script>alert("Wrong login/password.");document.location=\'?do=login'.$redir.'\';</script>'; // Redirect to login screen. | 459 | // Redirect to login screen. |
460 | echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'?do=login'.$redir.'\';</script>'; | ||
472 | exit; | 461 | exit; |
473 | } | 462 | } |
474 | } | 463 | } |
475 | 464 | ||
476 | // ------------------------------------------------------------------------------------------ | 465 | // ------------------------------------------------------------------------------------------ |
477 | // Misc utility functions: | ||
478 | |||
479 | // Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes. | ||
480 | function return_bytes($val) | ||
481 | { | ||
482 | $val = trim($val); $last=strtolower($val[strlen($val)-1]); | ||
483 | switch($last) | ||
484 | { | ||
485 | case 'g': $val *= 1024; | ||
486 | case 'm': $val *= 1024; | ||
487 | case 'k': $val *= 1024; | ||
488 | } | ||
489 | return $val; | ||
490 | } | ||
491 | |||
492 | // Try to determine max file size for uploads (POST). | ||
493 | // Returns an integer (in bytes) | ||
494 | function getMaxFileSize() | ||
495 | { | ||
496 | $size1 = return_bytes(ini_get('post_max_size')); | ||
497 | $size2 = return_bytes(ini_get('upload_max_filesize')); | ||
498 | // Return the smaller of two: | ||
499 | $maxsize = min($size1,$size2); | ||
500 | // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000) | ||
501 | return $maxsize; | ||
502 | } | ||
503 | |||
504 | // ------------------------------------------------------------------------------------------ | ||
505 | // Token management for XSRF protection | 466 | // Token management for XSRF protection |
506 | // Token should be used in any form which acts on data (create,update,delete,import...). | 467 | // Token should be used in any form which acts on data (create,update,delete,import...). |
507 | if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. | 468 | if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. |
508 | 469 | ||
509 | /** | 470 | /** |
510 | * Returns a token. | ||
511 | * | ||
512 | * @param ConfigManager $conf Configuration Manager instance. | ||
513 | * | ||
514 | * @return string token. | ||
515 | */ | ||
516 | function getToken($conf) | ||
517 | { | ||
518 | $rnd = sha1(uniqid('', true) .'_'. mt_rand() . $conf->get('credentials.salt')); // We generate a random string. | ||
519 | $_SESSION['tokens'][$rnd]=1; // Store it on the server side. | ||
520 | return $rnd; | ||
521 | } | ||
522 | |||
523 | // Tells if a token is OK. Using this function will destroy the token. | ||
524 | // true=token is OK. | ||
525 | function tokenOk($token) | ||
526 | { | ||
527 | if (isset($_SESSION['tokens'][$token])) | ||
528 | { | ||
529 | unset($_SESSION['tokens'][$token]); // Token is used: destroy it. | ||
530 | return true; // Token is OK. | ||
531 | } | ||
532 | return false; // Wrong token, or already used. | ||
533 | } | ||
534 | |||
535 | /** | ||
536 | * Daily RSS feed: 1 RSS entry per day giving all the links on that day. | 471 | * Daily RSS feed: 1 RSS entry per day giving all the links on that day. |
537 | * Gives the last 7 days (which have links). | 472 | * Gives the last 7 days (which have links). |
538 | * This RSS feed cannot be filtered. | 473 | * This RSS feed cannot be filtered. |
@@ -602,7 +537,11 @@ function showDailyRSS($conf) { | |||
602 | 537 | ||
603 | // We pre-format some fields for proper output. | 538 | // We pre-format some fields for proper output. |
604 | foreach ($links as &$link) { | 539 | foreach ($links as &$link) { |
605 | $link['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); | 540 | $link['formatedDescription'] = format_description( |
541 | $link['description'], | ||
542 | $conf->get('redirector.url'), | ||
543 | $conf->get('redirector.encode_url') | ||
544 | ); | ||
606 | $link['thumbnail'] = thumbnail($conf, $link['url']); | 545 | $link['thumbnail'] = thumbnail($conf, $link['url']); |
607 | $link['timestamp'] = $link['created']->getTimestamp(); | 546 | $link['timestamp'] = $link['created']->getTimestamp(); |
608 | if (startsWith($link['url'], '?')) { | 547 | if (startsWith($link['url'], '?')) { |
@@ -618,7 +557,7 @@ function showDailyRSS($conf) { | |||
618 | $tpl->assign('links', $links); | 557 | $tpl->assign('links', $links); |
619 | $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); | 558 | $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); |
620 | $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false)); | 559 | $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false)); |
621 | $html = $tpl->draw('dailyrss', $return_string=true); | 560 | $html = $tpl->draw('dailyrss', true); |
622 | 561 | ||
623 | echo $html . PHP_EOL; | 562 | echo $html . PHP_EOL; |
624 | } | 563 | } |
@@ -639,20 +578,29 @@ function showDailyRSS($conf) { | |||
639 | */ | 578 | */ |
640 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | 579 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) |
641 | { | 580 | { |
642 | $day=date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. | 581 | $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD. |
643 | if (isset($_GET['day'])) $day=$_GET['day']; | 582 | if (isset($_GET['day'])) { |
583 | $day = $_GET['day']; | ||
584 | } | ||
644 | 585 | ||
645 | $days = $LINKSDB->days(); | 586 | $days = $LINKSDB->days(); |
646 | $i = array_search($day,$days); | 587 | $i = array_search($day, $days); |
647 | if ($i===false) { $i=count($days)-1; $day=$days[$i]; } | 588 | if ($i === false && count($days)) { |
648 | $previousday=''; | 589 | // no links for day, but at least one day with links |
649 | $nextday=''; | 590 | $i = count($days) - 1; |
650 | if ($i!==false) | 591 | $day = $days[$i]; |
651 | { | ||
652 | if ($i>=1) $previousday=$days[$i-1]; | ||
653 | if ($i<count($days)-1) $nextday=$days[$i+1]; | ||
654 | } | 592 | } |
593 | $previousday = ''; | ||
594 | $nextday = ''; | ||
655 | 595 | ||
596 | if ($i !== false) { | ||
597 | if ($i >= 1) { | ||
598 | $previousday=$days[$i - 1]; | ||
599 | } | ||
600 | if ($i < count($days) - 1) { | ||
601 | $nextday = $days[$i + 1]; | ||
602 | } | ||
603 | } | ||
656 | try { | 604 | try { |
657 | $linksToDisplay = $LINKSDB->filterDay($day); | 605 | $linksToDisplay = $LINKSDB->filterDay($day); |
658 | } catch (Exception $exc) { | 606 | } catch (Exception $exc) { |
@@ -661,13 +609,15 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
661 | } | 609 | } |
662 | 610 | ||
663 | // We pre-format some fields for proper output. | 611 | // We pre-format some fields for proper output. |
664 | foreach($linksToDisplay as $key=>$link) | 612 | foreach($linksToDisplay as $key => $link) { |
665 | { | ||
666 | |||
667 | $taglist = explode(' ',$link['tags']); | 613 | $taglist = explode(' ',$link['tags']); |
668 | uasort($taglist, 'strcasecmp'); | 614 | uasort($taglist, 'strcasecmp'); |
669 | $linksToDisplay[$key]['taglist']=$taglist; | 615 | $linksToDisplay[$key]['taglist']=$taglist; |
670 | $linksToDisplay[$key]['formatedDescription'] = format_description($link['description'], $conf->get('redirector.url')); | 616 | $linksToDisplay[$key]['formatedDescription'] = format_description( |
617 | $link['description'], | ||
618 | $conf->get('redirector.url'), | ||
619 | $conf->get('redirector.encode_url') | ||
620 | ); | ||
671 | $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']); | 621 | $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']); |
672 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); | 622 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); |
673 | } | 623 | } |
@@ -677,28 +627,31 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
677 | so I manually spread entries with a simple method: I roughly evaluate the | 627 | so I manually spread entries with a simple method: I roughly evaluate the |
678 | height of a div according to title and description length. | 628 | height of a div according to title and description length. |
679 | */ | 629 | */ |
680 | $columns=array(array(),array(),array()); // Entries to display, for each column. | 630 | $columns = array(array(), array(), array()); // Entries to display, for each column. |
681 | $fill=array(0,0,0); // Rough estimate of columns fill. | 631 | $fill = array(0, 0, 0); // Rough estimate of columns fill. |
682 | foreach($linksToDisplay as $key=>$link) | 632 | foreach($linksToDisplay as $key => $link) { |
683 | { | ||
684 | // Roughly estimate length of entry (by counting characters) | 633 | // Roughly estimate length of entry (by counting characters) |
685 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. | 634 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. |
686 | // Description: 836 characters gives roughly 342 pixel height. | 635 | // Description: 836 characters gives roughly 342 pixel height. |
687 | // This is not perfect, but it's usually OK. | 636 | // This is not perfect, but it's usually OK. |
688 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; | 637 | $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836; |
689 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. | 638 | if ($link['thumbnail']) { |
639 | $length += 100; // 1 thumbnails roughly takes 100 pixels height. | ||
640 | } | ||
690 | // Then put in column which is the less filled: | 641 | // Then put in column which is the less filled: |
691 | $smallest=min($fill); // find smallest value in array. | 642 | $smallest = min($fill); // find smallest value in array. |
692 | $index=array_search($smallest,$fill); // find index of this smallest value. | 643 | $index = array_search($smallest, $fill); // find index of this smallest value. |
693 | array_push($columns[$index],$link); // Put entry in this column. | 644 | array_push($columns[$index], $link); // Put entry in this column. |
694 | $fill[$index]+=$length; | 645 | $fill[$index] += $length; |
695 | } | 646 | } |
696 | 647 | ||
697 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | 648 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); |
698 | $data = array( | 649 | $data = array( |
650 | 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false), | ||
699 | 'linksToDisplay' => $linksToDisplay, | 651 | 'linksToDisplay' => $linksToDisplay, |
700 | 'cols' => $columns, | 652 | 'cols' => $columns, |
701 | 'day' => $dayDate->getTimestamp(), | 653 | 'day' => $dayDate->getTimestamp(), |
654 | 'dayDate' => $dayDate, | ||
702 | 'previousday' => $previousday, | 655 | 'previousday' => $previousday, |
703 | 'nextday' => $nextday, | 656 | 'nextday' => $nextday, |
704 | ); | 657 | ); |
@@ -729,19 +682,14 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { | |||
729 | /** | 682 | /** |
730 | * Render HTML page (according to URL parameters and user rights) | 683 | * Render HTML page (according to URL parameters and user rights) |
731 | * | 684 | * |
732 | * @param ConfigManager $conf Configuration Manager instance. | 685 | * @param ConfigManager $conf Configuration Manager instance. |
733 | * @param PluginManager $pluginManager Plugin Manager instance, | 686 | * @param PluginManager $pluginManager Plugin Manager instance, |
687 | * @param LinkDB $LINKSDB | ||
688 | * @param History $history instance | ||
689 | * @param SessionManager $sessionManager SessionManager instance | ||
734 | */ | 690 | */ |
735 | function renderPage($conf, $pluginManager) | 691 | function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) |
736 | { | 692 | { |
737 | $LINKSDB = new LinkDB( | ||
738 | $conf->get('resource.datastore'), | ||
739 | isLoggedIn(), | ||
740 | $conf->get('privacy.hide_public_links'), | ||
741 | $conf->get('redirector.url'), | ||
742 | $conf->get('redirector.encode_url') | ||
743 | ); | ||
744 | |||
745 | $updater = new Updater( | 693 | $updater = new Updater( |
746 | read_updates_file($conf->get('resource.updates')), | 694 | read_updates_file($conf->get('resource.updates')), |
747 | $LINKSDB, | 695 | $LINKSDB, |
@@ -761,7 +709,7 @@ function renderPage($conf, $pluginManager) | |||
761 | die($e->getMessage()); | 709 | die($e->getMessage()); |
762 | } | 710 | } |
763 | 711 | ||
764 | $PAGE = new PageBuilder($conf); | 712 | $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken()); |
765 | $PAGE->assign('linkcount', count($LINKSDB)); | 713 | $PAGE->assign('linkcount', count($LINKSDB)); |
766 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); | 714 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); |
767 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); | 715 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); |
@@ -770,6 +718,23 @@ function renderPage($conf, $pluginManager) | |||
770 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; | 718 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; |
771 | $targetPage = Router::findPage($query, $_GET, isLoggedIn()); | 719 | $targetPage = Router::findPage($query, $_GET, isLoggedIn()); |
772 | 720 | ||
721 | if ( | ||
722 | // if the user isn't logged in | ||
723 | !isLoggedIn() && | ||
724 | // and Shaarli doesn't have public content... | ||
725 | $conf->get('privacy.hide_public_links') && | ||
726 | // and is configured to enforce the login | ||
727 | $conf->get('privacy.force_login') && | ||
728 | // and the current page isn't already the login page | ||
729 | $targetPage !== Router::$PAGE_LOGIN && | ||
730 | // and the user is not requesting a feed (which would lead to a different content-type as expected) | ||
731 | $targetPage !== Router::$PAGE_FEED_ATOM && | ||
732 | $targetPage !== Router::$PAGE_FEED_RSS | ||
733 | ) { | ||
734 | // force current page to be the login page | ||
735 | $targetPage = Router::$PAGE_LOGIN; | ||
736 | } | ||
737 | |||
773 | // Call plugin hooks for header, footer and includes, specifying which page will be rendered. | 738 | // Call plugin hooks for header, footer and includes, specifying which page will be rendered. |
774 | // Then assign generated data to RainTPL. | 739 | // Then assign generated data to RainTPL. |
775 | $common_hooks = array( | 740 | $common_hooks = array( |
@@ -797,6 +762,8 @@ function renderPage($conf, $pluginManager) | |||
797 | $PAGE->assign('username', escape($_GET['username'])); | 762 | $PAGE->assign('username', escape($_GET['username'])); |
798 | } | 763 | } |
799 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); | 764 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); |
765 | // add default state of the 'remember me' checkbox | ||
766 | $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); | ||
800 | $PAGE->renderPage('loginform'); | 767 | $PAGE->renderPage('loginform'); |
801 | exit; | 768 | exit; |
802 | } | 769 | } |
@@ -844,7 +811,9 @@ function renderPage($conf, $pluginManager) | |||
844 | // -------- Tag cloud | 811 | // -------- Tag cloud |
845 | if ($targetPage == Router::$PAGE_TAGCLOUD) | 812 | if ($targetPage == Router::$PAGE_TAGCLOUD) |
846 | { | 813 | { |
847 | $tags= $LINKSDB->allTags(); | 814 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; |
815 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; | ||
816 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); | ||
848 | 817 | ||
849 | // We sort tags alphabetically, then choose a font size according to count. | 818 | // We sort tags alphabetically, then choose a font size according to count. |
850 | // First, find max value. | 819 | // First, find max value. |
@@ -853,20 +822,13 @@ function renderPage($conf, $pluginManager) | |||
853 | $maxcount = max($maxcount, $value); | 822 | $maxcount = max($maxcount, $value); |
854 | } | 823 | } |
855 | 824 | ||
856 | // Sort tags alphabetically: case insensitive, support locale if available. | 825 | alphabetical_sort($tags, false, true); |
857 | uksort($tags, function($a, $b) { | ||
858 | // Collator is part of PHP intl. | ||
859 | if (class_exists('Collator')) { | ||
860 | $c = new Collator(setlocale(LC_COLLATE, 0)); | ||
861 | if (!intl_is_failure(intl_get_error_code())) { | ||
862 | return $c->compare($a, $b); | ||
863 | } | ||
864 | } | ||
865 | return strcasecmp($a, $b); | ||
866 | }); | ||
867 | 826 | ||
868 | $tagList = array(); | 827 | $tagList = array(); |
869 | foreach($tags as $key => $value) { | 828 | foreach($tags as $key => $value) { |
829 | if (in_array($key, $filteringTags)) { | ||
830 | continue; | ||
831 | } | ||
870 | // Tag font size scaling: | 832 | // Tag font size scaling: |
871 | // default 15 and 30 logarithm bases affect scaling, | 833 | // default 15 and 30 logarithm bases affect scaling, |
872 | // 22 and 6 are arbitrary font sizes for max and min sizes. | 834 | // 22 and 6 are arbitrary font sizes for max and min sizes. |
@@ -878,6 +840,7 @@ function renderPage($conf, $pluginManager) | |||
878 | } | 840 | } |
879 | 841 | ||
880 | $data = array( | 842 | $data = array( |
843 | 'search_tags' => implode(' ', escape($filteringTags)), | ||
881 | 'tags' => $tagList, | 844 | 'tags' => $tagList, |
882 | ); | 845 | ); |
883 | $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); | 846 | $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); |
@@ -886,7 +849,37 @@ function renderPage($conf, $pluginManager) | |||
886 | $PAGE->assign($key, $value); | 849 | $PAGE->assign($key, $value); |
887 | } | 850 | } |
888 | 851 | ||
889 | $PAGE->renderPage('tagcloud'); | 852 | $PAGE->renderPage('tag.cloud'); |
853 | exit; | ||
854 | } | ||
855 | |||
856 | // -------- Tag list | ||
857 | if ($targetPage == Router::$PAGE_TAGLIST) | ||
858 | { | ||
859 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; | ||
860 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; | ||
861 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); | ||
862 | foreach ($filteringTags as $tag) { | ||
863 | if (array_key_exists($tag, $tags)) { | ||
864 | unset($tags[$tag]); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | if (! empty($_GET['sort']) && $_GET['sort'] === 'alpha') { | ||
869 | alphabetical_sort($tags, false, true); | ||
870 | } | ||
871 | |||
872 | $data = [ | ||
873 | 'search_tags' => implode(' ', escape($filteringTags)), | ||
874 | 'tags' => $tags, | ||
875 | ]; | ||
876 | $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => isLoggedIn()]); | ||
877 | |||
878 | foreach ($data as $key => $value) { | ||
879 | $PAGE->assign($key, $value); | ||
880 | } | ||
881 | |||
882 | $PAGE->renderPage('tag.list'); | ||
890 | exit; | 883 | exit; |
891 | } | 884 | } |
892 | 885 | ||
@@ -918,10 +911,6 @@ function renderPage($conf, $pluginManager) | |||
918 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); | 911 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); |
919 | $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); | 912 | $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); |
920 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); | 913 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); |
921 | $pshUrl = $conf->get('config.PUBSUBHUB_URL'); | ||
922 | if (!empty($pshUrl)) { | ||
923 | $feedGenerator->setPubsubhubUrl($pshUrl); | ||
924 | } | ||
925 | $data = $feedGenerator->buildData(); | 914 | $data = $feedGenerator->buildData(); |
926 | 915 | ||
927 | // Process plugin hook. | 916 | // Process plugin hook. |
@@ -938,7 +927,7 @@ function renderPage($conf, $pluginManager) | |||
938 | exit; | 927 | exit; |
939 | } | 928 | } |
940 | 929 | ||
941 | // Display openseach plugin (XML) | 930 | // Display opensearch plugin (XML) |
942 | if ($targetPage == Router::$PAGE_OPENSEARCH) { | 931 | if ($targetPage == Router::$PAGE_OPENSEARCH) { |
943 | header('Content-Type: application/xml; charset=utf-8'); | 932 | header('Content-Type: application/xml; charset=utf-8'); |
944 | $PAGE->assign('serverurl', index_url($_SERVER)); | 933 | $PAGE->assign('serverurl', index_url($_SERVER)); |
@@ -1023,7 +1012,12 @@ function renderPage($conf, $pluginManager) | |||
1023 | $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); | 1012 | $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); |
1024 | } | 1013 | } |
1025 | 1014 | ||
1026 | header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage'))); | 1015 | if (! empty($_SERVER['HTTP_REFERER'])) { |
1016 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('linksperpage')); | ||
1017 | } else { | ||
1018 | $location = '?'; | ||
1019 | } | ||
1020 | header('Location: '. $location); | ||
1027 | exit; | 1021 | exit; |
1028 | } | 1022 | } |
1029 | 1023 | ||
@@ -1035,7 +1029,25 @@ function renderPage($conf, $pluginManager) | |||
1035 | unset($_SESSION['privateonly']); // See all links | 1029 | unset($_SESSION['privateonly']); // See all links |
1036 | } | 1030 | } |
1037 | 1031 | ||
1038 | header('Location: '. generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly'))); | 1032 | if (! empty($_SERVER['HTTP_REFERER'])) { |
1033 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')); | ||
1034 | } else { | ||
1035 | $location = '?'; | ||
1036 | } | ||
1037 | header('Location: '. $location); | ||
1038 | exit; | ||
1039 | } | ||
1040 | |||
1041 | // -------- User wants to see only untagged links (toggle) | ||
1042 | if (isset($_GET['untaggedonly'])) { | ||
1043 | $_SESSION['untaggedonly'] = empty($_SESSION['untaggedonly']); | ||
1044 | |||
1045 | if (! empty($_SERVER['HTTP_REFERER'])) { | ||
1046 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('untaggedonly')); | ||
1047 | } else { | ||
1048 | $location = '?'; | ||
1049 | } | ||
1050 | header('Location: '. $location); | ||
1039 | exit; | 1051 | exit; |
1040 | } | 1052 | } |
1041 | 1053 | ||
@@ -1046,7 +1058,13 @@ function renderPage($conf, $pluginManager) | |||
1046 | // Show login screen, then redirect to ?post=... | 1058 | // Show login screen, then redirect to ?post=... |
1047 | if (isset($_GET['post'])) | 1059 | if (isset($_GET['post'])) |
1048 | { | 1060 | { |
1049 | header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. | 1061 | header( // Redirect to login page, then back to post link. |
1062 | 'Location: ?do=login&post='.urlencode($_GET['post']). | ||
1063 | (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):''). | ||
1064 | (!empty($_GET['description'])?'&description='.urlencode($_GET['description']):''). | ||
1065 | (!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):''). | ||
1066 | (!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'') | ||
1067 | ); | ||
1050 | exit; | 1068 | exit; |
1051 | } | 1069 | } |
1052 | 1070 | ||
@@ -1064,10 +1082,10 @@ function renderPage($conf, $pluginManager) | |||
1064 | // -------- Display the Tools menu if requested (import/export/bookmarklet...) | 1082 | // -------- Display the Tools menu if requested (import/export/bookmarklet...) |
1065 | if ($targetPage == Router::$PAGE_TOOLS) | 1083 | if ($targetPage == Router::$PAGE_TOOLS) |
1066 | { | 1084 | { |
1067 | $data = array( | 1085 | $data = [ |
1068 | 'pageabsaddr' => index_url($_SERVER), | 1086 | 'pageabsaddr' => index_url($_SERVER), |
1069 | 'sslenabled' => !empty($_SERVER['HTTPS']) | 1087 | 'sslenabled' => is_https($_SERVER), |
1070 | ); | 1088 | ]; |
1071 | $pluginManager->executeHooks('render_tools', $data); | 1089 | $pluginManager->executeHooks('render_tools', $data); |
1072 | 1090 | ||
1073 | foreach ($data as $key => $value) { | 1091 | foreach ($data as $key => $value) { |
@@ -1082,16 +1100,19 @@ function renderPage($conf, $pluginManager) | |||
1082 | if ($targetPage == Router::$PAGE_CHANGEPASSWORD) | 1100 | if ($targetPage == Router::$PAGE_CHANGEPASSWORD) |
1083 | { | 1101 | { |
1084 | if ($conf->get('security.open_shaarli')) { | 1102 | if ($conf->get('security.open_shaarli')) { |
1085 | die('You are not supposed to change a password on an Open Shaarli.'); | 1103 | die(t('You are not supposed to change a password on an Open Shaarli.')); |
1086 | } | 1104 | } |
1087 | 1105 | ||
1088 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) | 1106 | if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) |
1089 | { | 1107 | { |
1090 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! | 1108 | if (!$sessionManager->checkToken($_POST['token'])) die(t('Wrong token.')); // Go away! |
1091 | 1109 | ||
1092 | // Make sure old password is correct. | 1110 | // Make sure old password is correct. |
1093 | $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')); | 1111 | $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')); |
1094 | if ($oldhash!= $conf->get('credentials.hash')) { echo '<script>alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; } | 1112 | if ($oldhash!= $conf->get('credentials.hash')) { |
1113 | echo '<script>alert("'. t('The old password is not correct.') .'");document.location=\'?do=changepasswd\';</script>'; | ||
1114 | exit; | ||
1115 | } | ||
1095 | // Save new password | 1116 | // Save new password |
1096 | // Salt renders rainbow-tables attacks useless. | 1117 | // Salt renders rainbow-tables attacks useless. |
1097 | $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); | 1118 | $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); |
@@ -1109,7 +1130,7 @@ function renderPage($conf, $pluginManager) | |||
1109 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>'; | 1130 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=tools\';</script>'; |
1110 | exit; | 1131 | exit; |
1111 | } | 1132 | } |
1112 | echo '<script>alert("Your password has been changed.");document.location=\'?do=tools\';</script>'; | 1133 | echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>'; |
1113 | exit; | 1134 | exit; |
1114 | } | 1135 | } |
1115 | else // show the change password form. | 1136 | else // show the change password form. |
@@ -1124,8 +1145,8 @@ function renderPage($conf, $pluginManager) | |||
1124 | { | 1145 | { |
1125 | if (!empty($_POST['title']) ) | 1146 | if (!empty($_POST['title']) ) |
1126 | { | 1147 | { |
1127 | if (!tokenOk($_POST['token'])) { | 1148 | if (!$sessionManager->checkToken($_POST['token'])) { |
1128 | die('Wrong token.'); // Go away! | 1149 | die(t('Wrong token.')); // Go away! |
1129 | } | 1150 | } |
1130 | $tz = 'UTC'; | 1151 | $tz = 'UTC'; |
1131 | if (!empty($_POST['continent']) && !empty($_POST['city']) | 1152 | if (!empty($_POST['continent']) && !empty($_POST['city']) |
@@ -1136,14 +1157,21 @@ function renderPage($conf, $pluginManager) | |||
1136 | $conf->set('general.timezone', $tz); | 1157 | $conf->set('general.timezone', $tz); |
1137 | $conf->set('general.title', escape($_POST['title'])); | 1158 | $conf->set('general.title', escape($_POST['title'])); |
1138 | $conf->set('general.header_link', escape($_POST['titleLink'])); | 1159 | $conf->set('general.header_link', escape($_POST['titleLink'])); |
1160 | $conf->set('resource.theme', escape($_POST['theme'])); | ||
1139 | $conf->set('redirector.url', escape($_POST['redirector'])); | 1161 | $conf->set('redirector.url', escape($_POST['redirector'])); |
1140 | $conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection'])); | 1162 | $conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection'])); |
1141 | $conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault'])); | 1163 | $conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault'])); |
1142 | $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); | 1164 | $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); |
1143 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); | 1165 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); |
1144 | $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks'])); | 1166 | $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks'])); |
1167 | $conf->set('api.enabled', !empty($_POST['enableApi'])); | ||
1168 | $conf->set('api.secret', escape($_POST['apiSecret'])); | ||
1169 | $conf->set('translation.language', escape($_POST['language'])); | ||
1170 | |||
1145 | try { | 1171 | try { |
1146 | $conf->write(isLoggedIn()); | 1172 | $conf->write(isLoggedIn()); |
1173 | $history->updateSettings(); | ||
1174 | invalidateCaches($conf->get('resource.page_cache')); | ||
1147 | } | 1175 | } |
1148 | catch(Exception $e) { | 1176 | catch(Exception $e) { |
1149 | error_log( | 1177 | error_log( |
@@ -1155,21 +1183,30 @@ function renderPage($conf, $pluginManager) | |||
1155 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>'; | 1183 | echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do=configure\';</script>'; |
1156 | exit; | 1184 | exit; |
1157 | } | 1185 | } |
1158 | echo '<script>alert("Configuration was saved.");document.location=\'?do=configure\';</script>'; | 1186 | echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>'; |
1159 | exit; | 1187 | exit; |
1160 | } | 1188 | } |
1161 | else // Show the configuration form. | 1189 | else // Show the configuration form. |
1162 | { | 1190 | { |
1163 | $PAGE->assign('title', $conf->get('general.title')); | 1191 | $PAGE->assign('title', $conf->get('general.title')); |
1192 | $PAGE->assign('theme', $conf->get('resource.theme')); | ||
1193 | $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); | ||
1164 | $PAGE->assign('redirector', $conf->get('redirector.url')); | 1194 | $PAGE->assign('redirector', $conf->get('redirector.url')); |
1165 | list($timezone_form, $timezone_js) = generateTimeZoneForm($conf->get('general.timezone')); | 1195 | list($continents, $cities) = generateTimeZoneData( |
1166 | $PAGE->assign('timezone_form', $timezone_form); | 1196 | timezone_identifiers_list(), |
1167 | $PAGE->assign('timezone_js',$timezone_js); | 1197 | $conf->get('general.timezone') |
1198 | ); | ||
1199 | $PAGE->assign('continents', $continents); | ||
1200 | $PAGE->assign('cities', $cities); | ||
1168 | $PAGE->assign('private_links_default', $conf->get('privacy.default_private_links', false)); | 1201 | $PAGE->assign('private_links_default', $conf->get('privacy.default_private_links', false)); |
1169 | $PAGE->assign('session_protection_disabled', $conf->get('security.session_protection_disabled', false)); | 1202 | $PAGE->assign('session_protection_disabled', $conf->get('security.session_protection_disabled', false)); |
1170 | $PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false)); | 1203 | $PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false)); |
1171 | $PAGE->assign('enable_update_check', $conf->get('updates.check_updates', true)); | 1204 | $PAGE->assign('enable_update_check', $conf->get('updates.check_updates', true)); |
1172 | $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); | 1205 | $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); |
1206 | $PAGE->assign('api_enabled', $conf->get('api.enabled', true)); | ||
1207 | $PAGE->assign('api_secret', $conf->get('api.secret')); | ||
1208 | $PAGE->assign('languages', Languages::getAvailableLanguages()); | ||
1209 | $PAGE->assign('language', $conf->get('translation.language')); | ||
1173 | $PAGE->renderPage('configure'); | 1210 | $PAGE->renderPage('configure'); |
1174 | exit; | 1211 | exit; |
1175 | } | 1212 | } |
@@ -1179,48 +1216,28 @@ function renderPage($conf, $pluginManager) | |||
1179 | if ($targetPage == Router::$PAGE_CHANGETAG) | 1216 | if ($targetPage == Router::$PAGE_CHANGETAG) |
1180 | { | 1217 | { |
1181 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { | 1218 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { |
1182 | $PAGE->assign('tags', $LINKSDB->allTags()); | 1219 | $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : ''); |
1183 | $PAGE->renderPage('changetag'); | 1220 | $PAGE->renderPage('changetag'); |
1184 | exit; | 1221 | exit; |
1185 | } | 1222 | } |
1186 | 1223 | ||
1187 | if (!tokenOk($_POST['token'])) { | 1224 | if (!$sessionManager->checkToken($_POST['token'])) { |
1188 | die('Wrong token.'); | 1225 | die(t('Wrong token.')); |
1189 | } | 1226 | } |
1190 | 1227 | ||
1191 | // Delete a tag: | 1228 | $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag'])); |
1192 | if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { | 1229 | $LINKSDB->save($conf->get('resource.page_cache')); |
1193 | $needle = trim($_POST['fromtag']); | 1230 | foreach ($alteredLinks as $link) { |
1194 | // True for case-sensitive tag search. | 1231 | $history->updateLink($link); |
1195 | $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); | 1232 | } |
1196 | foreach($linksToAlter as $key=>$value) | 1233 | $delete = empty($_POST['totag']); |
1197 | { | 1234 | $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag'])); |
1198 | $tags = explode(' ',trim($value['tags'])); | 1235 | $count = count($alteredLinks); |
1199 | unset($tags[array_search($needle,$tags)]); // Remove tag. | 1236 | $alert = $delete |
1200 | $value['tags']=trim(implode(' ',$tags)); | 1237 | ? sprintf(t('The tag was removed from %d link.', 'The tag was removed from %d links.', $count), $count) |
1201 | $LINKSDB[$key]=$value; | 1238 | : sprintf(t('The tag was renamed in %d link.', 'The tag was renamed in %d links.', $count), $count); |
1202 | } | 1239 | echo '<script>alert("'. $alert .'");document.location=\'?'. $redirect .'\';</script>'; |
1203 | $LINKSDB->save($conf->get('resource.page_cache')); | 1240 | exit; |
1204 | echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; | ||
1205 | exit; | ||
1206 | } | ||
1207 | |||
1208 | // Rename a tag: | ||
1209 | if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { | ||
1210 | $needle = trim($_POST['fromtag']); | ||
1211 | // True for case-sensitive tag search. | ||
1212 | $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); | ||
1213 | foreach($linksToAlter as $key=>$value) | ||
1214 | { | ||
1215 | $tags = explode(' ',trim($value['tags'])); | ||
1216 | $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Replace tags value. | ||
1217 | $value['tags']=trim(implode(' ',$tags)); | ||
1218 | $LINKSDB[$key]=$value; | ||
1219 | } | ||
1220 | $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. | ||
1221 | echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; | ||
1222 | exit; | ||
1223 | } | ||
1224 | } | 1241 | } |
1225 | 1242 | ||
1226 | // -------- User wants to add a link without using the bookmarklet: Show form. | 1243 | // -------- User wants to add a link without using the bookmarklet: Show form. |
@@ -1234,27 +1251,29 @@ function renderPage($conf, $pluginManager) | |||
1234 | if (isset($_POST['save_edit'])) | 1251 | if (isset($_POST['save_edit'])) |
1235 | { | 1252 | { |
1236 | // Go away! | 1253 | // Go away! |
1237 | if (! tokenOk($_POST['token'])) { | 1254 | if (! $sessionManager->checkToken($_POST['token'])) { |
1238 | die('Wrong token.'); | 1255 | die(t('Wrong token.')); |
1239 | } | 1256 | } |
1240 | 1257 | ||
1241 | // lf_id should only be present if the link exists. | 1258 | // lf_id should only be present if the link exists. |
1242 | $id = !empty($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); | 1259 | $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); |
1243 | // Linkdate is kept here to: | 1260 | // Linkdate is kept here to: |
1244 | // - use the same permalink for notes as they're displayed when creating them | 1261 | // - use the same permalink for notes as they're displayed when creating them |
1245 | // - let users hack creation date of their posts | 1262 | // - let users hack creation date of their posts |
1246 | // See: https://github.com/shaarli/Shaarli/wiki/Datastore-hacks#changing-the-timestamp-for-a-link | 1263 | // See: https://shaarli.readthedocs.io/en/master/Various-hacks/#changing-the-timestamp-for-a-shaare |
1247 | $linkdate = escape($_POST['lf_linkdate']); | 1264 | $linkdate = escape($_POST['lf_linkdate']); |
1248 | if (isset($LINKSDB[$id])) { | 1265 | if (isset($LINKSDB[$id])) { |
1249 | // Edit | 1266 | // Edit |
1250 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); | 1267 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); |
1251 | $updated = new DateTime(); | 1268 | $updated = new DateTime(); |
1252 | $shortUrl = $LINKSDB[$id]['shorturl']; | 1269 | $shortUrl = $LINKSDB[$id]['shorturl']; |
1270 | $new = false; | ||
1253 | } else { | 1271 | } else { |
1254 | // New link | 1272 | // New link |
1255 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); | 1273 | $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); |
1256 | $updated = null; | 1274 | $updated = null; |
1257 | $shortUrl = link_small_hash($created, $id); | 1275 | $shortUrl = link_small_hash($created, $id); |
1276 | $new = true; | ||
1258 | } | 1277 | } |
1259 | 1278 | ||
1260 | // Remove multiple spaces. | 1279 | // Remove multiple spaces. |
@@ -1264,13 +1283,10 @@ function renderPage($conf, $pluginManager) | |||
1264 | // Remove duplicates. | 1283 | // Remove duplicates. |
1265 | $tags = implode(' ', array_unique(explode(' ', $tags))); | 1284 | $tags = implode(' ', array_unique(explode(' ', $tags))); |
1266 | 1285 | ||
1267 | $url = trim($_POST['lf_url']); | 1286 | if (empty(trim($_POST['lf_url']))) { |
1268 | if (! startsWith($url, 'http:') && ! startsWith($url, 'https:') | 1287 | $_POST['lf_url'] = '?' . smallHash($linkdate . $id); |
1269 | && ! startsWith($url, 'ftp:') && ! startsWith($url, 'magnet:') | ||
1270 | && ! startsWith($url, '?') && ! startsWith($url, 'javascript:') | ||
1271 | ) { | ||
1272 | $url = 'http://' . $url; | ||
1273 | } | 1288 | } |
1289 | $url = whitelist_protocols(trim($_POST['lf_url']), $conf->get('security.allowed_protocols')); | ||
1274 | 1290 | ||
1275 | $link = array( | 1291 | $link = array( |
1276 | 'id' => $id, | 1292 | 'id' => $id, |
@@ -1293,7 +1309,11 @@ function renderPage($conf, $pluginManager) | |||
1293 | 1309 | ||
1294 | $LINKSDB[$id] = $link; | 1310 | $LINKSDB[$id] = $link; |
1295 | $LINKSDB->save($conf->get('resource.page_cache')); | 1311 | $LINKSDB->save($conf->get('resource.page_cache')); |
1296 | pubsubhub($conf); | 1312 | if ($new) { |
1313 | $history->addLink($link); | ||
1314 | } else { | ||
1315 | $history->updateLink($link); | ||
1316 | } | ||
1297 | 1317 | ||
1298 | // If we are called from the bookmarklet, we must close the popup: | 1318 | // If we are called from the bookmarklet, we must close the popup: |
1299 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { | 1319 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { |
@@ -1313,9 +1333,13 @@ function renderPage($conf, $pluginManager) | |||
1313 | // -------- User clicked the "Cancel" button when editing a link. | 1333 | // -------- User clicked the "Cancel" button when editing a link. |
1314 | if (isset($_POST['cancel_edit'])) | 1334 | if (isset($_POST['cancel_edit'])) |
1315 | { | 1335 | { |
1336 | $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false; | ||
1337 | if (! isset($LINKSDB[$id])) { | ||
1338 | header('Location: ?'); | ||
1339 | } | ||
1316 | // If we are called from the bookmarklet, we must close the popup: | 1340 | // If we are called from the bookmarklet, we must close the popup: |
1317 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } | 1341 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } |
1318 | $link = $LINKSDB[(int) escape($_POST['lf_id'])]; | 1342 | $link = $LINKSDB[$id]; |
1319 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 1343 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
1320 | // Scroll to the link which has been edited. | 1344 | // Scroll to the link which has been edited. |
1321 | $returnurl .= '#'. $link['shorturl']; | 1345 | $returnurl .= '#'. $link['shorturl']; |
@@ -1325,49 +1349,44 @@ function renderPage($conf, $pluginManager) | |||
1325 | } | 1349 | } |
1326 | 1350 | ||
1327 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. | 1351 | // -------- User clicked the "Delete" button when editing a link: Delete link from database. |
1328 | if (isset($_POST['delete_link'])) | 1352 | if ($targetPage == Router::$PAGE_DELETELINK) |
1329 | { | 1353 | { |
1330 | if (!tokenOk($_POST['token'])) die('Wrong token.'); | 1354 | if (! $sessionManager->checkToken($_GET['token'])) { |
1331 | 1355 | die(t('Wrong token.')); | |
1332 | // We do not need to ask for confirmation: | 1356 | } |
1333 | // - confirmation is handled by JavaScript | ||
1334 | // - we are protected from XSRF by the token. | ||
1335 | |||
1336 | // FIXME! We keep `lf_linkdate` for consistency before a proper API. To be removed. | ||
1337 | $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : intval(escape($_POST['lf_linkdate'])); | ||
1338 | |||
1339 | $pluginManager->executeHooks('delete_link', $LINKSDB[$id]); | ||
1340 | 1357 | ||
1341 | unset($LINKSDB[$id]); | 1358 | $ids = trim($_GET['lf_linkdate']); |
1342 | $LINKSDB->save('resource.page_cache'); // save to disk | 1359 | if (strpos($ids, ' ') !== false) { |
1360 | // multiple, space-separated ids provided | ||
1361 | $ids = array_values(array_filter(preg_split('/\s+/', escape($ids)))); | ||
1362 | } else { | ||
1363 | // only a single id provided | ||
1364 | $ids = [$ids]; | ||
1365 | } | ||
1366 | // assert at least one id is given | ||
1367 | if(!count($ids)){ | ||
1368 | die('no id provided'); | ||
1369 | } | ||
1370 | foreach ($ids as $id) { | ||
1371 | $id = (int) escape($id); | ||
1372 | $link = $LINKSDB[$id]; | ||
1373 | $pluginManager->executeHooks('delete_link', $link); | ||
1374 | unset($LINKSDB[$id]); | ||
1375 | } | ||
1376 | $LINKSDB->save($conf->get('resource.page_cache')); // save to disk | ||
1377 | $history->deleteLink($link); | ||
1343 | 1378 | ||
1344 | // If we are called from the bookmarklet, we must close the popup: | 1379 | // If we are called from the bookmarklet, we must close the popup: |
1345 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } | 1380 | if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } |
1346 | // Pick where we're going to redirect | 1381 | |
1347 | // ============================================================= | 1382 | $location = '?'; |
1348 | // Basically, we can't redirect to where we were previously if it was a permalink | 1383 | if (isset($_SERVER['HTTP_REFERER'])) { |
1349 | // or an edit_link, because it would 404. | 1384 | // Don't redirect to where we were previously if it was a permalink or an edit_link, because it would 404. |
1350 | // Cases: | 1385 | $location = generateLocation( |
1351 | // - / : nothing in $_GET, redirect to self | 1386 | $_SERVER['HTTP_REFERER'], |
1352 | // - /?page : redirect to self | 1387 | $_SERVER['HTTP_HOST'], |
1353 | // - /?searchterm : redirect to self (there might be other links) | 1388 | ['delete_link', 'edit_link', $link['shorturl']] |
1354 | // - /?searchtags : redirect to self | 1389 | ); |
1355 | // - /permalink : redirect to / (the link does not exist anymore) | ||
1356 | // - /?edit_link : redirect to / (the link does not exist anymore) | ||
1357 | // PHP treats the permalink as a $_GET variable, so we need to check if every condition for self | ||
1358 | // redirect is not satisfied, and only then redirect to / | ||
1359 | $location = "?"; | ||
1360 | // Self redirection | ||
1361 | if (count($_GET) == 0 | ||
1362 | || isset($_GET['page']) | ||
1363 | || isset($_GET['searchterm']) | ||
1364 | || isset($_GET['searchtags']) | ||
1365 | ) { | ||
1366 | if (isset($_POST['returnurl'])) { | ||
1367 | $location = $_POST['returnurl']; // Handle redirects given by the form | ||
1368 | } else { | ||
1369 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('delete_link')); | ||
1370 | } | ||
1371 | } | 1390 | } |
1372 | 1391 | ||
1373 | header('Location: ' . $location); // After deleting the link, redirect to appropriate location | 1392 | header('Location: ' . $location); // After deleting the link, redirect to appropriate location |
@@ -1385,7 +1404,7 @@ function renderPage($conf, $pluginManager) | |||
1385 | 'link' => $link, | 1404 | 'link' => $link, |
1386 | 'link_is_new' => false, | 1405 | 'link_is_new' => false, |
1387 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), | 1406 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), |
1388 | 'tags' => $LINKSDB->allTags(), | 1407 | 'tags' => $LINKSDB->linksCountPerTag(), |
1389 | ); | 1408 | ); |
1390 | $pluginManager->executeHooks('render_editlink', $data); | 1409 | $pluginManager->executeHooks('render_editlink', $data); |
1391 | 1410 | ||
@@ -1417,22 +1436,16 @@ function renderPage($conf, $pluginManager) | |||
1417 | // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) | 1436 | // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) |
1418 | if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { | 1437 | if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { |
1419 | // Short timeout to keep the application responsive | 1438 | // Short timeout to keep the application responsive |
1420 | list($headers, $content) = get_http_response($url, 4); | 1439 | // The callback will fill $charset and $title with data from the downloaded page. |
1421 | if (strpos($headers[0], '200 OK') !== false) { | 1440 | get_http_response($url, 25, 4194304, get_curl_download_callback($charset, $title)); |
1422 | // Retrieve charset. | 1441 | if (! empty($title) && strtolower($charset) != 'utf-8') { |
1423 | $charset = get_charset($headers, $content); | 1442 | $title = mb_convert_encoding($title, 'utf-8', $charset); |
1424 | // Extract title. | ||
1425 | $title = html_extract_title($content); | ||
1426 | // Re-encode title in utf-8 if necessary. | ||
1427 | if (! empty($title) && strtolower($charset) != 'utf-8') { | ||
1428 | $title = mb_convert_encoding($title, 'utf-8', $charset); | ||
1429 | } | ||
1430 | } | 1443 | } |
1431 | } | 1444 | } |
1432 | 1445 | ||
1433 | if ($url == '') { | 1446 | if ($url == '') { |
1434 | $url = '?' . smallHash($linkdate . $LINKSDB->getNextId()); | 1447 | $url = '?' . smallHash($linkdate . $LINKSDB->getNextId()); |
1435 | $title = 'Note: '; | 1448 | $title = $conf->get('general.default_note_title', t('Note: ')); |
1436 | } | 1449 | } |
1437 | $url = escape($url); | 1450 | $url = escape($url); |
1438 | $title = escape($title); | 1451 | $title = escape($title); |
@@ -1443,7 +1456,7 @@ function renderPage($conf, $pluginManager) | |||
1443 | 'url' => $url, | 1456 | 'url' => $url, |
1444 | 'description' => $description, | 1457 | 'description' => $description, |
1445 | 'tags' => $tags, | 1458 | 'tags' => $tags, |
1446 | 'private' => $private | 1459 | 'private' => $private, |
1447 | ); | 1460 | ); |
1448 | } else { | 1461 | } else { |
1449 | $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); | 1462 | $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); |
@@ -1454,7 +1467,7 @@ function renderPage($conf, $pluginManager) | |||
1454 | 'link_is_new' => $link_is_new, | 1467 | 'link_is_new' => $link_is_new, |
1455 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), | 1468 | 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), |
1456 | 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), | 1469 | 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), |
1457 | 'tags' => $LINKSDB->allTags(), | 1470 | 'tags' => $LINKSDB->linksCountPerTag(), |
1458 | 'default_private_links' => $conf->get('privacy.default_private_links', false), | 1471 | 'default_private_links' => $conf->get('privacy.default_private_links', false), |
1459 | ); | 1472 | ); |
1460 | $pluginManager->executeHooks('render_editlink', $data); | 1473 | $pluginManager->executeHooks('render_editlink', $data); |
@@ -1516,7 +1529,22 @@ function renderPage($conf, $pluginManager) | |||
1516 | 1529 | ||
1517 | if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) { | 1530 | if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) { |
1518 | // Show import dialog | 1531 | // Show import dialog |
1519 | $PAGE->assign('maxfilesize', getMaxFileSize()); | 1532 | $PAGE->assign( |
1533 | 'maxfilesize', | ||
1534 | get_max_upload_size( | ||
1535 | ini_get('post_max_size'), | ||
1536 | ini_get('upload_max_filesize'), | ||
1537 | false | ||
1538 | ) | ||
1539 | ); | ||
1540 | $PAGE->assign( | ||
1541 | 'maxfilesizeHuman', | ||
1542 | get_max_upload_size( | ||
1543 | ini_get('post_max_size'), | ||
1544 | ini_get('upload_max_filesize'), | ||
1545 | true | ||
1546 | ) | ||
1547 | ); | ||
1520 | $PAGE->renderPage('import'); | 1548 | $PAGE->renderPage('import'); |
1521 | exit; | 1549 | exit; |
1522 | } | 1550 | } |
@@ -1524,21 +1552,25 @@ function renderPage($conf, $pluginManager) | |||
1524 | // Import bookmarks from an uploaded file | 1552 | // Import bookmarks from an uploaded file |
1525 | if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) { | 1553 | if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) { |
1526 | // The file is too big or some form field may be missing. | 1554 | // The file is too big or some form field may be missing. |
1527 | echo '<script>alert("The file you are trying to upload is probably' | 1555 | $msg = sprintf( |
1528 | .' bigger than what this webserver can accept (' | 1556 | t( |
1529 | .getMaxFileSize().' bytes).' | 1557 | 'The file you are trying to upload is probably bigger than what this webserver can accept' |
1530 | .' Please upload in smaller chunks.");document.location=\'?do=' | 1558 | .' (%s). Please upload in smaller chunks.' |
1531 | .Router::$PAGE_IMPORT .'\';</script>'; | 1559 | ), |
1560 | get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')) | ||
1561 | ); | ||
1562 | echo '<script>alert("'. $msg .'");document.location=\'?do='.Router::$PAGE_IMPORT .'\';</script>'; | ||
1532 | exit; | 1563 | exit; |
1533 | } | 1564 | } |
1534 | if (! tokenOk($_POST['token'])) { | 1565 | if (! $sessionManager->checkToken($_POST['token'])) { |
1535 | die('Wrong token.'); | 1566 | die('Wrong token.'); |
1536 | } | 1567 | } |
1537 | $status = NetscapeBookmarkUtils::import( | 1568 | $status = NetscapeBookmarkUtils::import( |
1538 | $_POST, | 1569 | $_POST, |
1539 | $_FILES, | 1570 | $_FILES, |
1540 | $LINKSDB, | 1571 | $LINKSDB, |
1541 | $conf->get('resource.page_cache') | 1572 | $conf, |
1573 | $history | ||
1542 | ); | 1574 | ); |
1543 | echo '<script>alert("'.$status.'");document.location=\'?do=' | 1575 | echo '<script>alert("'.$status.'");document.location=\'?do=' |
1544 | .Router::$PAGE_IMPORT .'\';</script>'; | 1576 | .Router::$PAGE_IMPORT .'\';</script>'; |
@@ -1578,6 +1610,7 @@ function renderPage($conf, $pluginManager) | |||
1578 | $conf->set('general.enabled_plugins', save_plugin_config($_POST)); | 1610 | $conf->set('general.enabled_plugins', save_plugin_config($_POST)); |
1579 | } | 1611 | } |
1580 | $conf->write(isLoggedIn()); | 1612 | $conf->write(isLoggedIn()); |
1613 | $history->updateSettings(); | ||
1581 | } | 1614 | } |
1582 | catch (Exception $e) { | 1615 | catch (Exception $e) { |
1583 | error_log( | 1616 | error_log( |
@@ -1593,6 +1626,13 @@ function renderPage($conf, $pluginManager) | |||
1593 | exit; | 1626 | exit; |
1594 | } | 1627 | } |
1595 | 1628 | ||
1629 | // Get a fresh token | ||
1630 | if ($targetPage == Router::$GET_TOKEN) { | ||
1631 | header('Content-Type:text/plain'); | ||
1632 | echo $sessionManager->generateToken($conf); | ||
1633 | exit; | ||
1634 | } | ||
1635 | |||
1596 | // -------- Otherwise, simply display search form and links: | 1636 | // -------- Otherwise, simply display search form and links: |
1597 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); | 1637 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); |
1598 | exit; | 1638 | exit; |
@@ -1610,8 +1650,16 @@ function renderPage($conf, $pluginManager) | |||
1610 | function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | 1650 | function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) |
1611 | { | 1651 | { |
1612 | // Used in templates | 1652 | // Used in templates |
1613 | $searchtags = !empty($_GET['searchtags']) ? escape($_GET['searchtags']) : ''; | 1653 | if (isset($_GET['searchtags'])) { |
1614 | $searchterm = !empty($_GET['searchterm']) ? escape($_GET['searchterm']) : ''; | 1654 | if (! empty($_GET['searchtags'])) { |
1655 | $searchtags = escape(normalize_spaces($_GET['searchtags'])); | ||
1656 | } else { | ||
1657 | $searchtags = false; | ||
1658 | } | ||
1659 | } else { | ||
1660 | $searchtags = ''; | ||
1661 | } | ||
1662 | $searchterm = !empty($_GET['searchterm']) ? escape(normalize_spaces($_GET['searchterm'])) : ''; | ||
1615 | 1663 | ||
1616 | // Smallhash filter | 1664 | // Smallhash filter |
1617 | if (! empty($_SERVER['QUERY_STRING']) | 1665 | if (! empty($_SERVER['QUERY_STRING']) |
@@ -1624,8 +1672,12 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1624 | } | 1672 | } |
1625 | } else { | 1673 | } else { |
1626 | // Filter links according search parameters. | 1674 | // Filter links according search parameters. |
1627 | $privateonly = !empty($_SESSION['privateonly']); | 1675 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; |
1628 | $linksToDisplay = $LINKSDB->filterSearch($_GET, false, $privateonly); | 1676 | $request = [ |
1677 | 'searchtags' => $searchtags, | ||
1678 | 'searchterm' => $searchterm, | ||
1679 | ]; | ||
1680 | $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility, !empty($_SESSION['untaggedonly'])); | ||
1629 | } | 1681 | } |
1630 | 1682 | ||
1631 | // ---- Handle paging. | 1683 | // ---- Handle paging. |
@@ -1649,7 +1701,11 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1649 | while ($i<$end && $i<count($keys)) | 1701 | while ($i<$end && $i<count($keys)) |
1650 | { | 1702 | { |
1651 | $link = $linksToDisplay[$keys[$i]]; | 1703 | $link = $linksToDisplay[$keys[$i]]; |
1652 | $link['description'] = format_description($link['description'], $conf->get('redirector.url')); | 1704 | $link['description'] = format_description( |
1705 | $link['description'], | ||
1706 | $conf->get('redirector.url'), | ||
1707 | $conf->get('redirector.encode_url') | ||
1708 | ); | ||
1653 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; | 1709 | $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; |
1654 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; | 1710 | $link['class'] = $link['private'] == 0 ? $classLi : 'private'; |
1655 | $link['timestamp'] = $link['created']->getTimestamp(); | 1711 | $link['timestamp'] = $link['created']->getTimestamp(); |
@@ -1658,7 +1714,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1658 | } else { | 1714 | } else { |
1659 | $link['updated_timestamp'] = ''; | 1715 | $link['updated_timestamp'] = ''; |
1660 | } | 1716 | } |
1661 | $taglist = explode(' ', $link['tags']); | 1717 | $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); |
1662 | uasort($taglist, 'strcasecmp'); | 1718 | uasort($taglist, 'strcasecmp'); |
1663 | $link['taglist'] = $taglist; | 1719 | $link['taglist'] = $taglist; |
1664 | // Check for both signs of a note: starting with ? and 7 chars long. | 1720 | // Check for both signs of a note: starting with ? and 7 chars long. |
@@ -1672,7 +1728,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1672 | } | 1728 | } |
1673 | 1729 | ||
1674 | // Compute paging navigation | 1730 | // Compute paging navigation |
1675 | $searchtagsUrl = empty($searchtags) ? '' : '&searchtags=' . urlencode($searchtags); | 1731 | $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); |
1676 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); | 1732 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); |
1677 | $previous_page_url = ''; | 1733 | $previous_page_url = ''; |
1678 | if ($i != count($keys)) { | 1734 | if ($i != count($keys)) { |
@@ -1692,9 +1748,9 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1692 | 'result_count' => count($linksToDisplay), | 1748 | 'result_count' => count($linksToDisplay), |
1693 | 'search_term' => $searchterm, | 1749 | 'search_term' => $searchterm, |
1694 | 'search_tags' => $searchtags, | 1750 | 'search_tags' => $searchtags, |
1751 | 'visibility' => ! empty($_SESSION['privateonly']) ? 'private' : '', | ||
1695 | 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. | 1752 | 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. |
1696 | 'links' => $linkDisp, | 1753 | 'links' => $linkDisp, |
1697 | 'tags' => $LINKSDB->allTags(), | ||
1698 | ); | 1754 | ); |
1699 | 1755 | ||
1700 | // If there is only a single link, we change on-the-fly the title of the page. | 1756 | // If there is only a single link, we change on-the-fly the title of the page. |
@@ -1903,10 +1959,10 @@ function lazyThumbnail($conf, $url,$href=false) | |||
1903 | * Installation | 1959 | * Installation |
1904 | * This function should NEVER be called if the file data/config.php exists. | 1960 | * This function should NEVER be called if the file data/config.php exists. |
1905 | * | 1961 | * |
1906 | * @param ConfigManager $conf Configuration Manager instance. | 1962 | * @param ConfigManager $conf Configuration Manager instance. |
1963 | * @param SessionManager $sessionManager SessionManager instance | ||
1907 | */ | 1964 | */ |
1908 | function install($conf) | 1965 | function install($conf, $sessionManager) { |
1909 | { | ||
1910 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. | 1966 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. |
1911 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); | 1967 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); |
1912 | 1968 | ||
@@ -1915,12 +1971,20 @@ function install($conf) | |||
1915 | // (Because on some hosts, session.save_path may not be set correctly, | 1971 | // (Because on some hosts, session.save_path may not be set correctly, |
1916 | // or we may not have write access to it.) | 1972 | // or we may not have write access to it.) |
1917 | if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) | 1973 | if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) |
1918 | { // Step 2: Check if data in session is correct. | 1974 | { |
1919 | echo '<pre>Sessions do not seem to work correctly on your server.<br>'; | 1975 | // Step 2: Check if data in session is correct. |
1920 | echo 'Make sure the variable session.save_path is set correctly in your php config, and that you have write access to it.<br>'; | 1976 | $msg = t( |
1921 | echo 'It currently points to '.session_save_path().'<br>'; | 1977 | '<pre>Sessions do not seem to work correctly on your server.<br>'. |
1922 | echo 'Check that the hostname used to access Shaarli contains a dot. On some browsers, accessing your server via a hostname like \'localhost\' or any custom hostname without a dot causes cookie storage to fail. We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>'; | 1978 | 'Make sure the variable "session.save_path" is set correctly in your PHP config, '. |
1923 | echo '<br><a href="?">Click to try again.</a></pre>'; | 1979 | 'and that you have write access to it.<br>'. |
1980 | 'It currently points to %s.<br>'. | ||
1981 | 'On some browsers, accessing your server via a hostname like \'localhost\' '. | ||
1982 | 'or any custom hostname without a dot causes cookie storage to fail. '. | ||
1983 | 'We recommend accessing your server via it\'s IP address or Fully Qualified Domain Name.<br>' | ||
1984 | ); | ||
1985 | $msg = sprintf($msg, session_save_path()); | ||
1986 | echo $msg; | ||
1987 | echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>'; | ||
1924 | die; | 1988 | die; |
1925 | } | 1989 | } |
1926 | if (!isset($_SESSION['session_tested'])) | 1990 | if (!isset($_SESSION['session_tested'])) |
@@ -1953,7 +2017,16 @@ function install($conf) | |||
1953 | } else { | 2017 | } else { |
1954 | $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); | 2018 | $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); |
1955 | } | 2019 | } |
2020 | $conf->set('translation.language', escape($_POST['language'])); | ||
1956 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); | 2021 | $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); |
2022 | $conf->set('api.enabled', !empty($_POST['enableApi'])); | ||
2023 | $conf->set( | ||
2024 | 'api.secret', | ||
2025 | generate_api_secret( | ||
2026 | $conf->get('credentials.login'), | ||
2027 | $conf->get('credentials.salt') | ||
2028 | ) | ||
2029 | ); | ||
1957 | try { | 2030 | try { |
1958 | // Everything is ok, let's create config file. | 2031 | // Everything is ok, let's create config file. |
1959 | $conf->write(isLoggedIn()); | 2032 | $conf->write(isLoggedIn()); |
@@ -1972,16 +2045,11 @@ function install($conf) | |||
1972 | exit; | 2045 | exit; |
1973 | } | 2046 | } |
1974 | 2047 | ||
1975 | // Display config form: | 2048 | $PAGE = new PageBuilder($conf, null, $sessionManager->generateToken()); |
1976 | list($timezone_form, $timezone_js) = generateTimeZoneForm(); | 2049 | list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); |
1977 | $timezone_html = ''; | 2050 | $PAGE->assign('continents', $continents); |
1978 | if ($timezone_form != '') { | 2051 | $PAGE->assign('cities', $cities); |
1979 | $timezone_html = '<tr><td><b>Timezone:</b></td><td>'.$timezone_form.'</td></tr>'; | 2052 | $PAGE->assign('languages', Languages::getAvailableLanguages()); |
1980 | } | ||
1981 | |||
1982 | $PAGE = new PageBuilder($conf); | ||
1983 | $PAGE->assign('timezone_html',$timezone_html); | ||
1984 | $PAGE->assign('timezone_js',$timezone_js); | ||
1985 | $PAGE->renderPage('install'); | 2053 | $PAGE->renderPage('install'); |
1986 | exit; | 2054 | exit; |
1987 | } | 2055 | } |
@@ -2216,4 +2284,45 @@ if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do= | |||
2216 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { | 2284 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { |
2217 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); | 2285 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); |
2218 | } | 2286 | } |
2219 | renderPage($conf, $pluginManager); | 2287 | |
2288 | try { | ||
2289 | $history = new History($conf->get('resource.history')); | ||
2290 | } catch(Exception $e) { | ||
2291 | die($e->getMessage()); | ||
2292 | } | ||
2293 | |||
2294 | $linkDb = new LinkDB( | ||
2295 | $conf->get('resource.datastore'), | ||
2296 | isLoggedIn(), | ||
2297 | $conf->get('privacy.hide_public_links'), | ||
2298 | $conf->get('redirector.url'), | ||
2299 | $conf->get('redirector.encode_url') | ||
2300 | ); | ||
2301 | |||
2302 | $container = new \Slim\Container(); | ||
2303 | $container['conf'] = $conf; | ||
2304 | $container['plugins'] = $pluginManager; | ||
2305 | $container['history'] = $history; | ||
2306 | $app = new \Slim\App($container); | ||
2307 | |||
2308 | // REST API routes | ||
2309 | $app->group('/api/v1', function() { | ||
2310 | $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); | ||
2311 | $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); | ||
2312 | $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); | ||
2313 | $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); | ||
2314 | $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); | ||
2315 | $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); | ||
2316 | $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory'); | ||
2317 | })->add('\Shaarli\Api\ApiMiddleware'); | ||
2318 | |||
2319 | $response = $app->run(true); | ||
2320 | // Hack to make Slim and Shaarli router work together: | ||
2321 | // If a Slim route isn't found and NOT API call, we call renderPage(). | ||
2322 | if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { | ||
2323 | // We use UTF-8 for proper international characters handling. | ||
2324 | header('Content-Type: text/html; charset=utf-8'); | ||
2325 | renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager); | ||
2326 | } else { | ||
2327 | $app->respond($response); | ||
2328 | } | ||