diff options
Diffstat (limited to 'index.php')
-rw-r--r-- | index.php | 982 |
1 files changed, 263 insertions, 719 deletions
@@ -48,7 +48,7 @@ if (! file_exists(__DIR__ . '/vendor/autoload.php')) { | |||
48 | ."If you installed Shaarli through Git or using the development branch,\n" | 48 | ."If you installed Shaarli through Git or using the development branch,\n" |
49 | ."please refer to the installation documentation to install PHP" | 49 | ."please refer to the installation documentation to install PHP" |
50 | ." dependencies using Composer:\n" | 50 | ." dependencies using Composer:\n" |
51 | ."- https://shaarli.readthedocs.io/en/master/Server-requirements/\n" | 51 | ."- https://shaarli.readthedocs.io/en/master/Server-configuration/\n" |
52 | ."- https://shaarli.readthedocs.io/en/master/Download-and-Installation/"; | 52 | ."- https://shaarli.readthedocs.io/en/master/Download-and-Installation/"; |
53 | exit; | 53 | exit; |
54 | } | 54 | } |
@@ -75,10 +75,12 @@ require_once 'application/Utils.php'; | |||
75 | require_once 'application/PluginManager.php'; | 75 | require_once 'application/PluginManager.php'; |
76 | require_once 'application/Router.php'; | 76 | require_once 'application/Router.php'; |
77 | require_once 'application/Updater.php'; | 77 | require_once 'application/Updater.php'; |
78 | use \Shaarli\Config\ConfigManager; | ||
78 | use \Shaarli\Languages; | 79 | use \Shaarli\Languages; |
80 | use \Shaarli\Security\LoginManager; | ||
81 | use \Shaarli\Security\SessionManager; | ||
79 | use \Shaarli\ThemeUtils; | 82 | use \Shaarli\ThemeUtils; |
80 | use \Shaarli\Config\ConfigManager; | 83 | use \Shaarli\Thumbnailer; |
81 | use \Shaarli\SessionManager; | ||
82 | 84 | ||
83 | // Ensure the PHP version is supported | 85 | // Ensure the PHP version is supported |
84 | try { | 86 | try { |
@@ -100,8 +102,6 @@ if (dirname($_SERVER['SCRIPT_NAME']) != '/') { | |||
100 | // Set default cookie expiration and path. | 102 | // Set default cookie expiration and path. |
101 | session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['SERVER_NAME']); | 103 | session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['SERVER_NAME']); |
102 | // Set session parameters on server side. | 104 | // Set session parameters on server side. |
103 | // If the user does not access any page within this time, his/her session is considered expired. | ||
104 | define('INACTIVITY_TIMEOUT', 3600); // in seconds. | ||
105 | // Use cookies to store session. | 105 | // Use cookies to store session. |
106 | ini_set('session.use_cookies', 1); | 106 | ini_set('session.use_cookies', 1); |
107 | // Force cookies for session (phpsessionID forbidden in URL). | 107 | // Force cookies for session (phpsessionID forbidden in URL). |
@@ -123,6 +123,9 @@ if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli'])) | |||
123 | 123 | ||
124 | $conf = new ConfigManager(); | 124 | $conf = new ConfigManager(); |
125 | $sessionManager = new SessionManager($_SESSION, $conf); | 125 | $sessionManager = new SessionManager($_SESSION, $conf); |
126 | $loginManager = new LoginManager($GLOBALS, $conf, $sessionManager); | ||
127 | $loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']); | ||
128 | $clientIpId = client_ip_id($_SERVER); | ||
126 | 129 | ||
127 | // LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. | 130 | // LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead. |
128 | if (! defined('LC_MESSAGES')) { | 131 | if (! defined('LC_MESSAGES')) { |
@@ -172,246 +175,64 @@ if (! is_file($conf->getConfigFileExt())) { | |||
172 | } | 175 | } |
173 | 176 | ||
174 | // Display the installation form if no existing config is found | 177 | // Display the installation form if no existing config is found |
175 | install($conf, $sessionManager); | 178 | install($conf, $sessionManager, $loginManager); |
176 | } | 179 | } |
177 | 180 | ||
178 | // a token depending of deployment salt, user password, and the current ip | 181 | $loginManager->checkLoginState($_COOKIE, $clientIpId); |
179 | define('STAY_SIGNED_IN_TOKEN', sha1($conf->get('credentials.hash') . $_SERVER['REMOTE_ADDR'] . $conf->get('credentials.salt'))); | ||
180 | 182 | ||
181 | /** | 183 | /** |
182 | * Checking session state (i.e. is the user still logged in) | 184 | * Adapter function to ensure compatibility with third-party templates |
183 | * | 185 | * |
184 | * @param ConfigManager $conf The configuration manager. | 186 | * @see https://github.com/shaarli/Shaarli/pull/1086 |
185 | * | 187 | * |
186 | * @return bool: true if the user is logged in, false otherwise. | 188 | * @return bool true when the user is logged in, false otherwise |
187 | */ | 189 | */ |
188 | function setup_login_state($conf) | ||
189 | { | ||
190 | if ($conf->get('security.open_shaarli')) { | ||
191 | return true; | ||
192 | } | ||
193 | $userIsLoggedIn = false; // By default, we do not consider the user as logged in; | ||
194 | $loginFailure = false; // If set to true, every attempt to authenticate the user will fail. This indicates that an important condition isn't met. | ||
195 | if (! $conf->exists('credentials.login')) { | ||
196 | $userIsLoggedIn = false; // Shaarli is not configured yet. | ||
197 | $loginFailure = true; | ||
198 | } | ||
199 | if (isset($_COOKIE['shaarli_staySignedIn']) && | ||
200 | $_COOKIE['shaarli_staySignedIn']===STAY_SIGNED_IN_TOKEN && | ||
201 | !$loginFailure) | ||
202 | { | ||
203 | fillSessionInfo($conf); | ||
204 | $userIsLoggedIn = true; | ||
205 | } | ||
206 | // If session does not exist on server side, or IP address has changed, or session has expired, logout. | ||
207 | if (empty($_SESSION['uid']) | ||
208 | || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != allIPs()) | ||
209 | || time() >= $_SESSION['expires_on']) | ||
210 | { | ||
211 | logout(); | ||
212 | $userIsLoggedIn = false; | ||
213 | $loginFailure = true; | ||
214 | } | ||
215 | if (!empty($_SESSION['longlastingsession'])) { | ||
216 | $_SESSION['expires_on']=time()+$_SESSION['longlastingsession']; // In case of "Stay signed in" checked. | ||
217 | } | ||
218 | else { | ||
219 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Standard session expiration date. | ||
220 | } | ||
221 | if (!$loginFailure) { | ||
222 | $userIsLoggedIn = true; | ||
223 | } | ||
224 | |||
225 | return $userIsLoggedIn; | ||
226 | } | ||
227 | $userIsLoggedIn = setup_login_state($conf); | ||
228 | |||
229 | // ------------------------------------------------------------------------------------------ | ||
230 | // Session management | ||
231 | |||
232 | // Returns the IP address of the client (Used to prevent session cookie hijacking.) | ||
233 | function allIPs() | ||
234 | { | ||
235 | $ip = $_SERVER['REMOTE_ADDR']; | ||
236 | // Then we use more HTTP headers to prevent session hijacking from users behind the same proxy. | ||
237 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip=$ip.'_'.$_SERVER['HTTP_X_FORWARDED_FOR']; } | ||
238 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip=$ip.'_'.$_SERVER['HTTP_CLIENT_IP']; } | ||
239 | return $ip; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * Load user session. | ||
244 | * | ||
245 | * @param ConfigManager $conf Configuration Manager instance. | ||
246 | */ | ||
247 | function fillSessionInfo($conf) | ||
248 | { | ||
249 | $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) | ||
250 | $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. | ||
251 | $_SESSION['username']= $conf->get('credentials.login'); | ||
252 | $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * Check that user/password is correct. | ||
257 | * | ||
258 | * @param string $login Username | ||
259 | * @param string $password User password | ||
260 | * @param ConfigManager $conf Configuration Manager instance. | ||
261 | * | ||
262 | * @return bool: authentication successful or not. | ||
263 | */ | ||
264 | function check_auth($login, $password, $conf) | ||
265 | { | ||
266 | $hash = sha1($password . $login . $conf->get('credentials.salt')); | ||
267 | if ($login == $conf->get('credentials.login') && $hash == $conf->get('credentials.hash')) | ||
268 | { // Login/password is correct. | ||
269 | fillSessionInfo($conf); | ||
270 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login successful'); | ||
271 | return true; | ||
272 | } | ||
273 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Login failed for user '.$login); | ||
274 | return false; | ||
275 | } | ||
276 | |||
277 | // Returns true if the user is logged in. | ||
278 | function isLoggedIn() | 190 | function isLoggedIn() |
279 | { | 191 | { |
280 | global $userIsLoggedIn; | 192 | global $loginManager; |
281 | return $userIsLoggedIn; | 193 | return $loginManager->isLoggedIn(); |
282 | } | ||
283 | |||
284 | // Force logout. | ||
285 | function logout() { | ||
286 | if (isset($_SESSION)) { | ||
287 | unset($_SESSION['uid']); | ||
288 | unset($_SESSION['ip']); | ||
289 | unset($_SESSION['username']); | ||
290 | unset($_SESSION['privateonly']); | ||
291 | unset($_SESSION['untaggedonly']); | ||
292 | } | ||
293 | setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); | ||
294 | } | 194 | } |
295 | 195 | ||
296 | 196 | ||
297 | // ------------------------------------------------------------------------------------------ | 197 | // ------------------------------------------------------------------------------------------ |
298 | // Brute force protection system | ||
299 | // Several consecutive failed logins will ban the IP address for 30 minutes. | ||
300 | if (!is_file($conf->get('resource.ban_file', 'data/ipbans.php'))) { | ||
301 | // FIXME! globals | ||
302 | file_put_contents( | ||
303 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
304 | "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(),'BANS'=>array()),true).";\n?>" | ||
305 | ); | ||
306 | } | ||
307 | include $conf->get('resource.ban_file', 'data/ipbans.php'); | ||
308 | /** | ||
309 | * Signal a failed login. Will ban the IP if too many failures: | ||
310 | * | ||
311 | * @param ConfigManager $conf Configuration Manager instance. | ||
312 | */ | ||
313 | function ban_loginFailed($conf) | ||
314 | { | ||
315 | $ip = $_SERVER['REMOTE_ADDR']; | ||
316 | $trusted = $conf->get('security.trusted_proxies', array()); | ||
317 | if (in_array($ip, $trusted)) { | ||
318 | $ip = getIpAddressFromProxy($_SERVER, $trusted); | ||
319 | if (!$ip) { | ||
320 | return; | ||
321 | } | ||
322 | } | ||
323 | $gb = $GLOBALS['IPBANS']; | ||
324 | if (! isset($gb['FAILURES'][$ip])) { | ||
325 | $gb['FAILURES'][$ip]=0; | ||
326 | } | ||
327 | $gb['FAILURES'][$ip]++; | ||
328 | if ($gb['FAILURES'][$ip] > ($conf->get('security.ban_after') - 1)) | ||
329 | { | ||
330 | $gb['BANS'][$ip] = time() + $conf->get('security.ban_after', 1800); | ||
331 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'IP address banned from login'); | ||
332 | } | ||
333 | $GLOBALS['IPBANS'] = $gb; | ||
334 | file_put_contents( | ||
335 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
336 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
337 | ); | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * Signals a successful login. Resets failed login counter. | ||
342 | * | ||
343 | * @param ConfigManager $conf Configuration Manager instance. | ||
344 | */ | ||
345 | function ban_loginOk($conf) | ||
346 | { | ||
347 | $ip = $_SERVER['REMOTE_ADDR']; | ||
348 | $gb = $GLOBALS['IPBANS']; | ||
349 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); | ||
350 | $GLOBALS['IPBANS'] = $gb; | ||
351 | file_put_contents( | ||
352 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
353 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
354 | ); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * Checks if the user CAN login. If 'true', the user can try to login. | ||
359 | * | ||
360 | * @param ConfigManager $conf Configuration Manager instance. | ||
361 | * | ||
362 | * @return bool: true if the user is allowed to login. | ||
363 | */ | ||
364 | function ban_canLogin($conf) | ||
365 | { | ||
366 | $ip=$_SERVER["REMOTE_ADDR"]; $gb=$GLOBALS['IPBANS']; | ||
367 | if (isset($gb['BANS'][$ip])) | ||
368 | { | ||
369 | // User is banned. Check if the ban has expired: | ||
370 | if ($gb['BANS'][$ip]<=time()) | ||
371 | { // Ban expired, user can try to login again. | ||
372 | logm($conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], 'Ban lifted.'); | ||
373 | unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]); | ||
374 | file_put_contents( | ||
375 | $conf->get('resource.ban_file', 'data/ipbans.php'), | ||
376 | "<?php\n\$GLOBALS['IPBANS']=".var_export($gb,true).";\n?>" | ||
377 | ); | ||
378 | return true; // Ban has expired, user can login. | ||
379 | } | ||
380 | return false; // User is banned. | ||
381 | } | ||
382 | return true; // User is not banned. | ||
383 | } | ||
384 | |||
385 | // ------------------------------------------------------------------------------------------ | ||
386 | // Process login form: Check if login/password is correct. | 198 | // Process login form: Check if login/password is correct. |
387 | if (isset($_POST['login'])) | 199 | if (isset($_POST['login'])) { |
388 | { | 200 | if (! $loginManager->canLogin($_SERVER)) { |
389 | if (!ban_canLogin($conf)) die(t('I said: NO. You are banned for the moment. Go away.')); | 201 | die(t('I said: NO. You are banned for the moment. Go away.')); |
202 | } | ||
390 | if (isset($_POST['password']) | 203 | if (isset($_POST['password']) |
391 | && $sessionManager->checkToken($_POST['token']) | 204 | && $sessionManager->checkToken($_POST['token']) |
392 | && (check_auth($_POST['login'], $_POST['password'], $conf)) | 205 | && $loginManager->checkCredentials($_SERVER['REMOTE_ADDR'], $clientIpId, $_POST['login'], $_POST['password']) |
393 | ) { // Login/password is OK. | 206 | ) { |
394 | ban_loginOk($conf); | 207 | $loginManager->handleSuccessfulLogin($_SERVER); |
395 | // If user wants to keep the session cookie even after the browser closes: | ||
396 | if (!empty($_POST['longlastingsession'])) | ||
397 | { | ||
398 | $_SESSION['longlastingsession'] = 31536000; // (31536000 seconds = 1 year) | ||
399 | $expiration = time() + $_SESSION['longlastingsession']; // calculate relative cookie expiration (1 year from now) | ||
400 | setcookie('shaarli_staySignedIn', STAY_SIGNED_IN_TOKEN, $expiration, WEB_PATH); | ||
401 | $_SESSION['expires_on'] = $expiration; // Set session expiration on server-side. | ||
402 | 208 | ||
403 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; | 209 | $cookiedir = ''; |
404 | session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['SERVER_NAME']); // Set session cookie expiration on client side | 210 | if (dirname($_SERVER['SCRIPT_NAME']) != '/') { |
405 | // Note: Never forget the trailing slash on the cookie path! | 211 | // Note: Never forget the trailing slash on the cookie path! |
406 | session_regenerate_id(true); // Send cookie with new expiration date to browser. | 212 | $cookiedir = dirname($_SERVER["SCRIPT_NAME"]) . '/'; |
407 | } | 213 | } |
408 | else // Standard session expiration (=when browser closes) | 214 | |
409 | { | 215 | if (!empty($_POST['longlastingsession'])) { |
410 | $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; | 216 | // Keep the session cookie even after the browser closes |
411 | session_set_cookie_params(0,$cookiedir,$_SERVER['SERVER_NAME']); // 0 means "When browser closes" | 217 | $sessionManager->setStaySignedIn(true); |
412 | session_regenerate_id(true); | 218 | $expirationTime = $sessionManager->extendSession(); |
219 | |||
220 | setcookie( | ||
221 | $loginManager::$STAY_SIGNED_IN_COOKIE, | ||
222 | $loginManager->getStaySignedInToken(), | ||
223 | $expirationTime, | ||
224 | WEB_PATH | ||
225 | ); | ||
226 | |||
227 | } else { | ||
228 | // Standard session expiration (=when browser closes) | ||
229 | $expirationTime = 0; | ||
413 | } | 230 | } |
414 | 231 | ||
232 | // Send cookie with the new expiration date to the browser | ||
233 | session_set_cookie_params($expirationTime, $cookiedir, $_SERVER['SERVER_NAME']); | ||
234 | session_regenerate_id(true); | ||
235 | |||
415 | // Optional redirect after login: | 236 | // Optional redirect after login: |
416 | if (isset($_GET['post'])) { | 237 | if (isset($_GET['post'])) { |
417 | $uri = '?post='. urlencode($_GET['post']); | 238 | $uri = '?post='. urlencode($_GET['post']); |
@@ -437,10 +258,8 @@ if (isset($_POST['login'])) | |||
437 | } | 258 | } |
438 | } | 259 | } |
439 | header('Location: ?'); exit; | 260 | header('Location: ?'); exit; |
440 | } | 261 | } else { |
441 | else | 262 | $loginManager->handleFailedLogin($_SERVER); |
442 | { | ||
443 | ban_loginFailed($conf); | ||
444 | $redir = '&username='. urlencode($_POST['login']); | 263 | $redir = '&username='. urlencode($_POST['login']); |
445 | if (isset($_GET['post'])) { | 264 | if (isset($_GET['post'])) { |
446 | $redir .= '&post=' . urlencode($_GET['post']); | 265 | $redir .= '&post=' . urlencode($_GET['post']); |
@@ -466,15 +285,16 @@ if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are atta | |||
466 | * Gives the last 7 days (which have links). | 285 | * Gives the last 7 days (which have links). |
467 | * This RSS feed cannot be filtered. | 286 | * This RSS feed cannot be filtered. |
468 | * | 287 | * |
469 | * @param ConfigManager $conf Configuration Manager instance. | 288 | * @param ConfigManager $conf Configuration Manager instance |
289 | * @param LoginManager $loginManager LoginManager instance | ||
470 | */ | 290 | */ |
471 | function showDailyRSS($conf) { | 291 | function showDailyRSS($conf, $loginManager) { |
472 | // Cache system | 292 | // Cache system |
473 | $query = $_SERVER['QUERY_STRING']; | 293 | $query = $_SERVER['QUERY_STRING']; |
474 | $cache = new CachedPage( | 294 | $cache = new CachedPage( |
475 | $conf->get('config.PAGE_CACHE'), | 295 | $conf->get('config.PAGE_CACHE'), |
476 | page_url($_SERVER), | 296 | page_url($_SERVER), |
477 | startsWith($query,'do=dailyrss') && !isLoggedIn() | 297 | startsWith($query,'do=dailyrss') && !$loginManager->isLoggedIn() |
478 | ); | 298 | ); |
479 | $cached = $cache->cachedVersion(); | 299 | $cached = $cache->cachedVersion(); |
480 | if (!empty($cached)) { | 300 | if (!empty($cached)) { |
@@ -486,7 +306,7 @@ function showDailyRSS($conf) { | |||
486 | // Read links from database (and filter private links if used it not logged in). | 306 | // Read links from database (and filter private links if used it not logged in). |
487 | $LINKSDB = new LinkDB( | 307 | $LINKSDB = new LinkDB( |
488 | $conf->get('resource.datastore'), | 308 | $conf->get('resource.datastore'), |
489 | isLoggedIn(), | 309 | $loginManager->isLoggedIn(), |
490 | $conf->get('privacy.hide_public_links'), | 310 | $conf->get('privacy.hide_public_links'), |
491 | $conf->get('redirector.url'), | 311 | $conf->get('redirector.url'), |
492 | $conf->get('redirector.encode_url') | 312 | $conf->get('redirector.encode_url') |
@@ -568,9 +388,10 @@ function showDailyRSS($conf) { | |||
568 | * @param PageBuilder $pageBuilder Template engine wrapper. | 388 | * @param PageBuilder $pageBuilder Template engine wrapper. |
569 | * @param LinkDB $LINKSDB LinkDB instance. | 389 | * @param LinkDB $LINKSDB LinkDB instance. |
570 | * @param ConfigManager $conf Configuration Manager instance. | 390 | * @param ConfigManager $conf Configuration Manager instance. |
571 | * @param PluginManager $pluginManager Plugin Manager instane. | 391 | * @param PluginManager $pluginManager Plugin Manager instance. |
392 | * @param LoginManager $loginManager Login Manager instance | ||
572 | */ | 393 | */ |
573 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | 394 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager) |
574 | { | 395 | { |
575 | $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD. | 396 | $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD. |
576 | if (isset($_GET['day'])) { | 397 | if (isset($_GET['day'])) { |
@@ -616,6 +437,20 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
616 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); | 437 | $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); |
617 | } | 438 | } |
618 | 439 | ||
440 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | ||
441 | $data = array( | ||
442 | 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false), | ||
443 | 'linksToDisplay' => $linksToDisplay, | ||
444 | 'day' => $dayDate->getTimestamp(), | ||
445 | 'dayDate' => $dayDate, | ||
446 | 'previousday' => $previousday, | ||
447 | 'nextday' => $nextday, | ||
448 | ); | ||
449 | |||
450 | /* Hook is called before column construction so that plugins don't have | ||
451 | to deal with columns. */ | ||
452 | $pluginManager->executeHooks('render_daily', $data, array('loggedin' => $loginManager->isLoggedIn())); | ||
453 | |||
619 | /* We need to spread the articles on 3 columns. | 454 | /* We need to spread the articles on 3 columns. |
620 | I did not want to use a JavaScript lib like http://masonry.desandro.com/ | 455 | I did not want to use a JavaScript lib like http://masonry.desandro.com/ |
621 | so I manually spread entries with a simple method: I roughly evaluate the | 456 | so I manually spread entries with a simple method: I roughly evaluate the |
@@ -623,7 +458,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
623 | */ | 458 | */ |
624 | $columns = array(array(), array(), array()); // Entries to display, for each column. | 459 | $columns = array(array(), array(), array()); // Entries to display, for each column. |
625 | $fill = array(0, 0, 0); // Rough estimate of columns fill. | 460 | $fill = array(0, 0, 0); // Rough estimate of columns fill. |
626 | foreach($linksToDisplay as $key => $link) { | 461 | foreach($data['linksToDisplay'] as $key => $link) { |
627 | // Roughly estimate length of entry (by counting characters) | 462 | // Roughly estimate length of entry (by counting characters) |
628 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. | 463 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. |
629 | // Description: 836 characters gives roughly 342 pixel height. | 464 | // Description: 836 characters gives roughly 342 pixel height. |
@@ -639,23 +474,13 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
639 | $fill[$index] += $length; | 474 | $fill[$index] += $length; |
640 | } | 475 | } |
641 | 476 | ||
642 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | 477 | $data['cols'] = $columns; |
643 | $data = array( | ||
644 | 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false), | ||
645 | 'linksToDisplay' => $linksToDisplay, | ||
646 | 'cols' => $columns, | ||
647 | 'day' => $dayDate->getTimestamp(), | ||
648 | 'dayDate' => $dayDate, | ||
649 | 'previousday' => $previousday, | ||
650 | 'nextday' => $nextday, | ||
651 | ); | ||
652 | |||
653 | $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn())); | ||
654 | 478 | ||
655 | foreach ($data as $key => $value) { | 479 | foreach ($data as $key => $value) { |
656 | $pageBuilder->assign($key, $value); | 480 | $pageBuilder->assign($key, $value); |
657 | } | 481 | } |
658 | 482 | ||
483 | $pageBuilder->assign('pagetitle', t('Daily') .' - '. $conf->get('general.title', 'Shaarli')); | ||
659 | $pageBuilder->renderPage('daily'); | 484 | $pageBuilder->renderPage('daily'); |
660 | exit; | 485 | exit; |
661 | } | 486 | } |
@@ -668,8 +493,8 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
668 | * @param ConfigManager $conf Configuration Manager instance. | 493 | * @param ConfigManager $conf Configuration Manager instance. |
669 | * @param PluginManager $pluginManager Plugin Manager instance. | 494 | * @param PluginManager $pluginManager Plugin Manager instance. |
670 | */ | 495 | */ |
671 | function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { | 496 | function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) { |
672 | buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager); // Compute list of links to display | 497 | buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager, $loginManager); |
673 | $PAGE->renderPage('linklist'); | 498 | $PAGE->renderPage('linklist'); |
674 | } | 499 | } |
675 | 500 | ||
@@ -681,14 +506,16 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { | |||
681 | * @param LinkDB $LINKSDB | 506 | * @param LinkDB $LINKSDB |
682 | * @param History $history instance | 507 | * @param History $history instance |
683 | * @param SessionManager $sessionManager SessionManager instance | 508 | * @param SessionManager $sessionManager SessionManager instance |
509 | * @param LoginManager $loginManager LoginManager instance | ||
684 | */ | 510 | */ |
685 | function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | 511 | function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $loginManager) |
686 | { | 512 | { |
687 | $updater = new Updater( | 513 | $updater = new Updater( |
688 | read_updates_file($conf->get('resource.updates')), | 514 | read_updates_file($conf->get('resource.updates')), |
689 | $LINKSDB, | 515 | $LINKSDB, |
690 | $conf, | 516 | $conf, |
691 | isLoggedIn() | 517 | $loginManager->isLoggedIn(), |
518 | $_SESSION | ||
692 | ); | 519 | ); |
693 | try { | 520 | try { |
694 | $newUpdates = $updater->update(); | 521 | $newUpdates = $updater->update(); |
@@ -703,18 +530,18 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
703 | die($e->getMessage()); | 530 | die($e->getMessage()); |
704 | } | 531 | } |
705 | 532 | ||
706 | $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken()); | 533 | $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); |
707 | $PAGE->assign('linkcount', count($LINKSDB)); | 534 | $PAGE->assign('linkcount', count($LINKSDB)); |
708 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); | 535 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); |
709 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); | 536 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); |
710 | 537 | ||
711 | // Determine which page will be rendered. | 538 | // Determine which page will be rendered. |
712 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; | 539 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; |
713 | $targetPage = Router::findPage($query, $_GET, isLoggedIn()); | 540 | $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn()); |
714 | 541 | ||
715 | if ( | 542 | if ( |
716 | // if the user isn't logged in | 543 | // if the user isn't logged in |
717 | !isLoggedIn() && | 544 | !$loginManager->isLoggedIn() && |
718 | // and Shaarli doesn't have public content... | 545 | // and Shaarli doesn't have public content... |
719 | $conf->get('privacy.hide_public_links') && | 546 | $conf->get('privacy.hide_public_links') && |
720 | // and is configured to enforce the login | 547 | // and is configured to enforce the login |
@@ -742,7 +569,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
742 | $pluginManager->executeHooks('render_' . $name, $plugin_data, | 569 | $pluginManager->executeHooks('render_' . $name, $plugin_data, |
743 | array( | 570 | array( |
744 | 'target' => $targetPage, | 571 | 'target' => $targetPage, |
745 | 'loggedin' => isLoggedIn() | 572 | 'loggedin' => $loginManager->isLoggedIn() |
746 | ) | 573 | ) |
747 | ); | 574 | ); |
748 | $PAGE->assign('plugins_' . $name, $plugin_data); | 575 | $PAGE->assign('plugins_' . $name, $plugin_data); |
@@ -758,6 +585,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
758 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); | 585 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); |
759 | // add default state of the 'remember me' checkbox | 586 | // add default state of the 'remember me' checkbox |
760 | $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); | 587 | $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); |
588 | $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER)); | ||
589 | $PAGE->assign('pagetitle', t('Login') .' - '. $conf->get('general.title', 'Shaarli')); | ||
761 | $PAGE->renderPage('loginform'); | 590 | $PAGE->renderPage('loginform'); |
762 | exit; | 591 | exit; |
763 | } | 592 | } |
@@ -765,7 +594,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
765 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) | 594 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) |
766 | { | 595 | { |
767 | invalidateCaches($conf->get('resource.page_cache')); | 596 | invalidateCaches($conf->get('resource.page_cache')); |
768 | logout(); | 597 | $sessionManager->logout(); |
598 | setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, WEB_PATH); | ||
769 | header('Location: ?'); | 599 | header('Location: ?'); |
770 | exit; | 600 | exit; |
771 | } | 601 | } |
@@ -773,31 +603,36 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
773 | // -------- Picture wall | 603 | // -------- Picture wall |
774 | if ($targetPage == Router::$PAGE_PICWALL) | 604 | if ($targetPage == Router::$PAGE_PICWALL) |
775 | { | 605 | { |
606 | $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); | ||
607 | if (! $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) { | ||
608 | $PAGE->assign('linksToDisplay', []); | ||
609 | $PAGE->renderPage('picwall'); | ||
610 | exit; | ||
611 | } | ||
612 | |||
776 | // Optionally filter the results: | 613 | // Optionally filter the results: |
777 | $links = $LINKSDB->filterSearch($_GET); | 614 | $links = $LINKSDB->filterSearch($_GET); |
778 | $linksToDisplay = array(); | 615 | $linksToDisplay = array(); |
779 | 616 | ||
780 | // Get only links which have a thumbnail. | 617 | // Get only links which have a thumbnail. |
781 | foreach($links as $link) | 618 | // Note: we do not retrieve thumbnails here, the request is too heavy. |
619 | foreach($links as $key => $link) | ||
782 | { | 620 | { |
783 | $permalink='?'.$link['shorturl']; | 621 | if (isset($link['thumbnail']) && $link['thumbnail'] !== false) { |
784 | $thumb=lazyThumbnail($conf, $link['url'],$permalink); | 622 | $linksToDisplay[] = $link; // Add to array. |
785 | if ($thumb!='') // Only output links which have a thumbnail. | ||
786 | { | ||
787 | $link['thumbnail']=$thumb; // Thumbnail HTML code. | ||
788 | $linksToDisplay[]=$link; // Add to array. | ||
789 | } | 623 | } |
790 | } | 624 | } |
791 | 625 | ||
792 | $data = array( | 626 | $data = array( |
793 | 'linksToDisplay' => $linksToDisplay, | 627 | 'linksToDisplay' => $linksToDisplay, |
794 | ); | 628 | ); |
795 | $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn())); | 629 | $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => $loginManager->isLoggedIn())); |
796 | 630 | ||
797 | foreach ($data as $key => $value) { | 631 | foreach ($data as $key => $value) { |
798 | $PAGE->assign($key, $value); | 632 | $PAGE->assign($key, $value); |
799 | } | 633 | } |
800 | 634 | ||
635 | |||
801 | $PAGE->renderPage('picwall'); | 636 | $PAGE->renderPage('picwall'); |
802 | exit; | 637 | exit; |
803 | } | 638 | } |
@@ -805,7 +640,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
805 | // -------- Tag cloud | 640 | // -------- Tag cloud |
806 | if ($targetPage == Router::$PAGE_TAGCLOUD) | 641 | if ($targetPage == Router::$PAGE_TAGCLOUD) |
807 | { | 642 | { |
808 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; | 643 | $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; |
809 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; | 644 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; |
810 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); | 645 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); |
811 | 646 | ||
@@ -833,16 +668,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
833 | ); | 668 | ); |
834 | } | 669 | } |
835 | 670 | ||
671 | $searchTags = implode(' ', escape($filteringTags)); | ||
836 | $data = array( | 672 | $data = array( |
837 | 'search_tags' => implode(' ', escape($filteringTags)), | 673 | 'search_tags' => $searchTags, |
838 | 'tags' => $tagList, | 674 | 'tags' => $tagList, |
839 | ); | 675 | ); |
840 | $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); | 676 | $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => $loginManager->isLoggedIn())); |
841 | 677 | ||
842 | foreach ($data as $key => $value) { | 678 | foreach ($data as $key => $value) { |
843 | $PAGE->assign($key, $value); | 679 | $PAGE->assign($key, $value); |
844 | } | 680 | } |
845 | 681 | ||
682 | $searchTags = ! empty($searchTags) ? $searchTags .' - ' : ''; | ||
683 | $PAGE->assign('pagetitle', $searchTags. t('Tag cloud') .' - '. $conf->get('general.title', 'Shaarli')); | ||
846 | $PAGE->renderPage('tag.cloud'); | 684 | $PAGE->renderPage('tag.cloud'); |
847 | exit; | 685 | exit; |
848 | } | 686 | } |
@@ -850,7 +688,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
850 | // -------- Tag list | 688 | // -------- Tag list |
851 | if ($targetPage == Router::$PAGE_TAGLIST) | 689 | if ($targetPage == Router::$PAGE_TAGLIST) |
852 | { | 690 | { |
853 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; | 691 | $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; |
854 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; | 692 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; |
855 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); | 693 | $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); |
856 | foreach ($filteringTags as $tag) { | 694 | foreach ($filteringTags as $tag) { |
@@ -863,23 +701,26 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
863 | alphabetical_sort($tags, false, true); | 701 | alphabetical_sort($tags, false, true); |
864 | } | 702 | } |
865 | 703 | ||
704 | $searchTags = implode(' ', escape($filteringTags)); | ||
866 | $data = [ | 705 | $data = [ |
867 | 'search_tags' => implode(' ', escape($filteringTags)), | 706 | 'search_tags' => $searchTags, |
868 | 'tags' => $tags, | 707 | 'tags' => $tags, |
869 | ]; | 708 | ]; |
870 | $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => isLoggedIn()]); | 709 | $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => $loginManager->isLoggedIn()]); |
871 | 710 | ||
872 | foreach ($data as $key => $value) { | 711 | foreach ($data as $key => $value) { |
873 | $PAGE->assign($key, $value); | 712 | $PAGE->assign($key, $value); |
874 | } | 713 | } |
875 | 714 | ||
715 | $searchTags = ! empty($searchTags) ? $searchTags .' - ' : ''; | ||
716 | $PAGE->assign('pagetitle', $searchTags . t('Tag list') .' - '. $conf->get('general.title', 'Shaarli')); | ||
876 | $PAGE->renderPage('tag.list'); | 717 | $PAGE->renderPage('tag.list'); |
877 | exit; | 718 | exit; |
878 | } | 719 | } |
879 | 720 | ||
880 | // Daily page. | 721 | // Daily page. |
881 | if ($targetPage == Router::$PAGE_DAILY) { | 722 | if ($targetPage == Router::$PAGE_DAILY) { |
882 | showDaily($PAGE, $LINKSDB, $conf, $pluginManager); | 723 | showDaily($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); |
883 | } | 724 | } |
884 | 725 | ||
885 | // ATOM and RSS feed. | 726 | // ATOM and RSS feed. |
@@ -892,7 +733,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
892 | $cache = new CachedPage( | 733 | $cache = new CachedPage( |
893 | $conf->get('resource.page_cache'), | 734 | $conf->get('resource.page_cache'), |
894 | page_url($_SERVER), | 735 | page_url($_SERVER), |
895 | startsWith($query,'do='. $targetPage) && !isLoggedIn() | 736 | startsWith($query,'do='. $targetPage) && !$loginManager->isLoggedIn() |
896 | ); | 737 | ); |
897 | $cached = $cache->cachedVersion(); | 738 | $cached = $cache->cachedVersion(); |
898 | if (!empty($cached)) { | 739 | if (!empty($cached)) { |
@@ -901,15 +742,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
901 | } | 742 | } |
902 | 743 | ||
903 | // Generate data. | 744 | // Generate data. |
904 | $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, isLoggedIn()); | 745 | $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, $loginManager->isLoggedIn()); |
905 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); | 746 | $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); |
906 | $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); | 747 | $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !$loginManager->isLoggedIn()); |
907 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); | 748 | $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); |
908 | $data = $feedGenerator->buildData(); | 749 | $data = $feedGenerator->buildData(); |
909 | 750 | ||
910 | // Process plugin hook. | 751 | // Process plugin hook. |
911 | $pluginManager->executeHooks('render_feed', $data, array( | 752 | $pluginManager->executeHooks('render_feed', $data, array( |
912 | 'loggedin' => isLoggedIn(), | 753 | 'loggedin' => $loginManager->isLoggedIn(), |
913 | 'target' => $targetPage, | 754 | 'target' => $targetPage, |
914 | )); | 755 | )); |
915 | 756 | ||
@@ -959,7 +800,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
959 | if (empty($params['searchtags'])) { | 800 | if (empty($params['searchtags'])) { |
960 | $params['searchtags'] = trim($_GET['addtag']); | 801 | $params['searchtags'] = trim($_GET['addtag']); |
961 | } | 802 | } |
962 | else if ($addtag) { | 803 | elseif ($addtag) { |
963 | $params['searchtags'] = trim($params['searchtags']).' '.trim($_GET['addtag']); | 804 | $params['searchtags'] = trim($params['searchtags']).' '.trim($_GET['addtag']); |
964 | } | 805 | } |
965 | 806 | ||
@@ -1016,15 +857,26 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1016 | } | 857 | } |
1017 | 858 | ||
1018 | // -------- User wants to see only private links (toggle) | 859 | // -------- User wants to see only private links (toggle) |
1019 | if (isset($_GET['privateonly'])) { | 860 | if (isset($_GET['visibility'])) { |
1020 | if (empty($_SESSION['privateonly'])) { | 861 | if ($_GET['visibility'] === 'private') { |
1021 | $_SESSION['privateonly'] = 1; // See only private links | 862 | // Visibility not set or not already private, set private, otherwise reset it |
1022 | } else { | 863 | if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'private') { |
1023 | unset($_SESSION['privateonly']); // See all links | 864 | // See only private links |
865 | $_SESSION['visibility'] = 'private'; | ||
866 | } else { | ||
867 | unset($_SESSION['visibility']); | ||
868 | } | ||
869 | } elseif ($_GET['visibility'] === 'public') { | ||
870 | if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'public') { | ||
871 | // See only public links | ||
872 | $_SESSION['visibility'] = 'public'; | ||
873 | } else { | ||
874 | unset($_SESSION['visibility']); | ||
875 | } | ||
1024 | } | 876 | } |
1025 | 877 | ||
1026 | if (! empty($_SERVER['HTTP_REFERER'])) { | 878 | if (! empty($_SERVER['HTTP_REFERER'])) { |
1027 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('privateonly')); | 879 | $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('visibility')); |
1028 | } else { | 880 | } else { |
1029 | $location = '?'; | 881 | $location = '?'; |
1030 | } | 882 | } |
@@ -1046,7 +898,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1046 | } | 898 | } |
1047 | 899 | ||
1048 | // -------- Handle other actions allowed for non-logged in users: | 900 | // -------- Handle other actions allowed for non-logged in users: |
1049 | if (!isLoggedIn()) | 901 | if (!$loginManager->isLoggedIn()) |
1050 | { | 902 | { |
1051 | // User tries to post new link but is not logged in: | 903 | // User tries to post new link but is not logged in: |
1052 | // Show login screen, then redirect to ?post=... | 904 | // Show login screen, then redirect to ?post=... |
@@ -1062,7 +914,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1062 | exit; | 914 | exit; |
1063 | } | 915 | } |
1064 | 916 | ||
1065 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); | 917 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); |
1066 | if (isset($_GET['edit_link'])) { | 918 | if (isset($_GET['edit_link'])) { |
1067 | header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); | 919 | header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); |
1068 | exit; | 920 | exit; |
@@ -1086,6 +938,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1086 | $PAGE->assign($key, $value); | 938 | $PAGE->assign($key, $value); |
1087 | } | 939 | } |
1088 | 940 | ||
941 | $PAGE->assign('pagetitle', t('Tools') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1089 | $PAGE->renderPage('tools'); | 942 | $PAGE->renderPage('tools'); |
1090 | exit; | 943 | exit; |
1091 | } | 944 | } |
@@ -1112,7 +965,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1112 | $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); | 965 | $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); |
1113 | $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt'))); | 966 | $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt'))); |
1114 | try { | 967 | try { |
1115 | $conf->write(isLoggedIn()); | 968 | $conf->write($loginManager->isLoggedIn()); |
1116 | } | 969 | } |
1117 | catch(Exception $e) { | 970 | catch(Exception $e) { |
1118 | error_log( | 971 | error_log( |
@@ -1129,6 +982,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1129 | } | 982 | } |
1130 | else // show the change password form. | 983 | else // show the change password form. |
1131 | { | 984 | { |
985 | $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1132 | $PAGE->renderPage('changepassword'); | 986 | $PAGE->renderPage('changepassword'); |
1133 | exit; | 987 | exit; |
1134 | } | 988 | } |
@@ -1152,7 +1006,6 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1152 | $conf->set('general.title', escape($_POST['title'])); | 1006 | $conf->set('general.title', escape($_POST['title'])); |
1153 | $conf->set('general.header_link', escape($_POST['titleLink'])); | 1007 | $conf->set('general.header_link', escape($_POST['titleLink'])); |
1154 | $conf->set('resource.theme', escape($_POST['theme'])); | 1008 | $conf->set('resource.theme', escape($_POST['theme'])); |
1155 | $conf->set('redirector.url', escape($_POST['redirector'])); | ||
1156 | $conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection'])); | 1009 | $conf->set('security.session_protection_disabled', !empty($_POST['disablesessionprotection'])); |
1157 | $conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault'])); | 1010 | $conf->set('privacy.default_private_links', !empty($_POST['privateLinkByDefault'])); |
1158 | $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); | 1011 | $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); |
@@ -1162,8 +1015,18 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1162 | $conf->set('api.secret', escape($_POST['apiSecret'])); | 1015 | $conf->set('api.secret', escape($_POST['apiSecret'])); |
1163 | $conf->set('translation.language', escape($_POST['language'])); | 1016 | $conf->set('translation.language', escape($_POST['language'])); |
1164 | 1017 | ||
1018 | $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE; | ||
1019 | if ($thumbnailsMode !== Thumbnailer::MODE_NONE | ||
1020 | && $thumbnailsMode !== $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) | ||
1021 | ) { | ||
1022 | $_SESSION['warnings'][] = t( | ||
1023 | 'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.' | ||
1024 | ); | ||
1025 | } | ||
1026 | $conf->set('thumbnails.mode', $thumbnailsMode); | ||
1027 | |||
1165 | try { | 1028 | try { |
1166 | $conf->write(isLoggedIn()); | 1029 | $conf->write($loginManager->isLoggedIn()); |
1167 | $history->updateSettings(); | 1030 | $history->updateSettings(); |
1168 | invalidateCaches($conf->get('resource.page_cache')); | 1031 | invalidateCaches($conf->get('resource.page_cache')); |
1169 | } | 1032 | } |
@@ -1185,7 +1048,6 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1185 | $PAGE->assign('title', $conf->get('general.title')); | 1048 | $PAGE->assign('title', $conf->get('general.title')); |
1186 | $PAGE->assign('theme', $conf->get('resource.theme')); | 1049 | $PAGE->assign('theme', $conf->get('resource.theme')); |
1187 | $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); | 1050 | $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); |
1188 | $PAGE->assign('redirector', $conf->get('redirector.url')); | ||
1189 | list($continents, $cities) = generateTimeZoneData( | 1051 | list($continents, $cities) = generateTimeZoneData( |
1190 | timezone_identifiers_list(), | 1052 | timezone_identifiers_list(), |
1191 | $conf->get('general.timezone') | 1053 | $conf->get('general.timezone') |
@@ -1201,6 +1063,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1201 | $PAGE->assign('api_secret', $conf->get('api.secret')); | 1063 | $PAGE->assign('api_secret', $conf->get('api.secret')); |
1202 | $PAGE->assign('languages', Languages::getAvailableLanguages()); | 1064 | $PAGE->assign('languages', Languages::getAvailableLanguages()); |
1203 | $PAGE->assign('language', $conf->get('translation.language')); | 1065 | $PAGE->assign('language', $conf->get('translation.language')); |
1066 | $PAGE->assign('gd_enabled', extension_loaded('gd')); | ||
1067 | $PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); | ||
1068 | $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1204 | $PAGE->renderPage('configure'); | 1069 | $PAGE->renderPage('configure'); |
1205 | exit; | 1070 | exit; |
1206 | } | 1071 | } |
@@ -1211,6 +1076,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1211 | { | 1076 | { |
1212 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { | 1077 | if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { |
1213 | $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : ''); | 1078 | $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : ''); |
1079 | $PAGE->assign('pagetitle', t('Manage tags') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1214 | $PAGE->renderPage('changetag'); | 1080 | $PAGE->renderPage('changetag'); |
1215 | exit; | 1081 | exit; |
1216 | } | 1082 | } |
@@ -1237,6 +1103,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1237 | // -------- User wants to add a link without using the bookmarklet: Show form. | 1103 | // -------- User wants to add a link without using the bookmarklet: Show form. |
1238 | if ($targetPage == Router::$PAGE_ADDLINK) | 1104 | if ($targetPage == Router::$PAGE_ADDLINK) |
1239 | { | 1105 | { |
1106 | $PAGE->assign('pagetitle', t('Shaare a new link') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1240 | $PAGE->renderPage('addlink'); | 1107 | $PAGE->renderPage('addlink'); |
1241 | exit; | 1108 | exit; |
1242 | } | 1109 | } |
@@ -1254,7 +1121,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1254 | // Linkdate is kept here to: | 1121 | // Linkdate is kept here to: |
1255 | // - use the same permalink for notes as they're displayed when creating them | 1122 | // - use the same permalink for notes as they're displayed when creating them |
1256 | // - let users hack creation date of their posts | 1123 | // - let users hack creation date of their posts |
1257 | // See: https://shaarli.readthedocs.io/en/master/Various-hacks/#changing-the-timestamp-for-a-shaare | 1124 | // See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare |
1258 | $linkdate = escape($_POST['lf_linkdate']); | 1125 | $linkdate = escape($_POST['lf_linkdate']); |
1259 | if (isset($LINKSDB[$id])) { | 1126 | if (isset($LINKSDB[$id])) { |
1260 | // Edit | 1127 | // Edit |
@@ -1299,6 +1166,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1299 | $link['title'] = $link['url']; | 1166 | $link['title'] = $link['url']; |
1300 | } | 1167 | } |
1301 | 1168 | ||
1169 | if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) { | ||
1170 | $thumbnailer = new Thumbnailer($conf); | ||
1171 | $link['thumbnail'] = $thumbnailer->get($url); | ||
1172 | } | ||
1173 | |||
1302 | $pluginManager->executeHooks('save_link', $link); | 1174 | $pluginManager->executeHooks('save_link', $link); |
1303 | 1175 | ||
1304 | $LINKSDB[$id] = $link; | 1176 | $LINKSDB[$id] = $link; |
@@ -1406,6 +1278,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1406 | $PAGE->assign($key, $value); | 1278 | $PAGE->assign($key, $value); |
1407 | } | 1279 | } |
1408 | 1280 | ||
1281 | $PAGE->assign('pagetitle', t('Edit') .' '. t('Shaare') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1409 | $PAGE->renderPage('editlink'); | 1282 | $PAGE->renderPage('editlink'); |
1410 | exit; | 1283 | exit; |
1411 | } | 1284 | } |
@@ -1431,7 +1304,12 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1431 | if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { | 1304 | if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { |
1432 | // Short timeout to keep the application responsive | 1305 | // Short timeout to keep the application responsive |
1433 | // The callback will fill $charset and $title with data from the downloaded page. | 1306 | // The callback will fill $charset and $title with data from the downloaded page. |
1434 | get_http_response($url, 25, 4194304, get_curl_download_callback($charset, $title)); | 1307 | get_http_response( |
1308 | $url, | ||
1309 | $conf->get('general.download_timeout', 30), | ||
1310 | $conf->get('general.download_max_size', 4194304), | ||
1311 | get_curl_download_callback($charset, $title) | ||
1312 | ); | ||
1435 | if (! empty($title) && strtolower($charset) != 'utf-8') { | 1313 | if (! empty($title) && strtolower($charset) != 'utf-8') { |
1436 | $title = mb_convert_encoding($title, 'utf-8', $charset); | 1314 | $title = mb_convert_encoding($title, 'utf-8', $charset); |
1437 | } | 1315 | } |
@@ -1470,6 +1348,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1470 | $PAGE->assign($key, $value); | 1348 | $PAGE->assign($key, $value); |
1471 | } | 1349 | } |
1472 | 1350 | ||
1351 | $PAGE->assign('pagetitle', t('Shaare') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1473 | $PAGE->renderPage('editlink'); | 1352 | $PAGE->renderPage('editlink'); |
1474 | exit; | 1353 | exit; |
1475 | } | 1354 | } |
@@ -1478,6 +1357,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1478 | // Export links as a Netscape Bookmarks file | 1357 | // Export links as a Netscape Bookmarks file |
1479 | 1358 | ||
1480 | if (empty($_GET['selection'])) { | 1359 | if (empty($_GET['selection'])) { |
1360 | $PAGE->assign('pagetitle', t('Export') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1481 | $PAGE->renderPage('export'); | 1361 | $PAGE->renderPage('export'); |
1482 | exit; | 1362 | exit; |
1483 | } | 1363 | } |
@@ -1539,6 +1419,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1539 | true | 1419 | true |
1540 | ) | 1420 | ) |
1541 | ); | 1421 | ); |
1422 | $PAGE->assign('pagetitle', t('Import') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1542 | $PAGE->renderPage('import'); | 1423 | $PAGE->renderPage('import'); |
1543 | exit; | 1424 | exit; |
1544 | } | 1425 | } |
@@ -1587,6 +1468,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1587 | 1468 | ||
1588 | $PAGE->assign('enabledPlugins', $enabledPlugins); | 1469 | $PAGE->assign('enabledPlugins', $enabledPlugins); |
1589 | $PAGE->assign('disabledPlugins', $disabledPlugins); | 1470 | $PAGE->assign('disabledPlugins', $disabledPlugins); |
1471 | $PAGE->assign('pagetitle', t('Plugin administration') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1590 | $PAGE->renderPage('pluginsadmin'); | 1472 | $PAGE->renderPage('pluginsadmin'); |
1591 | exit; | 1473 | exit; |
1592 | } | 1474 | } |
@@ -1603,7 +1485,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1603 | else { | 1485 | else { |
1604 | $conf->set('general.enabled_plugins', save_plugin_config($_POST)); | 1486 | $conf->set('general.enabled_plugins', save_plugin_config($_POST)); |
1605 | } | 1487 | } |
1606 | $conf->write(isLoggedIn()); | 1488 | $conf->write($loginManager->isLoggedIn()); |
1607 | $history->updateSettings(); | 1489 | $history->updateSettings(); |
1608 | } | 1490 | } |
1609 | catch (Exception $e) { | 1491 | catch (Exception $e) { |
@@ -1627,8 +1509,45 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1627 | exit; | 1509 | exit; |
1628 | } | 1510 | } |
1629 | 1511 | ||
1512 | // -------- Thumbnails Update | ||
1513 | if ($targetPage == Router::$PAGE_THUMBS_UPDATE) { | ||
1514 | $ids = []; | ||
1515 | foreach ($LINKSDB as $link) { | ||
1516 | // A note or not HTTP(S) | ||
1517 | if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) { | ||
1518 | continue; | ||
1519 | } | ||
1520 | $ids[] = $link['id']; | ||
1521 | } | ||
1522 | $PAGE->assign('ids', $ids); | ||
1523 | $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1524 | $PAGE->renderPage('thumbnails'); | ||
1525 | exit; | ||
1526 | } | ||
1527 | |||
1528 | // -------- Single Thumbnail Update | ||
1529 | if ($targetPage == Router::$AJAX_THUMB_UPDATE) { | ||
1530 | if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) { | ||
1531 | http_response_code(400); | ||
1532 | exit; | ||
1533 | } | ||
1534 | $id = (int) $_POST['id']; | ||
1535 | if (empty($LINKSDB[$id])) { | ||
1536 | http_response_code(404); | ||
1537 | exit; | ||
1538 | } | ||
1539 | $thumbnailer = new Thumbnailer($conf); | ||
1540 | $link = $LINKSDB[$id]; | ||
1541 | $link['thumbnail'] = $thumbnailer->get($link['url']); | ||
1542 | $LINKSDB[$id] = $link; | ||
1543 | $LINKSDB->save($conf->get('resource.page_cache')); | ||
1544 | |||
1545 | echo json_encode($link); | ||
1546 | exit; | ||
1547 | } | ||
1548 | |||
1630 | // -------- Otherwise, simply display search form and links: | 1549 | // -------- Otherwise, simply display search form and links: |
1631 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); | 1550 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); |
1632 | exit; | 1551 | exit; |
1633 | } | 1552 | } |
1634 | 1553 | ||
@@ -1640,8 +1559,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager) | |||
1640 | * @param LinkDB $LINKSDB LinkDB instance. | 1559 | * @param LinkDB $LINKSDB LinkDB instance. |
1641 | * @param ConfigManager $conf Configuration Manager instance. | 1560 | * @param ConfigManager $conf Configuration Manager instance. |
1642 | * @param PluginManager $pluginManager Plugin Manager instance. | 1561 | * @param PluginManager $pluginManager Plugin Manager instance. |
1562 | * @param LoginManager $loginManager LoginManager instance | ||
1643 | */ | 1563 | */ |
1644 | function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | 1564 | function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) |
1645 | { | 1565 | { |
1646 | // Used in templates | 1566 | // Used in templates |
1647 | if (isset($_GET['searchtags'])) { | 1567 | if (isset($_GET['searchtags'])) { |
@@ -1666,7 +1586,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1666 | } | 1586 | } |
1667 | } else { | 1587 | } else { |
1668 | // Filter links according search parameters. | 1588 | // Filter links according search parameters. |
1669 | $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; | 1589 | $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; |
1670 | $request = [ | 1590 | $request = [ |
1671 | 'searchtags' => $searchtags, | 1591 | 'searchtags' => $searchtags, |
1672 | 'searchterm' => $searchterm, | 1592 | 'searchterm' => $searchterm, |
@@ -1680,8 +1600,6 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1680 | $keys[] = $key; | 1600 | $keys[] = $key; |
1681 | } | 1601 | } |
1682 | 1602 | ||
1683 | |||
1684 | |||
1685 | // Select articles according to paging. | 1603 | // Select articles according to paging. |
1686 | $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); | 1604 | $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']); |
1687 | $pagecount = $pagecount == 0 ? 1 : $pagecount; | 1605 | $pagecount = $pagecount == 0 ? 1 : $pagecount; |
@@ -1691,6 +1609,12 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1691 | // Start index. | 1609 | // Start index. |
1692 | $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; | 1610 | $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; |
1693 | $end = $i + $_SESSION['LINKS_PER_PAGE']; | 1611 | $end = $i + $_SESSION['LINKS_PER_PAGE']; |
1612 | |||
1613 | $thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE; | ||
1614 | if ($thumbnailsEnabled) { | ||
1615 | $thumbnailer = new Thumbnailer($conf); | ||
1616 | } | ||
1617 | |||
1694 | $linkDisp = array(); | 1618 | $linkDisp = array(); |
1695 | while ($i<$end && $i<count($keys)) | 1619 | while ($i<$end && $i<count($keys)) |
1696 | { | 1620 | { |
@@ -1711,9 +1635,21 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1711 | $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); | 1635 | $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); |
1712 | uasort($taglist, 'strcasecmp'); | 1636 | uasort($taglist, 'strcasecmp'); |
1713 | $link['taglist'] = $taglist; | 1637 | $link['taglist'] = $taglist; |
1638 | |||
1639 | // Thumbnails enabled, not a note, | ||
1640 | // and (never retrieved yet or no valid cache file) | ||
1641 | if ($thumbnailsEnabled && $link['url'][0] != '?' | ||
1642 | && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail']))) | ||
1643 | ) { | ||
1644 | $elem = $LINKSDB[$keys[$i]]; | ||
1645 | $elem['thumbnail'] = $thumbnailer->get($link['url']); | ||
1646 | $LINKSDB[$keys[$i]] = $elem; | ||
1647 | $updateDB = true; | ||
1648 | $link['thumbnail'] = $elem['thumbnail']; | ||
1649 | } | ||
1650 | |||
1714 | // Check for both signs of a note: starting with ? and 7 chars long. | 1651 | // Check for both signs of a note: starting with ? and 7 chars long. |
1715 | if ($link['url'][0] === '?' && | 1652 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { |
1716 | strlen($link['url']) === 7) { | ||
1717 | $link['url'] = index_url($_SERVER) . $link['url']; | 1653 | $link['url'] = index_url($_SERVER) . $link['url']; |
1718 | } | 1654 | } |
1719 | 1655 | ||
@@ -1721,6 +1657,11 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1721 | $i++; | 1657 | $i++; |
1722 | } | 1658 | } |
1723 | 1659 | ||
1660 | // If we retrieved new thumbnails, we update the database. | ||
1661 | if (!empty($updateDB)) { | ||
1662 | $LINKSDB->save($conf->get('resource.page_cache')); | ||
1663 | } | ||
1664 | |||
1724 | // Compute paging navigation | 1665 | // Compute paging navigation |
1725 | $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); | 1666 | $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); |
1726 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); | 1667 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); |
@@ -1742,7 +1683,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1742 | 'result_count' => count($linksToDisplay), | 1683 | 'result_count' => count($linksToDisplay), |
1743 | 'search_term' => $searchterm, | 1684 | 'search_term' => $searchterm, |
1744 | 'search_tags' => $searchtags, | 1685 | 'search_tags' => $searchtags, |
1745 | 'visibility' => ! empty($_SESSION['privateonly']) ? 'private' : '', | 1686 | 'visibility' => ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '', |
1746 | 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. | 1687 | 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. |
1747 | 'links' => $linkDisp, | 1688 | 'links' => $linkDisp, |
1748 | ); | 1689 | ); |
@@ -1750,9 +1691,19 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1750 | // If there is only a single link, we change on-the-fly the title of the page. | 1691 | // If there is only a single link, we change on-the-fly the title of the page. |
1751 | if (count($linksToDisplay) == 1) { | 1692 | if (count($linksToDisplay) == 1) { |
1752 | $data['pagetitle'] = $linksToDisplay[$keys[0]]['title'] .' - '. $conf->get('general.title'); | 1693 | $data['pagetitle'] = $linksToDisplay[$keys[0]]['title'] .' - '. $conf->get('general.title'); |
1694 | } elseif (! empty($searchterm) || ! empty($searchtags)) { | ||
1695 | $data['pagetitle'] = t('Search: '); | ||
1696 | $data['pagetitle'] .= ! empty($searchterm) ? $searchterm .' ' : ''; | ||
1697 | $bracketWrap = function ($tag) { | ||
1698 | return '['. $tag .']'; | ||
1699 | }; | ||
1700 | $data['pagetitle'] .= ! empty($searchtags) | ||
1701 | ? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchtags))).' ' | ||
1702 | : ''; | ||
1703 | $data['pagetitle'] .= '- '. $conf->get('general.title'); | ||
1753 | } | 1704 | } |
1754 | 1705 | ||
1755 | $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn())); | 1706 | $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => $loginManager->isLoggedIn())); |
1756 | 1707 | ||
1757 | foreach ($data as $key => $value) { | 1708 | foreach ($data as $key => $value) { |
1758 | $PAGE->assign($key, $value); | 1709 | $PAGE->assign($key, $value); |
@@ -1762,201 +1713,14 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) | |||
1762 | } | 1713 | } |
1763 | 1714 | ||
1764 | /** | 1715 | /** |
1765 | * Compute the thumbnail for a link. | ||
1766 | * | ||
1767 | * With a link to the original URL. | ||
1768 | * Understands various services (youtube.com...) | ||
1769 | * Input: $url = URL for which the thumbnail must be found. | ||
1770 | * $href = if provided, this URL will be followed instead of $url | ||
1771 | * Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) | ||
1772 | * Some of them may be missing. | ||
1773 | * Return an empty array if no thumbnail available. | ||
1774 | * | ||
1775 | * @param ConfigManager $conf Configuration Manager instance. | ||
1776 | * @param string $url | ||
1777 | * @param string|bool $href | ||
1778 | * | ||
1779 | * @return array | ||
1780 | */ | ||
1781 | function computeThumbnail($conf, $url, $href = false) | ||
1782 | { | ||
1783 | if (!$conf->get('thumbnail.enable_thumbnails')) return array(); | ||
1784 | if ($href==false) $href=$url; | ||
1785 | |||
1786 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. | ||
1787 | // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) | ||
1788 | // ^^^^^^^^^^^ ^^^^^^^^^^^ | ||
1789 | $domain = parse_url($url,PHP_URL_HOST); | ||
1790 | if ($domain=='youtube.com' || $domain=='www.youtube.com') | ||
1791 | { | ||
1792 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail | ||
1793 | if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg', | ||
1794 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | ||
1795 | } | ||
1796 | if ($domain=='youtu.be') // Youtube short links | ||
1797 | { | ||
1798 | $path = parse_url($url,PHP_URL_PATH); | ||
1799 | return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg', | ||
1800 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | ||
1801 | } | ||
1802 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting | ||
1803 | { | ||
1804 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename. | ||
1805 | if (!empty($params) && !empty($params['img'])) return array('src'=>'http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']), | ||
1806 | 'href'=>$href,'style'=>'max-width:120px; max-height:150px','alt'=>'pix.toile-libre.org thumbnail'); | ||
1807 | } | ||
1808 | |||
1809 | if ($domain=='imgur.com') | ||
1810 | { | ||
1811 | $path = parse_url($url,PHP_URL_PATH); | ||
1812 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. | ||
1813 | if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg', | ||
1814 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1815 | if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg', | ||
1816 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1817 | |||
1818 | if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg', | ||
1819 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1820 | } | ||
1821 | if ($domain=='i.imgur.com') | ||
1822 | { | ||
1823 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); | ||
1824 | if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg', | ||
1825 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1826 | } | ||
1827 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') | ||
1828 | { | ||
1829 | if (strpos($url,'dailymotion.com/video/')!==false) | ||
1830 | { | ||
1831 | $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url); | ||
1832 | return array('src'=>$thumburl, | ||
1833 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'DailyMotion thumbnail'); | ||
1834 | } | ||
1835 | } | ||
1836 | if (endsWith($domain,'.imageshack.us')) | ||
1837 | { | ||
1838 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); | ||
1839 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') | ||
1840 | { | ||
1841 | $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext; | ||
1842 | return array('src'=>$thumburl, | ||
1843 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'imageshack.us thumbnail'); | ||
1844 | } | ||
1845 | } | ||
1846 | |||
1847 | // Some other hosts are SLOW AS HELL and usually require an extra HTTP request to get the thumbnail URL. | ||
1848 | // So we deport the thumbnail generation in order not to slow down page generation | ||
1849 | // (and we also cache the thumbnail) | ||
1850 | |||
1851 | if (! $conf->get('thumbnail.enable_localcache')) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache. | ||
1852 | |||
1853 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') | ||
1854 | || $domain=='vimeo.com' | ||
1855 | || $domain=='ted.com' || endsWith($domain,'.ted.com') | ||
1856 | || $domain=='xkcd.com' || endsWith($domain,'.xkcd.com') | ||
1857 | ) | ||
1858 | { | ||
1859 | if ($domain=='vimeo.com') | ||
1860 | { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric) | ||
1861 | $path = parse_url($url,PHP_URL_PATH); | ||
1862 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. | ||
1863 | } | ||
1864 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | ||
1865 | { // Make sure this URL points to a single comic (/xxx... where xxx is numeric) | ||
1866 | $path = parse_url($url,PHP_URL_PATH); | ||
1867 | if (!preg_match('!/\d+.+?!',$path)) return array(); | ||
1868 | } | ||
1869 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) | ||
1870 | { // Make sure this TED URL points to a video (/talks/...) | ||
1871 | $path = parse_url($url,PHP_URL_PATH); | ||
1872 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. | ||
1873 | } | ||
1874 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) | ||
1875 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | ||
1876 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | ||
1877 | } | ||
1878 | |||
1879 | // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif | ||
1880 | // Technically speaking, we should download ALL links and check their Content-Type to see if they are images. | ||
1881 | // But using the extension will do. | ||
1882 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); | ||
1883 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') | ||
1884 | { | ||
1885 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) | ||
1886 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | ||
1887 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | ||
1888 | } | ||
1889 | return array(); // No thumbnail. | ||
1890 | |||
1891 | } | ||
1892 | |||
1893 | |||
1894 | // Returns the HTML code to display a thumbnail for a link | ||
1895 | // with a link to the original URL. | ||
1896 | // Understands various services (youtube.com...) | ||
1897 | // Input: $url = URL for which the thumbnail must be found. | ||
1898 | // $href = if provided, this URL will be followed instead of $url | ||
1899 | // Returns '' if no thumbnail available. | ||
1900 | function thumbnail($url,$href=false) | ||
1901 | { | ||
1902 | // FIXME! | ||
1903 | global $conf; | ||
1904 | $t = computeThumbnail($conf, $url,$href); | ||
1905 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | ||
1906 | |||
1907 | $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"'; | ||
1908 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1909 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1910 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1911 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1912 | $html.='></a>'; | ||
1913 | return $html; | ||
1914 | } | ||
1915 | |||
1916 | // Returns the HTML code to display a thumbnail for a link | ||
1917 | // for the picture wall (using lazy image loading) | ||
1918 | // Understands various services (youtube.com...) | ||
1919 | // Input: $url = URL for which the thumbnail must be found. | ||
1920 | // $href = if provided, this URL will be followed instead of $url | ||
1921 | // Returns '' if no thumbnail available. | ||
1922 | function lazyThumbnail($conf, $url,$href=false) | ||
1923 | { | ||
1924 | // FIXME! | ||
1925 | global $conf; | ||
1926 | $t = computeThumbnail($conf, $url,$href); | ||
1927 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | ||
1928 | |||
1929 | $html='<a href="'.escape($t['href']).'">'; | ||
1930 | |||
1931 | // Lazy image | ||
1932 | $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"'; | ||
1933 | |||
1934 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1935 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1936 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1937 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1938 | $html.='>'; | ||
1939 | |||
1940 | // No-JavaScript fallback. | ||
1941 | $html.='<noscript><img src="'.escape($t['src']).'"'; | ||
1942 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1943 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1944 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1945 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1946 | $html.='></noscript></a>'; | ||
1947 | |||
1948 | return $html; | ||
1949 | } | ||
1950 | |||
1951 | |||
1952 | /** | ||
1953 | * Installation | 1716 | * Installation |
1954 | * This function should NEVER be called if the file data/config.php exists. | 1717 | * This function should NEVER be called if the file data/config.php exists. |
1955 | * | 1718 | * |
1956 | * @param ConfigManager $conf Configuration Manager instance. | 1719 | * @param ConfigManager $conf Configuration Manager instance. |
1957 | * @param SessionManager $sessionManager SessionManager instance | 1720 | * @param SessionManager $sessionManager SessionManager instance |
1721 | * @param LoginManager $loginManager LoginManager instance | ||
1958 | */ | 1722 | */ |
1959 | function install($conf, $sessionManager) { | 1723 | function install($conf, $sessionManager, $loginManager) { |
1960 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. | 1724 | // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. |
1961 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); | 1725 | if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); |
1962 | 1726 | ||
@@ -2023,7 +1787,7 @@ function install($conf, $sessionManager) { | |||
2023 | ); | 1787 | ); |
2024 | try { | 1788 | try { |
2025 | // Everything is ok, let's create config file. | 1789 | // Everything is ok, let's create config file. |
2026 | $conf->write(isLoggedIn()); | 1790 | $conf->write($loginManager->isLoggedIn()); |
2027 | } | 1791 | } |
2028 | catch(Exception $e) { | 1792 | catch(Exception $e) { |
2029 | error_log( | 1793 | error_log( |
@@ -2039,7 +1803,7 @@ function install($conf, $sessionManager) { | |||
2039 | exit; | 1803 | exit; |
2040 | } | 1804 | } |
2041 | 1805 | ||
2042 | $PAGE = new PageBuilder($conf, null, $sessionManager->generateToken()); | 1806 | $PAGE = new PageBuilder($conf, $_SESSION, null, $sessionManager->generateToken()); |
2043 | list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); | 1807 | list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); |
2044 | $PAGE->assign('continents', $continents); | 1808 | $PAGE->assign('continents', $continents); |
2045 | $PAGE->assign('cities', $cities); | 1809 | $PAGE->assign('cities', $cities); |
@@ -2048,232 +1812,6 @@ function install($conf, $sessionManager) { | |||
2048 | exit; | 1812 | exit; |
2049 | } | 1813 | } |
2050 | 1814 | ||
2051 | /** | ||
2052 | * Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, | ||
2053 | * I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. | ||
2054 | * The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. | ||
2055 | * This function is called by passing the URL: | ||
2056 | * http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] | ||
2057 | * [URL] is the URL of the link (e.g. a flickr page) | ||
2058 | * [HMAC] is the signature for the [URL] (so that these URL cannot be forged). | ||
2059 | * The function below will fetch the image from the webservice and store it in the cache. | ||
2060 | * | ||
2061 | * @param ConfigManager $conf Configuration Manager instance, | ||
2062 | */ | ||
2063 | function genThumbnail($conf) | ||
2064 | { | ||
2065 | // Make sure the parameters in the URL were generated by us. | ||
2066 | $sign = hash_hmac('sha256', $_GET['url'], $conf->get('credentials.salt')); | ||
2067 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); | ||
2068 | |||
2069 | $cacheDir = $conf->get('resource.thumbnails_cache', 'cache'); | ||
2070 | // Let's see if we don't already have the image for this URL in the cache. | ||
2071 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; | ||
2072 | if (is_file($cacheDir .'/'. $thumbname)) | ||
2073 | { // We have the thumbnail, just serve it: | ||
2074 | header('Content-Type: image/jpeg'); | ||
2075 | echo file_get_contents($cacheDir .'/'. $thumbname); | ||
2076 | return; | ||
2077 | } | ||
2078 | // We may also serve a blank image (if service did not respond) | ||
2079 | $blankname=hash('sha1',$_GET['url']).'.gif'; | ||
2080 | if (is_file($cacheDir .'/'. $blankname)) | ||
2081 | { | ||
2082 | header('Content-Type: image/gif'); | ||
2083 | echo file_get_contents($cacheDir .'/'. $blankname); | ||
2084 | return; | ||
2085 | } | ||
2086 | |||
2087 | // Otherwise, generate the thumbnail. | ||
2088 | $url = $_GET['url']; | ||
2089 | $domain = parse_url($url,PHP_URL_HOST); | ||
2090 | |||
2091 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) | ||
2092 | { | ||
2093 | // Crude replacement to handle new flickr domain policy (They prefer www. now) | ||
2094 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); | ||
2095 | |||
2096 | // Is this a link to an image, or to a flickr page ? | ||
2097 | $imageurl=''; | ||
2098 | if (endsWith(parse_url($url, PHP_URL_PATH), '.jpg')) | ||
2099 | { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg | ||
2100 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); | ||
2101 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; | ||
2102 | } | ||
2103 | else // This is a flickr page (html) | ||
2104 | { | ||
2105 | // Get the flickr html page. | ||
2106 | list($headers, $content) = get_http_response($url, 20); | ||
2107 | if (strpos($headers[0], '200 OK') !== false) | ||
2108 | { | ||
2109 | // flickr now nicely provides the URL of the thumbnail in each flickr page. | ||
2110 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!', $content, $matches); | ||
2111 | if (!empty($matches[1])) $imageurl=$matches[1]; | ||
2112 | |||
2113 | // In albums (and some other pages), the link rel="image_src" is not provided, | ||
2114 | // but flickr provides: | ||
2115 | // <meta property="og:image" content="http://farm4.staticflickr.com/3398/3239339068_25d13535ff_z.jpg" /> | ||
2116 | if ($imageurl=='') | ||
2117 | { | ||
2118 | preg_match('!<meta property=\"og:image\" content=\"(.+?)\"!', $content, $matches); | ||
2119 | if (!empty($matches[1])) $imageurl=$matches[1]; | ||
2120 | } | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | if ($imageurl!='') | ||
2125 | { // Let's download the image. | ||
2126 | // Image is 240x120, so 10 seconds to download should be enough. | ||
2127 | list($headers, $content) = get_http_response($imageurl, 10); | ||
2128 | if (strpos($headers[0], '200 OK') !== false) { | ||
2129 | // Save image to cache. | ||
2130 | file_put_contents($cacheDir .'/'. $thumbname, $content); | ||
2131 | header('Content-Type: image/jpeg'); | ||
2132 | echo $content; | ||
2133 | return; | ||
2134 | } | ||
2135 | } | ||
2136 | } | ||
2137 | |||
2138 | elseif ($domain=='vimeo.com' ) | ||
2139 | { | ||
2140 | // This is more complex: we have to perform a HTTP request, then parse the result. | ||
2141 | // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 | ||
2142 | $vid = substr(parse_url($url,PHP_URL_PATH),1); | ||
2143 | list($headers, $content) = get_http_response('https://vimeo.com/api/v2/video/'.escape($vid).'.php', 5); | ||
2144 | if (strpos($headers[0], '200 OK') !== false) { | ||
2145 | $t = unserialize($content); | ||
2146 | $imageurl = $t[0]['thumbnail_medium']; | ||
2147 | // Then we download the image and serve it to our client. | ||
2148 | list($headers, $content) = get_http_response($imageurl, 10); | ||
2149 | if (strpos($headers[0], '200 OK') !== false) { | ||
2150 | // Save image to cache. | ||
2151 | file_put_contents($cacheDir .'/'. $thumbname, $content); | ||
2152 | header('Content-Type: image/jpeg'); | ||
2153 | echo $content; | ||
2154 | return; | ||
2155 | } | ||
2156 | } | ||
2157 | } | ||
2158 | |||
2159 | elseif ($domain=='ted.com' || endsWith($domain,'.ted.com')) | ||
2160 | { | ||
2161 | // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page | ||
2162 | // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html | ||
2163 | // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" /> | ||
2164 | list($headers, $content) = get_http_response($url, 5); | ||
2165 | if (strpos($headers[0], '200 OK') !== false) { | ||
2166 | // Extract the link to the thumbnail | ||
2167 | preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!', $content, $matches); | ||
2168 | if (!empty($matches[1])) | ||
2169 | { // Let's download the image. | ||
2170 | $imageurl=$matches[1]; | ||
2171 | // No control on image size, so wait long enough | ||
2172 | list($headers, $content) = get_http_response($imageurl, 20); | ||
2173 | if (strpos($headers[0], '200 OK') !== false) { | ||
2174 | $filepath = $cacheDir .'/'. $thumbname; | ||
2175 | file_put_contents($filepath, $content); // Save image to cache. | ||
2176 | if (resizeImage($filepath)) | ||
2177 | { | ||
2178 | header('Content-Type: image/jpeg'); | ||
2179 | echo file_get_contents($filepath); | ||
2180 | return; | ||
2181 | } | ||
2182 | } | ||
2183 | } | ||
2184 | } | ||
2185 | } | ||
2186 | |||
2187 | elseif ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | ||
2188 | { | ||
2189 | // There is no thumbnail available for xkcd comics, so download the whole image and resize it. | ||
2190 | // http://xkcd.com/327/ | ||
2191 | // <img src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" title="<BLABLA>" alt="<BLABLA>" /> | ||
2192 | list($headers, $content) = get_http_response($url, 5); | ||
2193 | if (strpos($headers[0], '200 OK') !== false) { | ||
2194 | // Extract the link to the thumbnail | ||
2195 | preg_match('!<img src="(http://imgs.xkcd.com/comics/.*)" title="[^s]!', $content, $matches); | ||
2196 | if (!empty($matches[1])) | ||
2197 | { // Let's download the image. | ||
2198 | $imageurl=$matches[1]; | ||
2199 | // No control on image size, so wait long enough | ||
2200 | list($headers, $content) = get_http_response($imageurl, 20); | ||
2201 | if (strpos($headers[0], '200 OK') !== false) { | ||
2202 | $filepath = $cacheDir.'/'.$thumbname; | ||
2203 | // Save image to cache. | ||
2204 | file_put_contents($filepath, $content); | ||
2205 | if (resizeImage($filepath)) | ||
2206 | { | ||
2207 | header('Content-Type: image/jpeg'); | ||
2208 | echo file_get_contents($filepath); | ||
2209 | return; | ||
2210 | } | ||
2211 | } | ||
2212 | } | ||
2213 | } | ||
2214 | } | ||
2215 | |||
2216 | else | ||
2217 | { | ||
2218 | // For all other domains, we try to download the image and make a thumbnail. | ||
2219 | // We allow 30 seconds max to download (and downloads are limited to 4 Mb) | ||
2220 | list($headers, $content) = get_http_response($url, 30); | ||
2221 | if (strpos($headers[0], '200 OK') !== false) { | ||
2222 | $filepath = $cacheDir .'/'.$thumbname; | ||
2223 | // Save image to cache. | ||
2224 | file_put_contents($filepath, $content); | ||
2225 | if (resizeImage($filepath)) | ||
2226 | { | ||
2227 | header('Content-Type: image/jpeg'); | ||
2228 | echo file_get_contents($filepath); | ||
2229 | return; | ||
2230 | } | ||
2231 | } | ||
2232 | } | ||
2233 | |||
2234 | |||
2235 | // Otherwise, return an empty image (8x8 transparent gif) | ||
2236 | $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7'); | ||
2237 | // Also put something in cache so that this URL is not requested twice. | ||
2238 | file_put_contents($cacheDir .'/'. $blankname, $blankgif); | ||
2239 | header('Content-Type: image/gif'); | ||
2240 | echo $blankgif; | ||
2241 | } | ||
2242 | |||
2243 | // Make a thumbnail of the image (to width: 120 pixels) | ||
2244 | // Returns true if success, false otherwise. | ||
2245 | function resizeImage($filepath) | ||
2246 | { | ||
2247 | if (!function_exists('imagecreatefromjpeg')) return false; // GD not present: no thumbnail possible. | ||
2248 | |||
2249 | // Trick: some stupid people rename GIF as JPEG... or else. | ||
2250 | // So we really try to open each image type whatever the extension is. | ||
2251 | $header=file_get_contents($filepath,false,NULL,0,256); // Read first 256 bytes and try to sniff file type. | ||
2252 | $im=false; | ||
2253 | $i=strpos($header,'GIF8'); if (($i!==false) && ($i==0)) $im = imagecreatefromgif($filepath); // Well this is crude, but it should be enough. | ||
2254 | $i=strpos($header,'PNG'); if (($i!==false) && ($i==1)) $im = imagecreatefrompng($filepath); | ||
2255 | $i=strpos($header,'JFIF'); if ($i!==false) $im = imagecreatefromjpeg($filepath); | ||
2256 | if (!$im) return false; // Unable to open image (corrupted or not an image) | ||
2257 | $w = imagesx($im); | ||
2258 | $h = imagesy($im); | ||
2259 | $ystart = 0; $yheight=$h; | ||
2260 | if ($h>$w) { $ystart= ($h/2)-($w/2); $yheight=$w/2; } | ||
2261 | $nw = 120; // Desired width | ||
2262 | $nh = min(floor(($h*$nw)/$w),120); // Compute new width/height, but maximum 120 pixels height. | ||
2263 | // Resize image: | ||
2264 | $im2 = imagecreatetruecolor($nw,$nh); | ||
2265 | imagecopyresampled($im2, $im, 0, 0, 0, $ystart, $nw, $nh, $w, $yheight); | ||
2266 | imageinterlace($im2,true); // For progressive JPEG. | ||
2267 | $tempname=$filepath.'_TEMP.jpg'; | ||
2268 | imagejpeg($im2, $tempname, 90); | ||
2269 | imagedestroy($im); | ||
2270 | imagedestroy($im2); | ||
2271 | unlink($filepath); | ||
2272 | rename($tempname,$filepath); // Overwrite original picture with thumbnail. | ||
2273 | return true; | ||
2274 | } | ||
2275 | |||
2276 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail($conf); exit; } // Thumbnail generation/cache does not need the link database. | ||
2277 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; } | 1815 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; } |
2278 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { | 1816 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { |
2279 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); | 1817 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); |
@@ -2287,7 +1825,7 @@ try { | |||
2287 | 1825 | ||
2288 | $linkDb = new LinkDB( | 1826 | $linkDb = new LinkDB( |
2289 | $conf->get('resource.datastore'), | 1827 | $conf->get('resource.datastore'), |
2290 | isLoggedIn(), | 1828 | $loginManager->isLoggedIn(), |
2291 | $conf->get('privacy.hide_public_links'), | 1829 | $conf->get('privacy.hide_public_links'), |
2292 | $conf->get('redirector.url'), | 1830 | $conf->get('redirector.url'), |
2293 | $conf->get('redirector.encode_url') | 1831 | $conf->get('redirector.encode_url') |
@@ -2307,6 +1845,12 @@ $app->group('/api/v1', function() { | |||
2307 | $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); | 1845 | $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); |
2308 | $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); | 1846 | $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); |
2309 | $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); | 1847 | $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); |
1848 | |||
1849 | $this->get('/tags', '\Shaarli\Api\Controllers\Tags:getTags')->setName('getTags'); | ||
1850 | $this->get('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:getTag')->setName('getTag'); | ||
1851 | $this->put('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:putTag')->setName('putTag'); | ||
1852 | $this->delete('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:deleteTag')->setName('deleteTag'); | ||
1853 | |||
2310 | $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory'); | 1854 | $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory'); |
2311 | })->add('\Shaarli\Api\ApiMiddleware'); | 1855 | })->add('\Shaarli\Api\ApiMiddleware'); |
2312 | 1856 | ||
@@ -2316,7 +1860,7 @@ $response = $app->run(true); | |||
2316 | if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { | 1860 | if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { |
2317 | // We use UTF-8 for proper international characters handling. | 1861 | // We use UTF-8 for proper international characters handling. |
2318 | header('Content-Type: text/html; charset=utf-8'); | 1862 | header('Content-Type: text/html; charset=utf-8'); |
2319 | renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager); | 1863 | renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager); |
2320 | } else { | 1864 | } else { |
2321 | $app->respond($response); | 1865 | $app->respond($response); |
2322 | } | 1866 | } |