aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--inc/shaarli.css10
-rw-r--r--index.php254
-rw-r--r--tpl/configure.html4
-rw-r--r--tpl/import.html2
5 files changed, 137 insertions, 137 deletions
diff --git a/README.md b/README.md
index cff718cf..a89d577f 100644
--- a/README.md
+++ b/README.md
@@ -12,12 +12,12 @@ Features:
12 * **FAST** 12 * **FAST**
13 * Dead-simple installation: Drop the files, open the page. No database required. 13 * Dead-simple installation: Drop the files, open the page. No database required.
14 * Easy to use: Single button in your browser to bookmark a page 14 * Easy to use: Single button in your browser to bookmark a page
15 * Save url, title, description (unlimited size). Classify links with tags (with autocomplete) 15 * Save URL, title, description (unlimited size). Classify links with tags (with autocomplete)
16 * Tag renaming, merging and deletion. 16 * Tag renaming, merging and deletion.
17 * Automatic thumbnails for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…) 17 * Automatic thumbnails for various services (imgur, imageshack.us, flickr, youtube, vimeo, dailymotion…)
18 * Automatic conversion of URLs to clickable links in descriptions. Support for http/ftp/file/apt/magnet protocols. 18 * Automatic conversion of URLs to clickable links in descriptions. Support for http/ftp/file/apt/magnet protocols.
19 * Save links as public or private 19 * Save links as public or private
20 * 1-clic access to your private links/notes 20 * 1-click access to your private links/notes
21 * Browse links by page, filter by tag or use the full text search engine 21 * Browse links by page, filter by tag or use the full text search engine
22 * Permalinks (with QR-Code) for easy reference 22 * Permalinks (with QR-Code) for easy reference
23 * RSS and ATOM feeds (which can be filtered by tag or text search) 23 * RSS and ATOM feeds (which can be filtered by tag or text search)
diff --git a/inc/shaarli.css b/inc/shaarli.css
index cdc05790..13e6ab33 100644
--- a/inc/shaarli.css
+++ b/inc/shaarli.css
@@ -1,4 +1,4 @@
1/* CSS Stylsheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */ 1/* Cascading Stylesheet for Shaarli - http://sebsauvage.net/wiki/doku.php?id=php:shaarli */
2 2
3/* CSS Reset from Yahoo to cope with browsers CSS inconsistencies. */ 3/* CSS Reset from Yahoo to cope with browsers CSS inconsistencies. */
4/* 4/*
@@ -408,13 +408,13 @@ div.dailyEntryDescription
408 overflow:auto; 408 overflow:auto;
409} 409}
410 410
411/* Common css screwdriver */ 411/* Common CSS screwdriver */
412.clear{ 412.clear{
413 clear:both; 413 clear:both;
414} 414}
415 415
416/* For lazy images loading in picture wall. 416/* For lazy images loading in picture wall.
417 using http://www.appelsiini.net/projects/lazyload 417 Using http://www.appelsiini.net/projects/lazyload
418 */ 418 */
419.lazyimage { display:none; } 419.lazyimage { display:none; }
420 420
@@ -451,7 +451,7 @@ a {color:#000!important;text-decoration:none!important;}
451#searchform_value { width:70% !important; } 451#searchform_value { width:70% !important; }
452#tagfilter_value { width:70% !important; } 452#tagfilter_value { width:70% !important; }
453div.qrcode { position:relative; float:left; top:-10px; left:0px; } 453div.qrcode { position:relative; float:left; top:-10px; left:0px; }
454#paging_privatelinks { float;none; } 454#paging_privatelinks { float:none; }
455#paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;} 455#paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;}
456#paging_older,#paging_newer,#paging_linksperpage a { border: 1px solid black; padding:3px 5px 3px 5px; background-color:#666; color:#fff; border-radius: 5px 5px 5px 5px;} 456#paging_older,#paging_newer,#paging_linksperpage a { border: 1px solid black; padding:3px 5px 3px 5px; background-color:#666; color:#fff; border-radius: 5px 5px 5px 5px;}
457.thumbnail { float:none; height:auto; margin: 0px; text-align:center;} 457.thumbnail { float:none; height:auto; margin: 0px; text-align:center;}
@@ -466,4 +466,4 @@ div.dailyEntryDescription { font-size:10pt; }
466} 466}
467 467
468/* Highlight search results */ 468/* Highlight search results */
469.highlight { background-color: #FFFF33; } 469.highlight { background-color: #FFFF33; } \ No newline at end of file
diff --git a/index.php b/index.php
index 3136193f..b6d4d2d7 100644
--- a/index.php
+++ b/index.php
@@ -1,9 +1,9 @@
1<?php 1<?php
2// Shaarli 0.0.42 beta - Shaare your links... 2// Shaarli 0.0.42 beta - Shaare your links...
3// The personal, minimalist, super-fast, no-database delicious clone. By sebsauvage.net 3// The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net
4// http://sebsauvage.net/wiki/doku.php?id=php:shaarli 4// http://sebsauvage.net/wiki/doku.php?id=php:shaarli
5// Licence: http://www.opensource.org/licenses/zlib-license.php 5// Licence: http://www.opensource.org/licenses/zlib-license.php
6// Requires: php 5.1.x (but autocomplete fields will only work if you have php 5.2.x) 6// Requires: PHP 5.1.x (but autocomplete fields will only work if you have PHP 5.2.x)
7// ----------------------------------------------------------------------------------------------- 7// -----------------------------------------------------------------------------------------------
8// NEVER TRUST IN PHP.INI 8// NEVER TRUST IN PHP.INI
9// Some hosts do not define a default timezone in php.ini, 9// Some hosts do not define a default timezone in php.ini,
@@ -24,7 +24,7 @@ $GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links
24$GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links. 24$GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links.
25$GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr) 25$GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr)
26$GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory. 26$GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory.
27$GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce webspace usage. 27$GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce web space usage.
28$GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable. 28$GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable.
29$GLOBALS['config']['RAINTPL_TMP'] = 'tmp' ; // Raintpl cache directory 29$GLOBALS['config']['RAINTPL_TMP'] = 'tmp' ; // Raintpl cache directory
30$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailling slash!) 30$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailling slash!)
@@ -32,13 +32,13 @@ $GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/las
32$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours 32$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
33 // Note: You must have publisher.php in the same directory as Shaarli index.php 33 // Note: You must have publisher.php in the same directory as Shaarli index.php
34// ----------------------------------------------------------------------------------------------- 34// -----------------------------------------------------------------------------------------------
35// You should not touch below (or at your own risks !) 35// You should not touch below (or at your own risks!)
36// Optionnal config file. 36// Optional config file.
37if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php'); 37if (is_file($GLOBALS['config']['DATADIR'].'/options.php')) require($GLOBALS['config']['DATADIR'].'/options.php');
38 38
39define('shaarli_version','0.0.42 beta'); 39define('shaarli_version','0.0.42 beta');
40define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code. 40define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in PHP code.
41define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code. 41define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in PHP code.
42// http://server.com/x/shaarli --> /shaarli/ 42// http://server.com/x/shaarli --> /shaarli/
43define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0))); 43define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
44 44
@@ -50,8 +50,8 @@ session_set_cookie_params($cookie['lifetime'],$cookiedir,$_SERVER['HTTP_HOST']);
50// Set session parameters on server side. 50// Set session parameters on server side.
51define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired. 51define('INACTIVITY_TIMEOUT',3600); // (in seconds). If the user does not access any page within this time, his/her session is considered expired.
52ini_set('session.use_cookies', 1); // Use cookies to store session. 52ini_set('session.use_cookies', 1); // Use cookies to store session.
53ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL) 53ini_set('session.use_only_cookies', 1); // Force cookies for session (phpsessionID forbidden in URL).
54ini_set('session.use_trans_sid', false); // Prevent php to use sessionID in URL if cookies are disabled. 54ini_set('session.use_trans_sid', false); // Prevent PHP form using sessionID in URL if cookies are disabled.
55session_name('shaarli'); 55session_name('shaarli');
56if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions). 56if (session_id() == '') session_start(); // Start session if needed (Some server auto-start sessions).
57 57
@@ -86,7 +86,7 @@ header("Cache-Control: no-store, no-cache, must-revalidate");
86header("Cache-Control: post-check=0, pre-check=0", false); 86header("Cache-Control: post-check=0, pre-check=0", false);
87header("Pragma: no-cache"); 87header("Pragma: no-cache");
88 88
89// Directories creations (Note that your web host may require differents rights than 705.) 89// Directories creations (Note that your web host may require different rights than 705.)
90if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>'); 90if (!is_writable(realpath(dirname(__FILE__)))) die('<pre>ERROR: Shaarli does not have the right to write in its own directory ('.realpath(dirname(__FILE__)).').</pre>');
91 91
92// Handling of old config file which do not have the new parameters. 92// Handling of old config file which do not have the new parameters.
@@ -110,13 +110,13 @@ define('STAY_SIGNED_IN_TOKEN', sha1($GLOBALS['hash'].$_SERVER["REMOTE_ADDR"].$GL
110autoLocale(); // Sniff browser language and set date format accordingly. 110autoLocale(); // Sniff browser language and set date format accordingly.
111header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling. 111header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling.
112 112
113// Check php version 113// Check PHP version
114function checkphpversion() 114function checkphpversion()
115{ 115{
116 if (version_compare(PHP_VERSION, '5.1.0') < 0) 116 if (version_compare(PHP_VERSION, '5.1.0') < 0)
117 { 117 {
118 header('Content-Type: text/plain; charset=utf-8'); 118 header('Content-Type: text/plain; charset=utf-8');
119 echo 'Your server supports php '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.'; 119 echo 'Your server supports PHP '.PHP_VERSION.'. Shaarli requires at least php 5.1.0, and thus cannot run. Sorry.';
120 exit; 120 exit;
121 } 121 }
122} 122}
@@ -135,7 +135,7 @@ function checkUpdate()
135 $version=shaarli_version; 135 $version=shaarli_version;
136 list($httpstatus,$headers,$data) = getHTTP('http://sebsauvage.net/files/shaarli_version.txt',2); 136 list($httpstatus,$headers,$data) = getHTTP('http://sebsauvage.net/files/shaarli_version.txt',2);
137 if (strpos($httpstatus,'200 OK')!==false) $version=$data; 137 if (strpos($httpstatus,'200 OK')!==false) $version=$data;
138 // If failed, nevermind. We don't want to bother the user with that. 138 // If failed, never mind. We don't want to bother the user with that.
139 file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date 139 file_put_contents($GLOBALS['config']['UPDATECHECK_FILENAME'],$version); // touch file date
140 } 140 }
141 // Compare versions: 141 // Compare versions:
@@ -151,11 +151,11 @@ function checkUpdate()
151class pageCache 151class pageCache
152{ 152{
153 private $url; // Full URL of the page to cache (typically the value returned by pageUrl()) 153 private $url; // Full URL of the page to cache (typically the value returned by pageUrl())
154 private $shouldBeCached; // boolean: Should this url be cached ? 154 private $shouldBeCached; // boolean: Should this url be cached?
155 private $filename; // Name of the cache file for this url 155 private $filename; // Name of the cache file for this url.
156 156
157 /* 157 /*
158 $url = url (typically the value returned by pageUrl()) 158 $url = URL (typically the value returned by pageUrl())
159 $shouldBeCached = boolean. If false, the cache will be disabled. 159 $shouldBeCached = boolean. If false, the cache will be disabled.
160 */ 160 */
161 public function __construct($url,$shouldBeCached) 161 public function __construct($url,$shouldBeCached)
@@ -217,7 +217,7 @@ function nl2br_escaped($html)
217} 217}
218 218
219/* Returns the small hash of a string, using RFC 4648 base64url format 219/* Returns the small hash of a string, using RFC 4648 base64url format
220 eg. smallHash('20111006_131924') --> yZH23w 220 e.g. smallHash('20111006_131924') --> yZH23w
221 Small hashes: 221 Small hashes:
222 - are unique (well, as unique as crc32, at last) 222 - are unique (well, as unique as crc32, at last)
223 - are always 6 characters long. 223 - are always 6 characters long.
@@ -231,7 +231,7 @@ function smallHash($text)
231 return strtr($t, '+/', '-_'); 231 return strtr($t, '+/', '-_');
232} 232}
233 233
234// In a string, converts urls to clickable links. 234// In a string, converts URLs to clickable links.
235// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 235// Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722
236function text2clickable($url) 236function text2clickable($url)
237{ 237{
@@ -252,8 +252,8 @@ function keepMultipleSpaces($text)
252function autoLocale() 252function autoLocale()
253{ 253{
254 $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE 254 $loc='en_US'; // Default if browser does not send HTTP_ACCEPT_LANGUAGE
255 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // eg. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3" 255 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // e.g. "fr,fr-fr;q=0.8,en;q=0.5,en-us;q=0.3"
256 { // (It's a bit crude, but it works very well. Prefered language is always presented first.) 256 { // (It's a bit crude, but it works very well. Preferred language is always presented first.)
257 if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1]; 257 if (preg_match('/([a-z]{2}(-[a-z]{2})?)/i',$_SERVER['HTTP_ACCEPT_LANGUAGE'],$matches)) $loc=$matches[1];
258 } 258 }
259 setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only. 259 setlocale(LC_TIME,$loc); // LC_TIME = Set local for date/time format only.
@@ -290,7 +290,7 @@ function allIPs()
290} 290}
291 291
292function fillSessionInfo() { 292function fillSessionInfo() {
293 $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid) 293 $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid)
294 $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. 294 $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked.
295 $_SESSION['username']=$GLOBALS['login']; 295 $_SESSION['username']=$GLOBALS['login'];
296 $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. 296 $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration.
@@ -394,7 +394,7 @@ if (isset($_POST['login']))
394{ 394{
395 if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.'); 395 if (!ban_canLogin()) die('I said: NO. You are banned for the moment. Go away.');
396 if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password']))) 396 if (isset($_POST['password']) && tokenOk($_POST['token']) && (check_auth($_POST['login'], $_POST['password'])))
397 { // Login/password is ok. 397 { // Login/password is OK.
398 ban_loginOk(); 398 ban_loginOk();
399 // If user wants to keep the session cookie even after the browser closes: 399 // If user wants to keep the session cookie even after the browser closes:
400 if (!empty($_POST['longlastingsession'])) 400 if (!empty($_POST['longlastingsession']))
@@ -405,7 +405,7 @@ if (isset($_POST['login']))
405 405
406 $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/'; 406 $cookiedir = ''; if(dirname($_SERVER['SCRIPT_NAME'])!='/') $cookiedir=dirname($_SERVER["SCRIPT_NAME"]).'/';
407 session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side 407 session_set_cookie_params($_SESSION['longlastingsession'],$cookiedir,$_SERVER['HTTP_HOST']); // Set session cookie expiration on client side
408 // Note: Never forget the trailing slash on the cookie path ! 408 // Note: Never forget the trailing slash on the cookie path!
409 session_regenerate_id(true); // Send cookie with new expiration date to browser. 409 session_regenerate_id(true); // Send cookie with new expiration date to browser.
410 } 410 }
411 else // Standard session expiration (=when browser closes) 411 else // Standard session expiration (=when browser closes)
@@ -437,7 +437,7 @@ if (isset($_POST['login']))
437// Misc utility functions: 437// Misc utility functions:
438 438
439// Returns the server URL (including port and http/https), without path. 439// Returns the server URL (including port and http/https), without path.
440// eg. "http://myserver.com:8080" 440// e.g. "http://myserver.com:8080"
441// You can append $_SERVER['SCRIPT_NAME'] to get the current script URL. 441// You can append $_SERVER['SCRIPT_NAME'] to get the current script URL.
442function serverUrl() 442function serverUrl()
443{ 443{
@@ -447,24 +447,24 @@ function serverUrl()
447} 447}
448 448
449// Returns the absolute URL of current script, without the query. 449// Returns the absolute URL of current script, without the query.
450// (eg. http://sebsauvage.net/links/) 450// (e.g. http://sebsauvage.net/links/)
451function indexUrl() 451function indexUrl()
452{ 452{
453 $scriptname = $_SERVER["SCRIPT_NAME"]; 453 $scriptname = $_SERVER["SCRIPT_NAME"];
454 // If the script is named 'index.php', we remove it (for better looking URLs, 454 // If the script is named 'index.php', we remove it (for better looking URLs,
455 // eg. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde) 455 // e.g. http://mysite.com/shaarli/?abcde instead of http://mysite.com/shaarli/index.php?abcde)
456 if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9); 456 if (endswith($scriptname,'index.php')) $scriptname = substr($scriptname,0,strlen($scriptname)-9);
457 return serverUrl() . $scriptname; 457 return serverUrl() . $scriptname;
458} 458}
459 459
460// Returns the absolute URL of current script, WITH the query. 460// Returns the absolute URL of current script, WITH the query.
461// (eg. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug) 461// (e.g. http://sebsauvage.net/links/?toto=titi&spamspamspam=humbug)
462function pageUrl() 462function pageUrl()
463{ 463{
464 return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : ''); 464 return indexUrl().(!empty($_SERVER["QUERY_STRING"]) ? '?'.$_SERVER["QUERY_STRING"] : '');
465} 465}
466 466
467// Convert post_max_size/upload_max_filesize (eg.'16M') parameters to bytes. 467// Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes.
468function return_bytes($val) 468function return_bytes($val)
469{ 469{
470 $val = trim($val); $last=strtolower($val[strlen($val)-1]); 470 $val = trim($val); $last=strtolower($val[strlen($val)-1]);
@@ -485,7 +485,7 @@ function getMaxFileSize()
485 $size2 = return_bytes(ini_get('upload_max_filesize')); 485 $size2 = return_bytes(ini_get('upload_max_filesize'));
486 // Return the smaller of two: 486 // Return the smaller of two:
487 $maxsize = min($size1,$size2); 487 $maxsize = min($size1,$size2);
488 // FIXME: Then convert back to readable notations ? (eg. 2M instead of 2000000) 488 // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000)
489 return $maxsize; 489 return $maxsize;
490} 490}
491 491
@@ -533,7 +533,7 @@ function linkdate2iso8601($linkdate)
533function linkdate2locale($linkdate) 533function linkdate2locale($linkdate)
534{ 534{
535 return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale. 535 return utf8_encode(strftime('%c',linkdate2timestamp($linkdate))); // %c is for automatic date format according to locale.
536 // Note that if you use a local which is not installed on your webserver, 536 // Note that if you use a locale which is not installed on your webserver,
537 // the date will not be displayed in the chosen locale, but probably in US notation. 537 // the date will not be displayed in the chosen locale, but probably in US notation.
538} 538}
539 539
@@ -555,10 +555,10 @@ function http_parse_headers_shaarli( $headers )
555} 555}
556 556
557/* GET an URL. 557/* GET an URL.
558 Input: $url : url to get (http://...) 558 Input: $url : URL to get (http://...)
559 $timeout : Network timeout (will wait this many seconds for an anwser before giving up). 559 $timeout : Network timeout (will wait this many seconds for an anwser before giving up).
560 Output: An array. [0] = HTTP status message (eg. "HTTP/1.1 200 OK") or error message 560 Output: An array. [0] = HTTP status message (e.g. "HTTP/1.1 200 OK") or error message
561 [1] = associative array containing HTTP response headers (eg. echo getHTTP($url)[1]['Content-Type']) 561 [1] = associative array containing HTTP response headers (e.g. echo getHTTP($url)[1]['Content-Type'])
562 [2] = data 562 [2] = data
563 Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/'); 563 Example: list($httpstatus,$headers,$data) = getHTTP('http://sebauvage.net/');
564 if (strpos($httpstatus,'200 OK')!==false) 564 if (strpos($httpstatus,'200 OK')!==false)
@@ -574,11 +574,11 @@ function getHTTP($url,$timeout=30)
574 $context = stream_context_create($options); 574 $context = stream_context_create($options);
575 $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source. 575 $data=file_get_contents($url,false,$context,-1, 4000000); // We download at most 4 Mb from source.
576 if (!$data) { return array('HTTP Error',array(),''); } 576 if (!$data) { return array('HTTP Error',array(),''); }
577 $httpStatus=$http_response_header[0]; // eg. "HTTP/1.1 200 OK" 577 $httpStatus=$http_response_header[0]; // e.g. "HTTP/1.1 200 OK"
578 $responseHeaders=http_parse_headers_shaarli($http_response_header); 578 $responseHeaders=http_parse_headers_shaarli($http_response_header);
579 return array($httpStatus,$responseHeaders,$data); 579 return array($httpStatus,$responseHeaders,$data);
580 } 580 }
581 catch (Exception $e) // getHTTP *can* fail silentely (we don't care if the title cannot be fetched) 581 catch (Exception $e) // getHTTP *can* fail silently (we don't care if the title cannot be fetched)
582 { 582 {
583 return array($e->getMessage(),'',''); 583 return array($e->getMessage(),'','');
584 } 584 }
@@ -604,14 +604,14 @@ function getToken()
604 return $rnd; 604 return $rnd;
605} 605}
606 606
607// Tells if a token is ok. Using this function will destroy the token. 607// Tells if a token is OK. Using this function will destroy the token.
608// true=token is ok. 608// true=token is OK.
609function tokenOk($token) 609function tokenOk($token)
610{ 610{
611 if (isset($_SESSION['tokens'][$token])) 611 if (isset($_SESSION['tokens'][$token]))
612 { 612 {
613 unset($_SESSION['tokens'][$token]); // Token is used: destroy it. 613 unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
614 return true; // Token is ok. 614 return true; // Token is OK.
615 } 615 }
616 return false; // Wrong token, or already used. 616 return false; // Wrong token, or already used.
617} 617}
@@ -646,7 +646,7 @@ class pageBuilder
646 $this->tpl->assign('version',shaarli_version); 646 $this->tpl->assign('version',shaarli_version);
647 $this->tpl->assign('scripturl',indexUrl()); 647 $this->tpl->assign('scripturl',indexUrl());
648 $this->tpl->assign('pagetitle','Shaarli'); 648 $this->tpl->assign('pagetitle','Shaarli');
649 $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links ? 649 $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links?
650 if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']); 650 if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']);
651 if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']); 651 if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']);
652 if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']); 652 if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']);
@@ -662,7 +662,7 @@ class pageBuilder
662 } 662 }
663 663
664 // Render a specific page (using a template). 664 // Render a specific page (using a template).
665 // eg. pb.renderPage('picwall') 665 // e.g. pb.renderPage('picwall')
666 public function renderPage($page) 666 public function renderPage($page)
667 { 667 {
668 if ($this->tpl===false) $this->initialize(); // Lazy initialization 668 if ($this->tpl===false) $this->initialize(); // Lazy initialization
@@ -681,10 +681,10 @@ class pageBuilder
681 681
682 Available keys: 682 Available keys:
683 title : Title of the link 683 title : Title of the link
684 url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (eg.'?m-ukcw') 684 url : URL of the link. Can be absolute or relative. Relative URLs are permalinks (e.g.'?m-ukcw')
685 description : description of the entry 685 description : description of the entry
686 private : Is this link private ? 0=no, other value=yes 686 private : Is this link private? 0=no, other value=yes
687 linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (eg.'20110914_192317') 687 linkdate : date of the creation of this entry, in the form YYYYMMDD_HHMMSS (e.g.'20110914_192317')
688 tags : tags attached to this entry (separated by spaces) 688 tags : tags attached to this entry (separated by spaces)
689 689
690 We implement 3 interfaces: 690 We implement 3 interfaces:
@@ -694,15 +694,15 @@ class pageBuilder
694*/ 694*/
695class linkdb implements Iterator, Countable, ArrayAccess 695class linkdb implements Iterator, Countable, ArrayAccess
696{ 696{
697 private $links; // List of links (associative array. Key=linkdate (eg. "20110823_124546"), value= associative array (keys:title,description...) 697 private $links; // List of links (associative array. Key=linkdate (e.g. "20110823_124546"), value= associative array (keys:title,description...)
698 private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate) 698 private $urls; // List of all recorded URLs (key=url, value=linkdate) for fast reserve search (url-->linkdate)
699 private $keys; // List of linkdate keys (for the Iterator interface implementation) 699 private $keys; // List of linkdate keys (for the Iterator interface implementation)
700 private $position; // Position in the $this->keys array. (for the Iterator interface implementation.) 700 private $position; // Position in the $this->keys array. (for the Iterator interface implementation.)
701 private $loggedin; // Is the used logged in ? (used to filter private links) 701 private $loggedin; // Is the user logged in? (used to filter private links)
702 702
703 // Constructor: 703 // Constructor:
704 function __construct($isLoggedIn) 704 function __construct($isLoggedIn)
705 // Input : $isLoggedIn : is the used logged in ? 705 // Input : $isLoggedIn : is the user logged in?
706 { 706 {
707 $this->loggedin = $isLoggedIn; 707 $this->loggedin = $isLoggedIn;
708 $this->checkdb(); // Make sure data file exists. 708 $this->checkdb(); // Make sure data file exists.
@@ -716,7 +716,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
716 public function offsetSet($offset, $value) 716 public function offsetSet($offset, $value)
717 { 717 {
718 if (!$this->loggedin) die('You are not authorized to add a link.'); 718 if (!$this->loggedin) die('You are not authorized to add a link.');
719 if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and url.'); 719 if (empty($value['linkdate']) || empty($value['url'])) die('Internal Error: A link should always have a linkdate and URL.');
720 if (empty($offset)) die('You must specify a key.'); 720 if (empty($offset)) die('You must specify a key.');
721 $this->links[$offset] = $value; 721 $this->links[$offset] = $value;
722 $this->urls[$value['url']]=$offset; 722 $this->urls[$value['url']]=$offset;
@@ -779,19 +779,19 @@ class linkdb implements Iterator, Countable, ArrayAccess
779 invalidateCaches(); 779 invalidateCaches();
780 } 780 }
781 781
782 // Returns the link for a given URL (if it exists). false it does not exist. 782 // Returns the link for a given URL (if it exists). False if it does not exist.
783 public function getLinkFromUrl($url) 783 public function getLinkFromUrl($url)
784 { 784 {
785 if (isset($this->urls[$url])) return $this->links[$this->urls[$url]]; 785 if (isset($this->urls[$url])) return $this->links[$this->urls[$url]];
786 return false; 786 return false;
787 } 787 }
788 788
789 // Case insentitive search among links (in url, title and description). Returns filtered list of links. 789 // Case insensitive search among links (in the URLs, title and description). Returns filtered list of links.
790 // eg. print_r($mydb->filterFulltext('hollandais')); 790 // e.g. print_r($mydb->filterFulltext('hollandais'));
791 public function filterFulltext($searchterms) 791 public function filterFulltext($searchterms)
792 { 792 {
793 // FIXME: explode(' ',$searchterms) and perform a AND search. 793 // FIXME: explode(' ',$searchterms) and perform a AND search.
794 // FIXME: accept double-quotes to search for a string "as is" ? 794 // FIXME: accept double-quotes to search for a string "as is"?
795 $filtered=array(); 795 $filtered=array();
796 $s = strtolower($searchterms); 796 $s = strtolower($searchterms);
797 foreach($this->links as $l) 797 foreach($this->links as $l)
@@ -808,7 +808,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
808 808
809 // Filter by tag. 809 // Filter by tag.
810 // You can specify one or more tags (tags can be separated by space or comma). 810 // You can specify one or more tags (tags can be separated by space or comma).
811 // eg. print_r($mydb->filterTags('linux programming')); 811 // e.g. print_r($mydb->filterTags('linux programming'));
812 public function filterTags($tags,$casesensitive=false) 812 public function filterTags($tags,$casesensitive=false)
813 { 813 {
814 $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags))); 814 $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags)));
@@ -824,9 +824,9 @@ class linkdb implements Iterator, Countable, ArrayAccess
824 return $filtered; 824 return $filtered;
825 } 825 }
826 826
827 // Filter by day. Day must be in the form 'YYYYMMDD' (eg. '20120125') 827 // Filter by day. Day must be in the form 'YYYYMMDD' (e.g. '20120125')
828 // Sort order is: older articles first. 828 // Sort order is: older articles first.
829 // eg. print_r($mydb->filterDay('20120125')); 829 // e.g. print_r($mydb->filterDay('20120125'));
830 public function filterDay($day) 830 public function filterDay($day)
831 { 831 {
832 $filtered=array(); 832 $filtered=array();
@@ -881,13 +881,13 @@ class linkdb implements Iterator, Countable, ArrayAccess
881} 881}
882 882
883// ------------------------------------------------------------------------------------------ 883// ------------------------------------------------------------------------------------------
884// Ouput the last N links in RSS 2.0 format. 884// Output the last N links in RSS 2.0 format.
885function showRSS() 885function showRSS()
886{ 886{
887 header('Content-Type: application/rss+xml; charset=utf-8'); 887 header('Content-Type: application/rss+xml; charset=utf-8');
888 888
889 // $usepermalink : If true, use permalink instead of final link. 889 // $usepermalink : If true, use permalink instead of final link.
890 // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=rss&permalinks 890 // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=rss&permalinks
891 $usepermalinks = isset($_GET['permalinks']); 891 $usepermalinks = isset($_GET['permalinks']);
892 892
893 // Cache system 893 // Cache system
@@ -896,9 +896,9 @@ function showRSS()
896 $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; } 896 $cached = $cache->cachedVersion(); if (!empty($cached)) { echo $cached; exit; }
897 897
898 // If cached was not found (or not usable), then read the database and build the response: 898 // If cached was not found (or not usable), then read the database and build the response:
899 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 899 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if user it not logged in).
900 900
901 // Optionnaly filter the results: 901 // Optionally filter the results:
902 $linksToDisplay=array(); 902 $linksToDisplay=array();
903 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); 903 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
904 elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); 904 elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -955,13 +955,13 @@ function showRSS()
955} 955}
956 956
957// ------------------------------------------------------------------------------------------ 957// ------------------------------------------------------------------------------------------
958// Ouput the last N links in ATOM format. 958// Output the last N links in ATOM format.
959function showATOM() 959function showATOM()
960{ 960{
961 header('Content-Type: application/atom+xml; charset=utf-8'); 961 header('Content-Type: application/atom+xml; charset=utf-8');
962 962
963 // $usepermalink : If true, use permalink instead of final link. 963 // $usepermalink : If true, use permalink instead of final link.
964 // User just has to add 'permalink' in URL parameters. eg. http://mysite.com/shaarli/?do=atom&permalinks 964 // User just has to add 'permalink' in URL parameters. e.g. http://mysite.com/shaarli/?do=atom&permalinks
965 $usepermalinks = isset($_GET['permalinks']); 965 $usepermalinks = isset($_GET['permalinks']);
966 966
967 // Cache system 967 // Cache system
@@ -973,7 +973,7 @@ function showATOM()
973 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 973 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
974 974
975 975
976 // Optionnaly filter the results: 976 // Optionally filter the results:
977 $linksToDisplay=array(); 977 $linksToDisplay=array();
978 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); 978 if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']);
979 elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags'])); 979 elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -1069,7 +1069,7 @@ function showDailyRSS()
1069 if (empty($days[$day])) $days[$day]=array(); 1069 if (empty($days[$day])) $days[$day]=array();
1070 $days[$day][]=$linkdate; 1070 $days[$day][]=$linkdate;
1071 } 1071 }
1072 if (count($days)>$nb_of_days) break; // Have we collected enough days ? 1072 if (count($days)>$nb_of_days) break; // Have we collected enough days?
1073 } 1073 }
1074 1074
1075 // Build the RSS feed. 1075 // Build the RSS feed.
@@ -1148,7 +1148,7 @@ function showDaily()
1148 } 1148 }
1149 1149
1150 /* We need to spread the articles on 3 columns. 1150 /* We need to spread the articles on 3 columns.
1151 I did not want to use a javascript lib like http://masonry.desandro.com/ 1151 I did not want to use a JavaScript lib like http://masonry.desandro.com/
1152 so I manually spread entries with a simple method: I roughly evaluate the 1152 so I manually spread entries with a simple method: I roughly evaluate the
1153 height of a div according to title and description length. 1153 height of a div according to title and description length.
1154 */ 1154 */
@@ -1159,7 +1159,7 @@ function showDaily()
1159 // Roughly estimate length of entry (by counting characters) 1159 // Roughly estimate length of entry (by counting characters)
1160 // Title: 30 chars = 1 line. 1 line is 30 pixels height. 1160 // Title: 30 chars = 1 line. 1 line is 30 pixels height.
1161 // Description: 836 characters gives roughly 342 pixel height. 1161 // Description: 836 characters gives roughly 342 pixel height.
1162 // This is not perfect, but it's usually ok. 1162 // This is not perfect, but it's usually OK.
1163 $length=strlen($link['title'])+(342*strlen($link['description']))/836; 1163 $length=strlen($link['title'])+(342*strlen($link['description']))/836;
1164 if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. 1164 if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height.
1165 // Then put in column which is the less filled: 1165 // Then put in column which is the less filled:
@@ -1212,7 +1212,7 @@ function renderPage()
1212 // -------- Picture wall 1212 // -------- Picture wall
1213 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) 1213 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall'))
1214 { 1214 {
1215 // Optionnaly filter the results: 1215 // Optionally filter the results:
1216 $links=array(); 1216 $links=array();
1217 if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']); 1217 if (!empty($_GET['searchterm'])) $links = $LINKSDB->filterFulltext($_GET['searchterm']);
1218 elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags'])); 1218 elseif (!empty($_GET['searchtags'])) $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
@@ -1292,7 +1292,7 @@ function renderPage()
1292 if (isset($_GET['linksperpage'])) 1292 if (isset($_GET['linksperpage']))
1293 { 1293 {
1294 if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); } 1294 if (is_numeric($_GET['linksperpage'])) { $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); }
1295 // Make sure the referer is from Shaarli itself. 1295 // Make sure the referrer is Shaarli itself.
1296 $referer = '?'; 1296 $referer = '?';
1297 if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) 1297 if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
1298 $referer = $_SERVER['HTTP_REFERER']; 1298 $referer = $_SERVER['HTTP_REFERER'];
@@ -1311,7 +1311,7 @@ function renderPage()
1311 { 1311 {
1312 unset($_SESSION['privateonly']); // See all links 1312 unset($_SESSION['privateonly']); // See all links
1313 } 1313 }
1314 // Make sure the referer is from Shaarli itself. 1314 // Make sure the referrer is Shaarli itself.
1315 $referer = '?'; 1315 $referer = '?';
1316 if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0) 1316 if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['HTTP_HOST'])==0)
1317 $referer = $_SERVER['HTTP_REFERER']; 1317 $referer = $_SERVER['HTTP_REFERER'];
@@ -1322,7 +1322,7 @@ function renderPage()
1322 // -------- Handle other actions allowed for non-logged in users: 1322 // -------- Handle other actions allowed for non-logged in users:
1323 if (!isLoggedIn()) 1323 if (!isLoggedIn())
1324 { 1324 {
1325 // User tries to post new link but is not loggedin: 1325 // User tries to post new link but is not logged in:
1326 // Show login screen, then redirect to ?post=... 1326 // Show login screen, then redirect to ?post=...
1327 if (isset($_GET['post'])) 1327 if (isset($_GET['post']))
1328 { 1328 {
@@ -1332,7 +1332,7 @@ function renderPage()
1332 $PAGE = new pageBuilder; 1332 $PAGE = new pageBuilder;
1333 buildLinkList($PAGE,$LINKSDB); // Compute list of links to display 1333 buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
1334 $PAGE->renderPage('linklist'); 1334 $PAGE->renderPage('linklist');
1335 exit; // Never remove this one ! All operations below are reserved for logged in user. 1335 exit; // Never remove this one! All operations below are reserved for logged in user.
1336 } 1336 }
1337 1337
1338 // -------- All other functions are reserved for the registered user: 1338 // -------- All other functions are reserved for the registered user:
@@ -1353,7 +1353,7 @@ function renderPage()
1353 if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); 1353 if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.');
1354 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) 1354 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
1355 { 1355 {
1356 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! 1356 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
1357 1357
1358 // Make sure old password is correct. 1358 // Make sure old password is correct.
1359 $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']); 1359 $oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']);
@@ -1380,7 +1380,7 @@ function renderPage()
1380 { 1380 {
1381 if (!empty($_POST['title']) ) 1381 if (!empty($_POST['title']) )
1382 { 1382 {
1383 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! 1383 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
1384 $tz = 'UTC'; 1384 $tz = 'UTC';
1385 if (!empty($_POST['continent']) && !empty($_POST['city'])) 1385 if (!empty($_POST['continent']) && !empty($_POST['city']))
1386 if (isTZvalid($_POST['continent'],$_POST['city'])) 1386 if (isTZvalid($_POST['continent'],$_POST['city']))
@@ -1404,7 +1404,7 @@ function renderPage()
1404 $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); 1404 $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES));
1405 $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); 1405 $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES));
1406 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); 1406 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
1407 $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ? 1407 $PAGE->assign('timezone_form',$timezone_form); // FIXME: Put entire tz form generation in template?
1408 $PAGE->assign('timezone_js',$timezone_js); 1408 $PAGE->assign('timezone_js',$timezone_js);
1409 $PAGE->renderPage('configure'); 1409 $PAGE->renderPage('configure');
1410 exit; 1410 exit;
@@ -1428,7 +1428,7 @@ function renderPage()
1428 if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) 1428 if (!empty($_POST['deletetag']) && !empty($_POST['fromtag']))
1429 { 1429 {
1430 $needle=trim($_POST['fromtag']); 1430 $needle=trim($_POST['fromtag']);
1431 $linksToAlter = $LINKSDB->filterTags($needle,true); // true for case-sensitive tag search. 1431 $linksToAlter = $LINKSDB->filterTags($needle,true); // True for case-sensitive tag search.
1432 foreach($linksToAlter as $key=>$value) 1432 foreach($linksToAlter as $key=>$value)
1433 { 1433 {
1434 $tags = explode(' ',trim($value['tags'])); 1434 $tags = explode(' ',trim($value['tags']));
@@ -1436,7 +1436,7 @@ function renderPage()
1436 $value['tags']=trim(implode(' ',$tags)); 1436 $value['tags']=trim(implode(' ',$tags));
1437 $LINKSDB[$key]=$value; 1437 $LINKSDB[$key]=$value;
1438 } 1438 }
1439 $LINKSDB->savedb(); // save to disk 1439 $LINKSDB->savedb(); // Save to disk.
1440 echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>'; 1440 echo '<script language="JavaScript">alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?\';</script>';
1441 exit; 1441 exit;
1442 } 1442 }
@@ -1449,17 +1449,17 @@ function renderPage()
1449 foreach($linksToAlter as $key=>$value) 1449 foreach($linksToAlter as $key=>$value)
1450 { 1450 {
1451 $tags = explode(' ',trim($value['tags'])); 1451 $tags = explode(' ',trim($value['tags']));
1452 $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Remplace tags value. 1452 $tags[array_search($needle,$tags)] = trim($_POST['totag']); // Replace tags value.
1453 $value['tags']=trim(implode(' ',$tags)); 1453 $value['tags']=trim(implode(' ',$tags));
1454 $LINKSDB[$key]=$value; 1454 $LINKSDB[$key]=$value;
1455 } 1455 }
1456 $LINKSDB->savedb(); // save to disk 1456 $LINKSDB->savedb(); // Save to disk.
1457 echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>'; 1457 echo '<script language="JavaScript">alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode($_POST['totag']).'\';</script>';
1458 exit; 1458 exit;
1459 } 1459 }
1460 } 1460 }
1461 1461
1462 // -------- User wants to add a link without using the bookmarklet: show form. 1462 // -------- User wants to add a link without using the bookmarklet: Show form.
1463 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) 1463 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink'))
1464 { 1464 {
1465 $PAGE = new pageBuilder; 1465 $PAGE = new pageBuilder;
@@ -1471,7 +1471,7 @@ function renderPage()
1471 // -------- User clicked the "Save" button when editing a link: Save link to database. 1471 // -------- User clicked the "Save" button when editing a link: Save link to database.
1472 if (isset($_POST['save_edit'])) 1472 if (isset($_POST['save_edit']))
1473 { 1473 {
1474 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away ! 1474 if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away!
1475 $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. 1475 $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces.
1476 $linkdate=$_POST['lf_linkdate']; 1476 $linkdate=$_POST['lf_linkdate'];
1477 $url = trim($_POST['lf_url']); 1477 $url = trim($_POST['lf_url']);
@@ -1481,7 +1481,7 @@ function renderPage()
1481 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); 1481 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
1482 if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. 1482 if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
1483 $LINKSDB[$linkdate] = $link; 1483 $LINKSDB[$linkdate] = $link;
1484 $LINKSDB->savedb(); // save to disk 1484 $LINKSDB->savedb(); // Save to disk.
1485 pubsubhub(); 1485 pubsubhub();
1486 1486
1487 // If we are called from the bookmarklet, we must close the popup: 1487 // If we are called from the bookmarklet, we must close the popup:
@@ -1495,7 +1495,7 @@ function renderPage()
1495 // -------- User clicked the "Cancel" button when editing a link. 1495 // -------- User clicked the "Cancel" button when editing a link.
1496 if (isset($_POST['cancel_edit'])) 1496 if (isset($_POST['cancel_edit']))
1497 { 1497 {
1498 // If we are called from the bookmarklet, we must close the popup; 1498 // If we are called from the bookmarklet, we must close the popup:
1499 if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } 1499 if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; }
1500 $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); 1500 $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
1501 $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. 1501 $returnurl .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited.
@@ -1503,12 +1503,12 @@ function renderPage()
1503 exit; 1503 exit;
1504 } 1504 }
1505 1505
1506 // -------- User clicked the "Delete" button when editing a link : Delete link from database. 1506 // -------- User clicked the "Delete" button when editing a link: Delete link from database.
1507 if (isset($_POST['delete_link'])) 1507 if (isset($_POST['delete_link']))
1508 { 1508 {
1509 if (!tokenOk($_POST['token'])) die('Wrong token.'); 1509 if (!tokenOk($_POST['token'])) die('Wrong token.');
1510 // We do not need to ask for confirmation: 1510 // We do not need to ask for confirmation:
1511 // - confirmation is handled by javascript 1511 // - confirmation is handled by JavaScript
1512 // - we are protected from XSRF by the token. 1512 // - we are protected from XSRF by the token.
1513 $linkdate=$_POST['lf_linkdate']; 1513 $linkdate=$_POST['lf_linkdate'];
1514 unset($LINKSDB[$linkdate]); 1514 unset($LINKSDB[$linkdate]);
@@ -1558,7 +1558,7 @@ function renderPage()
1558 $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL 1558 $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL
1559 $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL 1559 $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL
1560 if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url; 1560 if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
1561 // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.) 1561 // If this is an HTTP link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
1562 if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http') 1562 if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
1563 { 1563 {
1564 list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive. 1564 list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive.
@@ -1612,7 +1612,7 @@ function renderPage()
1612 exit; 1612 exit;
1613 } 1613 }
1614 $exportWhat=$_GET['what']; 1614 $exportWhat=$_GET['what'];
1615 if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???'); 1615 if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export???');
1616 1616
1617 header('Content-Type: text/html; charset=utf-8'); 1617 header('Content-Type: text/html; charset=utf-8');
1618 header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); 1618 header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html');
@@ -1685,8 +1685,8 @@ function importFile()
1685 $filename=$_FILES['filetoupload']['name']; 1685 $filename=$_FILES['filetoupload']['name'];
1686 $filesize=$_FILES['filetoupload']['size']; 1686 $filesize=$_FILES['filetoupload']['size'];
1687 $data=file_get_contents($_FILES['filetoupload']['tmp_name']); 1687 $data=file_get_contents($_FILES['filetoupload']['tmp_name']);
1688 $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private ? 1688 $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private?
1689 $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones ? 1689 $overwrite = !empty($_POST['overwrite']) ; // Should the imported links overwrite existing ones?
1690 $import_count=0; 1690 $import_count=0;
1691 1691
1692 // Sniff file type: 1692 // Sniff file type:
@@ -1697,7 +1697,7 @@ function importFile()
1697 if ($type=='netscape') 1697 if ($type=='netscape')
1698 { 1698 {
1699 // This is a standard Netscape-style bookmark file. 1699 // This is a standard Netscape-style bookmark file.
1700 // This format is supported by all browsers (except IE, of course), also delicious, diigo and others. 1700 // This format is supported by all browsers (except IE, of course), also Delicious, Diigo and others.
1701 foreach(explode('<DT>',$data) as $html) // explode is very fast 1701 foreach(explode('<DT>',$data) as $html) // explode is very fast
1702 { 1702 {
1703 $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0); 1703 $link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0);
@@ -1731,14 +1731,14 @@ function importFile()
1731 1731
1732 // Make sure date/time is not already used by another link. 1732 // Make sure date/time is not already used by another link.
1733 // (Some bookmark files have several different links with the same ADD_DATE) 1733 // (Some bookmark files have several different links with the same ADD_DATE)
1734 // We increment date by 1 second until we find a date which is not used in db. 1734 // We increment date by 1 second until we find a date which is not used in DB.
1735 // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.) 1735 // (so that links that have the same date/time are more or less kept grouped by date, but do not conflict.)
1736 while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly. 1736 while (!empty($LINKSDB[date('Ymd_His',$raw_add_date)])) { $raw_add_date++; }// Yes, I know it's ugly.
1737 $link['linkdate']=date('Ymd_His',$raw_add_date); 1737 $link['linkdate']=date('Ymd_His',$raw_add_date);
1738 $LINKSDB[$link['linkdate']] = $link; 1738 $LINKSDB[$link['linkdate']] = $link;
1739 $import_count++; 1739 $import_count++;
1740 } 1740 }
1741 else // link already present in database. 1741 else // Link already present in database.
1742 { 1742 {
1743 if ($overwrite) 1743 if ($overwrite)
1744 { // If overwrite is required, we import link data, except date/time. 1744 { // If overwrite is required, we import link data, except date/time.
@@ -1789,13 +1789,13 @@ function buildLinkList($PAGE,$LINKSDB)
1789 { 1789 {
1790 header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); 1790 header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
1791 echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.'; 1791 echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.';
1792 echo '<br>You would mind <a href="?">clicking here</a> ?'; 1792 echo '<br>You would mind <a href="?">clicking here</a>?';
1793 exit; 1793 exit;
1794 } 1794 }
1795 $search_type='permalink'; 1795 $search_type='permalink';
1796 } 1796 }
1797 else 1797 else
1798 $linksToDisplay = $LINKSDB; // otherwise, display without filtering. 1798 $linksToDisplay = $LINKSDB; // Otherwise, display without filtering.
1799 1799
1800 // Option: Show only private links 1800 // Option: Show only private links
1801 if (!empty($_SESSION['privateonly'])) 1801 if (!empty($_SESSION['privateonly']))
@@ -1809,11 +1809,11 @@ function buildLinkList($PAGE,$LINKSDB)
1809 } 1809 }
1810 1810
1811 // ---- Handle paging. 1811 // ---- Handle paging.
1812 /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ??? 1812 /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess???
1813 "Warning: array_keys() expects parameter 1 to be array, object given in ... " 1813 "Warning: array_keys() expects parameter 1 to be array, object given in ... "
1814 If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); ) 1814 If my class implements ArrayAccess, why won't array_keys() accept it ? ( $keys=array_keys($linksToDisplay); )
1815 */ 1815 */
1816 $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks php. 1816 $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // Stupid and ugly. Thanks PHP.
1817 1817
1818 // If there is only a single link, we change on-the-fly the title of the page. 1818 // If there is only a single link, we change on-the-fly the title of the page.
1819 if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title']; 1819 if (count($linksToDisplay)==1) $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'].' - '.$GLOBALS['title'];
@@ -1860,7 +1860,7 @@ function buildLinkList($PAGE,$LINKSDB)
1860 $PAGE->assign('result_count',count($linksToDisplay)); 1860 $PAGE->assign('result_count',count($linksToDisplay));
1861 $PAGE->assign('search_type',$search_type); 1861 $PAGE->assign('search_type',$search_type);
1862 $PAGE->assign('search_crits',$search_crits); 1862 $PAGE->assign('search_crits',$search_crits);
1863 $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // optional redirector URL 1863 $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL.
1864 $PAGE->assign('token',$token); 1864 $PAGE->assign('token',$token);
1865 $PAGE->assign('links',$linkDisp); 1865 $PAGE->assign('links',$linkDisp);
1866 return; 1866 return;
@@ -1868,9 +1868,9 @@ function buildLinkList($PAGE,$LINKSDB)
1868 1868
1869// Compute the thumbnail for a link. 1869// Compute the thumbnail for a link.
1870// 1870//
1871// with a link to the original URL. 1871// With a link to the original URL.
1872// Understands various services (youtube.com...) 1872// Understands various services (youtube.com...)
1873// Input: $url = url for which the thumbnail must be found. 1873// Input: $url = URL for which the thumbnail must be found.
1874// $href = if provided, this URL will be followed instead of $url 1874// $href = if provided, this URL will be followed instead of $url
1875// Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) 1875// Returns an associative array with thumbnail attributes (src,href,width,height,style,alt)
1876// Some of them may be missing. 1876// Some of them may be missing.
@@ -1881,7 +1881,7 @@ function computeThumbnail($url,$href=false)
1881 if ($href==false) $href=$url; 1881 if ($href==false) $href=$url;
1882 1882
1883 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. 1883 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link.
1884 // (eg. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) 1884 // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg )
1885 // ^^^^^^^^^^^ ^^^^^^^^^^^ 1885 // ^^^^^^^^^^^ ^^^^^^^^^^^
1886 $domain = parse_url($url,PHP_URL_HOST); 1886 $domain = parse_url($url,PHP_URL_HOST);
1887 if ($domain=='youtube.com' || $domain=='www.youtube.com') 1887 if ($domain=='youtube.com' || $domain=='www.youtube.com')
@@ -1954,17 +1954,17 @@ function computeThumbnail($url,$href=false)
1954 ) 1954 )
1955 { 1955 {
1956 if ($domain=='vimeo.com') 1956 if ($domain=='vimeo.com')
1957 { // Make sure this vimeo url points to a video (/xxx... where xxx is numeric) 1957 { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric)
1958 $path = parse_url($url,PHP_URL_PATH); 1958 $path = parse_url($url,PHP_URL_PATH);
1959 if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. 1959 if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL.
1960 } 1960 }
1961 if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) 1961 if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com'))
1962 { // Make sure this url points to a single comic (/xxx... where xxx is numeric) 1962 { // Make sure this URL points to a single comic (/xxx... where xxx is numeric)
1963 $path = parse_url($url,PHP_URL_PATH); 1963 $path = parse_url($url,PHP_URL_PATH);
1964 if (!preg_match('!/\d+.+?!',$path)) return array(); 1964 if (!preg_match('!/\d+.+?!',$path)) return array();
1965 } 1965 }
1966 if ($domain=='ted.com' || endsWith($domain,'.ted.com')) 1966 if ($domain=='ted.com' || endsWith($domain,'.ted.com'))
1967 { // Make sure this TED url points to a video (/talks/...) 1967 { // Make sure this TED URL points to a video (/talks/...)
1968 $path = parse_url($url,PHP_URL_PATH); 1968 $path = parse_url($url,PHP_URL_PATH);
1969 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. 1969 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
1970 } 1970 }
@@ -1991,7 +1991,7 @@ function computeThumbnail($url,$href=false)
1991// Returns the HTML code to display a thumbnail for a link 1991// Returns the HTML code to display a thumbnail for a link
1992// with a link to the original URL. 1992// with a link to the original URL.
1993// Understands various services (youtube.com...) 1993// Understands various services (youtube.com...)
1994// Input: $url = url for which the thumbnail must be found. 1994// Input: $url = URL for which the thumbnail must be found.
1995// $href = if provided, this URL will be followed instead of $url 1995// $href = if provided, this URL will be followed instead of $url
1996// Returns '' if no thumbnail available. 1996// Returns '' if no thumbnail available.
1997function thumbnail($url,$href=false) 1997function thumbnail($url,$href=false)
@@ -2012,7 +2012,7 @@ function thumbnail($url,$href=false)
2012// Returns the HTML code to display a thumbnail for a link 2012// Returns the HTML code to display a thumbnail for a link
2013// for the picture wall (using lazy image loading) 2013// for the picture wall (using lazy image loading)
2014// Understands various services (youtube.com...) 2014// Understands various services (youtube.com...)
2015// Input: $url = url for which the thumbnail must be found. 2015// Input: $url = URL for which the thumbnail must be found.
2016// $href = if provided, this URL will be followed instead of $url 2016// $href = if provided, this URL will be followed instead of $url
2017// Returns '' if no thumbnail available. 2017// Returns '' if no thumbnail available.
2018function lazyThumbnail($url,$href=false) 2018function lazyThumbnail($url,$href=false)
@@ -2022,7 +2022,7 @@ function lazyThumbnail($url,$href=false)
2022 2022
2023 $html='<a href="'.htmlspecialchars($t['href']).'">'; 2023 $html='<a href="'.htmlspecialchars($t['href']).'">';
2024 2024
2025 // Lazy image (only loaded by javascript when in the viewport). 2025 // Lazy image (only loaded by JavaScript when in the viewport).
2026 if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled) 2026 if (!empty($GLOBALS['disablejquery'])) // (except if jQuery is disabled)
2027 $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"'; 2027 $html.='<img class="lazyimage" src="'.htmlspecialchars($t['src']).'"';
2028 else 2028 else
@@ -2034,7 +2034,7 @@ function lazyThumbnail($url,$href=false)
2034 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"'; 2034 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
2035 $html.='>'; 2035 $html.='>';
2036 2036
2037 // No-javascript fallback. 2037 // No-JavaScript fallback.
2038 $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"'; 2038 $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"';
2039 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"'; 2039 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
2040 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"'; 2040 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
@@ -2071,7 +2071,7 @@ function install()
2071 header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data. 2071 header('Location: '.indexUrl().'?test_session'); // Redirect to check stored data.
2072 } 2072 }
2073 if (isset($_GET['test_session'])) 2073 if (isset($_GET['test_session']))
2074 { // Step 3: Sessions are ok. Remove test parameter from URL. 2074 { // Step 3: Sessions are OK. Remove test parameter from URL.
2075 header('Location: '.indexUrl()); 2075 header('Location: '.indexUrl());
2076 } 2076 }
2077 2077
@@ -2089,7 +2089,7 @@ function install()
2089 $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']); 2089 $GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
2090 $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] ); 2090 $GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] );
2091 writeConfig(); 2091 writeConfig();
2092 echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>'; 2092 echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
2093 exit; 2093 exit;
2094 } 2094 }
2095 2095
@@ -2104,14 +2104,14 @@ function install()
2104 exit; 2104 exit;
2105} 2105}
2106 2106
2107// Generates the timezone selection form and javascript. 2107// Generates the timezone selection form and JavaScript.
2108// Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected. 2108// Input: (optional) current timezone (can be 'UTC/UTC'). It will be pre-selected.
2109// Output: array(html,js) 2109// Output: array(html,js)
2110// Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected. 2110// Example: list($htmlform,$js) = templateTZform('Europe/Paris'); // Europe/Paris pre-selected.
2111// Returns array('','') if server does not support timezones list. (eg. php 5.1 on free.fr) 2111// Returns array('','') if server does not support timezones list. (e.g. PHP 5.1 on free.fr)
2112function templateTZform($ptz=false) 2112function templateTZform($ptz=false)
2113{ 2113{
2114 if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr 2114 if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
2115 { 2115 {
2116 // Try to split the provided timezone. 2116 // Try to split the provided timezone.
2117 if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; } 2117 if ($ptz==false) { $l=timezone_identifiers_list(); $ptz=$l[0]; }
@@ -2120,7 +2120,7 @@ function templateTZform($ptz=false)
2120 // Display config form: 2120 // Display config form:
2121 $timezone_form = ''; 2121 $timezone_form = '';
2122 $timezone_js = ''; 2122 $timezone_js = '';
2123 // The list is in the forme "Europe/Paris", "America/Argentina/Buenos_Aires"... 2123 // The list is in the form "Europe/Paris", "America/Argentina/Buenos_Aires"...
2124 // We split the list in continents/cities. 2124 // We split the list in continents/cities.
2125 $continents = array(); 2125 $continents = array();
2126 $cities = array(); 2126 $cities = array();
@@ -2158,9 +2158,9 @@ function templateTZform($ptz=false)
2158function isTZvalid($continent,$city) 2158function isTZvalid($continent,$city)
2159{ 2159{
2160 $tz = $continent.'/'.$city; 2160 $tz = $continent.'/'.$city;
2161 if (function_exists('timezone_identifiers_list')) // because of old php version (5.1) which can be found on free.fr 2161 if (function_exists('timezone_identifiers_list')) // because of old PHP version (5.1) which can be found on free.fr
2162 { 2162 {
2163 if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone ? 2163 if (in_array($tz, timezone_identifiers_list())) // it's a valid timezone?
2164 return true; 2164 return true;
2165 } 2165 }
2166 return false; 2166 return false;
@@ -2203,7 +2203,7 @@ if (!function_exists('json_encode')) {
2203} 2203}
2204 2204
2205// Webservices (for use with jQuery/jQueryUI) 2205// Webservices (for use with jQuery/jQueryUI)
2206// eg. index.php?ws=tags&term=minecr 2206// e.g. index.php?ws=tags&term=minecr
2207function processWS() 2207function processWS()
2208{ 2208{
2209 if (empty($_GET['ws']) || empty($_GET['term'])) return; 2209 if (empty($_GET['ws']) || empty($_GET['term'])) return;
@@ -2211,7 +2211,7 @@ function processWS()
2211 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 2211 $LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
2212 header('Content-Type: application/json; charset=utf-8'); 2212 header('Content-Type: application/json; charset=utf-8');
2213 2213
2214 // Search in tags (case insentitive, cumulative search) 2214 // Search in tags (case insensitive, cumulative search)
2215 if ($_GET['ws']=='tags') 2215 if ($_GET['ws']=='tags')
2216 { 2216 {
2217 $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d") 2217 $tags=explode(' ',str_replace(',',' ',$term)); $last = array_pop($tags); // Get the last term ("a b c d" ==> "a b c", "d")
@@ -2227,7 +2227,7 @@ function processWS()
2227 exit; 2227 exit;
2228 } 2228 }
2229 2229
2230 // Search a single tag (case sentitive, single tag search) 2230 // Search a single tag (case sensitive, single tag search)
2231 if ($_GET['ws']=='singletag') 2231 if ($_GET['ws']=='singletag')
2232 { 2232 {
2233 /* To speed up things, we store list of tags in session */ 2233 /* To speed up things, we store list of tags in session */
@@ -2243,7 +2243,7 @@ function processWS()
2243 2243
2244// Re-write configuration file according to globals. 2244// Re-write configuration file according to globals.
2245// Requires some $GLOBALS to be set (login,hash,salt,title). 2245// Requires some $GLOBALS to be set (login,hash,salt,title).
2246// If the config file cannot be saved, an error message is dislayed and the user is redirected to "Tools" menu. 2246// If the config file cannot be saved, an error message is displayed and the user is redirected to "Tools" menu.
2247// (otherwise, the function simply returns.) 2247// (otherwise, the function simply returns.)
2248function writeConfig() 2248function writeConfig()
2249{ 2249{
@@ -2263,12 +2263,12 @@ function writeConfig()
2263 } 2263 }
2264} 2264}
2265 2265
2266/* Because some f*cking services like Flickr require an extra HTTP request to get the thumbnail URL, 2266/* Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL,
2267 I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. 2267 I have deported the thumbnail URL code generation here, otherwise this would slow down page generation.
2268 The following function takes the URL a link (eg. a flickr page) and return the proper thumbnail. 2268 The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail.
2269 This function is called by passing the url: 2269 This function is called by passing the URL:
2270 http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] 2270 http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL]
2271 [URL] is the URL of the link (eg. a flickr page) 2271 [URL] is the URL of the link (e.g. a flickr page)
2272 [HMAC] is the signature for the [URL] (so that these URL cannot be forged). 2272 [HMAC] is the signature for the [URL] (so that these URL cannot be forged).
2273 The function below will fetch the image from the webservice and store it in the cache. 2273 The function below will fetch the image from the webservice and store it in the cache.
2274*/ 2274*/
@@ -2276,7 +2276,7 @@ function genThumbnail()
2276{ 2276{
2277 // Make sure the parameters in the URL were generated by us. 2277 // Make sure the parameters in the URL were generated by us.
2278 $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']); 2278 $sign = hash_hmac('sha256', $_GET['url'], $GLOBALS['salt']);
2279 if ($sign!=$_GET['hmac']) die('Naughty boy !'); 2279 if ($sign!=$_GET['hmac']) die('Naughty boy!');
2280 2280
2281 // Let's see if we don't already have the image for this URL in the cache. 2281 // Let's see if we don't already have the image for this URL in the cache.
2282 $thumbname=hash('sha1',$_GET['url']).'.jpg'; 2282 $thumbname=hash('sha1',$_GET['url']).'.jpg';
@@ -2301,22 +2301,22 @@ function genThumbnail()
2301 2301
2302 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) 2302 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com'))
2303 { 2303 {
2304 // Crude replacement to handle new Flickr domain policy (They prefer www. now) 2304 // Crude replacement to handle new flickr domain policy (They prefer www. now)
2305 $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); 2305 $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url);
2306 2306
2307 // Is this a link to an image, or to a flickr page ? 2307 // Is this a link to an image, or to a flickr page ?
2308 $imageurl=''; 2308 $imageurl='';
2309 if (endswith(parse_url($url,PHP_URL_PATH),'.jpg')) 2309 if (endswith(parse_url($url,PHP_URL_PATH),'.jpg'))
2310 { // This is a direct link to an image. eg. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg 2310 { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg
2311 preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); 2311 preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
2312 if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; 2312 if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
2313 } 2313 }
2314 else // this is a flickr page (html) 2314 else // This is a flickr page (html)
2315 { 2315 {
2316 list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page. 2316 list($httpstatus,$headers,$data) = getHTTP($url,20); // Get the flickr html page.
2317 if (strpos($httpstatus,'200 OK')!==false) 2317 if (strpos($httpstatus,'200 OK')!==false)
2318 { 2318 {
2319 // Flickr now nicely provides the URL of the thumbnail in each flickr page. 2319 // flickr now nicely provides the URL of the thumbnail in each flickr page.
2320 preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches); 2320 preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!',$data,$matches);
2321 if (!empty($matches[1])) $imageurl=$matches[1]; 2321 if (!empty($matches[1])) $imageurl=$matches[1];
2322 2322
@@ -2347,7 +2347,7 @@ function genThumbnail()
2347 elseif ($domain=='vimeo.com' ) 2347 elseif ($domain=='vimeo.com' )
2348 { 2348 {
2349 // This is more complex: we have to perform a HTTP request, then parse the result. 2349 // This is more complex: we have to perform a HTTP request, then parse the result.
2350 // Maybe we should deport this to javascript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 2350 // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
2351 $vid = substr(parse_url($url,PHP_URL_PATH),1); 2351 $vid = substr(parse_url($url,PHP_URL_PATH),1);
2352 list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5); 2352 list($httpstatus,$headers,$data) = getHTTP('http://vimeo.com/api/v2/video/'.htmlspecialchars($vid).'.php',5);
2353 if (strpos($httpstatus,'200 OK')!==false) 2353 if (strpos($httpstatus,'200 OK')!==false)
@@ -2483,7 +2483,7 @@ function resizeImage($filepath)
2483} 2483}
2484 2484
2485// Invalidate caches when the database is changed or the user logs out. 2485// Invalidate caches when the database is changed or the user logs out.
2486// (eg. tags cache). 2486// (e.g. tags cache).
2487function invalidateCaches() 2487function invalidateCaches()
2488{ 2488{
2489 unset($_SESSION['tags']); // Purge cache attached to session. 2489 unset($_SESSION['tags']); // Purge cache attached to session.
diff --git a/tpl/configure.html b/tpl/configure.html
index ef20e94e..645107ae 100644
--- a/tpl/configure.html
+++ b/tpl/configure.html
@@ -19,7 +19,7 @@
19 <tr><td valign="top"><b>Security:</b></td><td><input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" {if="!empty($GLOBALS['disablesessionprotection'])"}checked{/if}><label for="disablesessionprotection">&nbsp;Disable session cookie hijacking protection (Check this if you get disconnected often or if your IP address changes often.)</label></td></tr> 19 <tr><td valign="top"><b>Security:</b></td><td><input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" {if="!empty($GLOBALS['disablesessionprotection'])"}checked{/if}><label for="disablesessionprotection">&nbsp;Disable session cookie hijacking protection (Check this if you get disconnected often or if your IP address changes often.)</label></td></tr>
20 20
21 <tr><td valign="top"><b>Features:</b></td><td> 21 <tr><td valign="top"><b>Features:</b></td><td>
22 <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery">&nbsp;Disable jQuery and all heavy javascript (for example: Autocomplete in tags. Useful for slow computers.)</label> 22 <input type="checkbox" name="disablejquery" id="disablejquery" {if="!empty($GLOBALS['disablejquery'])"}checked{/if}><label for="disablejquery">&nbsp;Disable jQuery and all heavy JavaScript (for example: Autocomplete in tags. Useful for slow computers.)</label>
23 </td></tr> 23 </td></tr>
24 <tr><td valign="top"><b>New link:</b></td><td> 24 <tr><td valign="top"><b>New link:</b></td><td>
25 <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault">&nbsp;All new link are private by default</label></td> 25 <input type="checkbox" name="privateLinkByDefault" id="privateLinkByDefault" {if="!empty($GLOBALS['privateLinkByDefault'])"}checked{/if}/><label for="privateLinkByDefault">&nbsp;All new link are private by default</label></td>
@@ -30,4 +30,4 @@
30</div> 30</div>
31{include="page.footer"} 31{include="page.footer"}
32</body> 32</body>
33</html> 33</html> \ No newline at end of file
diff --git a/tpl/import.html b/tpl/import.html
index 9e581fc9..259e56ee 100644
--- a/tpl/import.html
+++ b/tpl/import.html
@@ -5,7 +5,7 @@
5<div id="pageheader"> 5<div id="pageheader">
6 {include="page.header"} 6 {include="page.header"}
7 <div id="uploaddiv"> 7 <div id="uploaddiv">
8 Import Netscape html bookmarks (as exported from Firefox/Chrome/Opera/delicious/diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes). 8 Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize|htmlspecialchars} bytes).
9 <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform"> 9 <form method="POST" action="?do=upload" enctype="multipart/form-data" name="uploadform" id="uploadform">
10 <input type="hidden" name="token" value="{$token}"> 10 <input type="hidden" name="token" value="{$token}">
11 <input type="file" name="filetoupload" size="80"> 11 <input type="file" name="filetoupload" size="80">