aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSeb Sauvage <sebsauvage@sebsauvage.net>2012-02-01 23:07:32 +0100
committerEmilien Klein <emilien@klein.st>2012-02-01 23:07:32 +0100
commita22e60cc0abb0547af8d792dec15f59bf77b5630 (patch)
tree826845d8d5d46d6a4d1096c57c2bddd07ae40071
parent96bc4efe9ea9ed513d1ec06e0823847d5eebe82f (diff)
downloadShaarli-a22e60cc0abb0547af8d792dec15f59bf77b5630.tar.gz
Shaarli-a22e60cc0abb0547af8d792dec15f59bf77b5630.tar.zst
Shaarli-a22e60cc0abb0547af8d792dec15f59bf77b5630.zip
Version 0.0.37 beta
- Added: Basic CSS for mobiles, which makes Shaarli much more usable on mobile devices. - Added: Picture wall no more instantly kills your browser. Now it uses lazy image loading [0]: The picture are loaded only as you scroll the page. This will reduce browser memory usage (especially on mobile devices) and will reduce server load. If you have javascript disabled, the page will still work as before (all images loaded at once). - Added: RSS feed for the “Daily” page. 1 RSS entry per day, with all links of that day. RSS feed provides the last 7 days (only non-empty days are returned). - Added: In link list, added an icon to see only private links. Click to toggle (only private / all). [0] http://www.appelsiini.net/projects/lazyload
-rw-r--r--images/feed-icon-14x14.pngbin0 -> 689 bytes
-rw-r--r--images/private_16x16.pngbin0 -> 439 bytes
-rw-r--r--inc/jquery.lazyload.min.js15
-rw-r--r--inc/shaarli.css49
-rw-r--r--index.php322
-rw-r--r--tpl/daily.html13
-rw-r--r--tpl/dailyrss.html8
-rw-r--r--tpl/includes.html2
-rw-r--r--tpl/linklist.html20
-rw-r--r--tpl/linklist.paging.html23
-rw-r--r--tpl/page.header.html8
-rw-r--r--tpl/picwall.html9
12 files changed, 359 insertions, 110 deletions
diff --git a/images/feed-icon-14x14.png b/images/feed-icon-14x14.png
new file mode 100644
index 00000000..b3c949d2
--- /dev/null
+++ b/images/feed-icon-14x14.png
Binary files differ
diff --git a/images/private_16x16.png b/images/private_16x16.png
new file mode 100644
index 00000000..d58c4823
--- /dev/null
+++ b/images/private_16x16.png
Binary files differ
diff --git a/inc/jquery.lazyload.min.js b/inc/jquery.lazyload.min.js
new file mode 100644
index 00000000..2364d8d9
--- /dev/null
+++ b/inc/jquery.lazyload.min.js
@@ -0,0 +1,15 @@
1/*
2 * Lazy Load - jQuery plugin for lazy loading images
3 *
4 * Copyright (c) 2007-2012 Mika Tuupola
5 *
6 * Licensed under the MIT license:
7 * http://www.opensource.org/licenses/mit-license.php
8 *
9 * Project home:
10 * http://www.appelsiini.net/projects/lazyload
11 *
12 * Version: 1.7.0
13 *
14 */
15(function(a,b){$window=a(b),a.fn.lazyload=function(c){var d={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null};c&&(undefined!==c.failurelimit&&(c.failure_limit=c.failurelimit,delete c.failurelimit),undefined!==c.effectspeed&&(c.effect_speed=c.effectspeed,delete c.effectspeed),a.extend(d,c));var e=this;return 0==d.event.indexOf("scroll")&&a(d.container).bind(d.event,function(b){var c=0;e.each(function(){$this=a(this);if(d.skip_invisible&&!$this.is(":visible"))return;if(!a.abovethetop(this,d)&&!a.leftofbegin(this,d))if(!a.belowthefold(this,d)&&!a.rightoffold(this,d))$this.trigger("appear");else if(++c>d.failure_limit)return!1})}),this.each(function(){var b=this,c=a(b);b.loaded=!1,c.one("appear",function(){if(!this.loaded){if(d.appear){var f=e.length;d.appear.call(b,f,d)}a("<img />").bind("load",function(){c.hide().attr("src",c.data(d.data_attribute))[d.effect](d.effect_speed),b.loaded=!0;var f=a.grep(e,function(a){return!a.loaded});e=a(f);if(d.load){var g=e.length;d.load.call(b,g,d)}}).attr("src",c.data(d.data_attribute))}}),0!=d.event.indexOf("scroll")&&c.bind(d.event,function(a){b.loaded||c.trigger("appear")})}),$window.bind("resize",function(b){a(d.container).trigger(d.event)}),a(d.container).trigger(d.event),this},a.belowthefold=function(c,d){if(d.container===undefined||d.container===b)var e=$window.height()+$window.scrollTop();else var e=a(d.container).offset().top+a(d.container).height();return e<=a(c).offset().top-d.threshold},a.rightoffold=function(c,d){if(d.container===undefined||d.container===b)var e=$window.width()+$window.scrollLeft();else var e=a(d.container).offset().left+a(d.container).width();return e<=a(c).offset().left-d.threshold},a.abovethetop=function(c,d){if(d.container===undefined||d.container===b)var e=$window.scrollTop();else var e=a(d.container).offset().top;return e>=a(c).offset().top+d.threshold+a(c).height()},a.leftofbegin=function(c,d){if(d.container===undefined||d.container===b)var e=$window.scrollLeft();else var e=a(d.container).offset().left;return e>=a(c).offset().left+d.threshold+a(c).width()},a.inviewport=function(b,c){return!a.rightofscreen(b,c)&&!a.leftofscreen(b,c)&&!a.belowthefold(b,c)&&!a.abovethetop(b,c)},a.extend(a.expr[":"],{"below-the-fold":function(c){return a.belowthefold(c,{threshold:0,container:b})},"above-the-top":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-screen":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-screen":function(c){return!a.rightoffold(c,{threshold:0,container:b})},"in-viewport":function(c){return!a.inviewport(c,{threshold:0,container:b})},"above-the-fold":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-fold":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-fold":function(c){return!a.rightoffold(c,{threshold:0,container:b})}})})(jQuery,window)
diff --git a/inc/shaarli.css b/inc/shaarli.css
index a949666e..5b4b88b6 100644
--- a/inc/shaarli.css
+++ b/inc/shaarli.css
@@ -184,6 +184,12 @@ cursor:pointer;
184.paging a:visited { color:#ccc; } 184.paging a:visited { color:#ccc; }
185.paging a:hover { color:#FFFFC9; } 185.paging a:hover { color:#FFFFC9; }
186.paging a:active { color:#fff; } 186.paging a:active { color:#fff; }
187#paging_privatelinks { float:left; }
188#paging_linksperpage { float:right; padding-right:5px; }
189#paging_current { display:inline; color:#fff; padding:0 20 0 20; }
190#paging_older { margin-right:15px; }
191#paging_newer { margin-left:15px; }
192
187#headerform { color:#ffffff; padding:5px 5px 5px 5px; clear: both;} 193#headerform { color:#ffffff; padding:5px 5px 5px 5px; clear: both;}
188#toolsdiv { color:#ffffff; padding:5px 5px 5px 5px; clear:left; } 194#toolsdiv { color:#ffffff; padding:5px 5px 5px 5px; clear:left; }
189#uploaddiv { color:#ffffff; padding:5px 5px 5px 5px; clear:left; } 195#uploaddiv { color:#ffffff; padding:5px 5px 5px 5px; clear:left; }
@@ -325,6 +331,11 @@ div.daily
325 position:relative; 331 position:relative;
326 border-bottom: 2px solid black; 332 border-bottom: 2px solid black;
327} 333}
334
335#daily_col1 { float:left;position:relative; width:33%; padding-left:1%; }
336#daily_col2 { float:left;position:relative; width:33%; }
337#daily_col3 { float:left;position:relative; width:33%;}
338
328div.dailyAbout 339div.dailyAbout
329{ 340{
330 float:left; 341 float:left;
@@ -383,6 +394,11 @@ div.dailyEntryDescription
383 clear:both; 394 clear:both;
384} 395}
385 396
397/* For lazy images loading in picture wall.
398 using http://www.appelsiini.net/projects/lazyload
399 */
400.lazyimage { display:none; }
401
386@media print { 402@media print {
387html {border:none;background:#fff!important;color:#000!important;} 403html {border:none;background:#fff!important;color:#000!important;}
388body {font-size:12pt;width:auto!important;margin:auto!important;} 404body {font-size:12pt;width:auto!important;margin:auto!important;}
@@ -397,3 +413,36 @@ a {color:#000!important;text-decoration:none!important;}
397.linktag { border: 1px solid black; font-style:italic; font-size:8pt;} 413.linktag { border: 1px solid black; font-style:italic; font-size:8pt;}
398 414
399} 415}
416
417
418@media handheld, only screen and (max-width: 480px), only screen and (max-device-width: 854px)
419{
420/* A few fixes for mobile devices (far from perfect). */
421.nomobile { display:none; }
422#logo { display:none; }
423#pageheader a
424{
425 padding:5px;
426 border-radius: 5px 5px 5px 5px;
427 margin:3px;
428}
429.searchform,.tagfilter { display:block !important; margin:0px !important; padding:0px !important; width:100% !important; }
430.searchform input,.tagfilter input { margin:0px !important; padding:0px !important; display:inline !important; }
431.tagfilter input.bigbutton,.searchform input.bigbutton,.addform input.bigbutton{ width:30%; font-size:smaller;}
432#searchform_value { width:70% !important; }
433#tagfilter_value { width:70% !important; }
434div.qrcode { position:relative; float:left; top:-10px; left:0px; }
435#paging_privatelinks { float;none; }
436#paging_linksperpage { float:none; margin-bottom:10px; font-size:smaller;}
437#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;}
438.thumbnail { float:none; height:auto; margin: 0px; text-align:center;}
439#cloudtag { padding:0px; }
440div.dailyAbout { float:none; position:relative; width:100%; clear:both; padding:0px; top:0px; left:0px; }
441#daily_col1,#daily_col2,#daily_col3 { float:none; width:100%; padding:0px;}
442div.dailyTitle { font-size: 18pt; margin-top:5px; padding:0px;}
443div.dailyDate { font-size: 11pt;padding:0px; display:block; }
444div.dailyEntryTitle { font-size:16pt; font-weight:bold;}
445div.dailyEntryDescription { font-size:10pt; }
446
447}
448
diff --git a/index.php b/index.php
index 9dd6483e..936d6129 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,5 @@
1<?php 1<?php
2// Shaarli 0.0.36 beta - Shaare your links... 2// Shaarli 0.0.37 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
@@ -58,7 +58,7 @@ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
58header("Cache-Control: no-store, no-cache, must-revalidate"); 58header("Cache-Control: no-store, no-cache, must-revalidate");
59header("Cache-Control: post-check=0, pre-check=0", false); 59header("Cache-Control: post-check=0, pre-check=0", false);
60header("Pragma: no-cache"); 60header("Pragma: no-cache");
61define('shaarli_version','0.0.36 beta'); 61define('shaarli_version','0.0.37 beta');
62if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); } 62if (!is_dir($GLOBALS['config']['DATADIR'])) { mkdir($GLOBALS['config']['DATADIR'],0705); chmod($GLOBALS['config']['DATADIR'],0705); }
63if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['DATADIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. 63if (!is_file($GLOBALS['config']['DATADIR'].'/.htaccess')) { file_put_contents($GLOBALS['config']['DATADIR'].'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files.
64if ($GLOBALS['config']['ENABLE_LOCALCACHE']) 64if ($GLOBALS['config']['ENABLE_LOCALCACHE'])
@@ -153,8 +153,8 @@ function text2clickable($url)
153// even in the absence of <pre> (This is used in description to keep text formatting) 153// even in the absence of <pre> (This is used in description to keep text formatting)
154function keepMultipleSpaces($text) 154function keepMultipleSpaces($text)
155{ 155{
156 return str_replace(' ',' &nbsp;',$text); 156 return str_replace(' ',' &nbsp;',$text);
157 157
158} 158}
159// ------------------------------------------------------------------------------------------ 159// ------------------------------------------------------------------------------------------
160// Sniff browser language to display dates in the right format automatically. 160// Sniff browser language to display dates in the right format automatically.
@@ -850,6 +850,75 @@ function showATOM()
850} 850}
851 851
852// ------------------------------------------------------------------------------------------ 852// ------------------------------------------------------------------------------------------
853// Daily RSS feed: 1 RSS entry per day giving all the links on that day.
854// Gives the last 7 days (which have links).
855// This RSS feed cannot be filtered.
856function showDailyRSS()
857{
858 global $LINKSDB;
859
860 /* Some Shaarlies may have very few links, so we need to look
861 back in time (rsort()) until we have enough days ($nb_of_days).
862 */
863 $linkdates=array(); foreach($LINKSDB as $linkdate=>$value) { $linkdates[]=$linkdate; }
864 rsort($linkdates);
865 $nb_of_days=7; // We take 7 days.
866 $today=Date('Ymd');
867 $days=array();
868 foreach($linkdates as $linkdate)
869 {
870 $day=substr($linkdate,0,8); // Extract day (without time)
871 if (strcmp($day,$today)<0)
872 {
873 if (empty($days[$day])) $days[$day]=array();
874 $days[$day][]=$linkdate;
875 }
876 if (count($days)>$nb_of_days) break; // Have we collected enough days ?
877 }
878
879 // Build the RSS feed.
880 header('Content-Type: application/rss+xml; charset=utf-8');
881 $pageaddr=htmlspecialchars(indexUrl());
882 echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">';
883 echo '<channel><title>Daily - '.htmlspecialchars($GLOBALS['title']).'</title><link>'.$pageaddr.'</link>';
884 echo '<description>Daily shared links</description><language>en-en</language><copyright>'.$pageaddr.'</copyright>'."\n";
885
886 foreach($days as $day=>$linkdates) // For each day.
887 {
888 $daydate = utf8_encode(strftime('%A %d, %B %Y',linkdate2timestamp($day.'_000000'))); // Full text date
889 $rfc822date = linkdate2rfc822($day.'_000000');
890 $absurl=htmlspecialchars(indexUrl().'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
891 echo '<item><title>'.htmlspecialchars($GLOBALS['title'].' - '.$daydate).'</title><guid>'.$absurl.'</guid><link>'.$absurl.'</link>';
892 echo '<pubDate>'.htmlspecialchars($rfc822date)."</pubDate>";
893
894 // Build the HTML body of this RSS entry.
895 $html='';
896 $href='';
897 $links=array();
898 // We pre-format some fields for proper output.
899 foreach($linkdates as $linkdate)
900 {
901 $l = $LINKSDB[$linkdate];
902 $l['formatedDescription']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($l['description']))));
903 $l['thumbnail'] = thumbnail($l['url']);
904 $l['localdate']=linkdate2locale($l['linkdate']);
905 if (startsWith($l['url'],'?')) $l['url']=indexUrl().$l['url']; // make permalink URL absolute
906 $links[$linkdate]=$l;
907 }
908 // Then build the HTML for this day:
909 $tpl = new RainTPL;
910 $tpl->assign('links',$links);
911 $html = $tpl->draw('dailyrss',$return_string=true);
912 echo "\n";
913 echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
914
915 }
916 echo '</channel></rss>';
917 exit;
918}
919
920
921// ------------------------------------------------------------------------------------------
853// Render HTML page (according to URL parameters and user rights) 922// Render HTML page (according to URL parameters and user rights)
854function renderPage() 923function renderPage()
855{ 924{
@@ -861,7 +930,7 @@ function renderPage()
861 if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli 930 if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli
862 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful. 931 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful.
863 $PAGE = new pageBuilder; 932 $PAGE = new pageBuilder;
864 $PAGE->assign('token',$token); 933 $PAGE->assign('token',$token);
865 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER']:'')); 934 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER']:''));
866 $PAGE->renderPage('loginform'); 935 $PAGE->renderPage('loginform');
867 exit; 936 exit;
@@ -890,17 +959,17 @@ function renderPage()
890 foreach($links as $link) 959 foreach($links as $link)
891 { 960 {
892 $permalink='?'.htmlspecialchars(smallhash($link['linkdate']),ENT_QUOTES); 961 $permalink='?'.htmlspecialchars(smallhash($link['linkdate']),ENT_QUOTES);
893 $thumb=thumbnail($link['url'],$permalink); 962 $thumb=lazyThumbnail($link['url'],$permalink);
894 if ($thumb!='') // Only output links which have a thumbnail. 963 if ($thumb!='') // Only output links which have a thumbnail.
895 { 964 {
896 $link['thumbnail']=$thumb; // Thumbnail HTML code. 965 $link['thumbnail']=$thumb; // Thumbnail HTML code.
897 $link['permalink']=$permalink; 966 $link['permalink']=$permalink;
898 $linksToDisplay[]=$link; // Add to array. 967 $linksToDisplay[]=$link; // Add to array.
899 } 968 }
900 } 969 }
901 $PAGE = new pageBuilder; 970 $PAGE = new pageBuilder;
902 $PAGE->assign('linksToDisplay',$linksToDisplay); 971 $PAGE->assign('linksToDisplay',$linksToDisplay);
903 $PAGE->renderPage('picwall'); 972 $PAGE->renderPage('picwall');
904 exit; 973 exit;
905 } 974 }
906 975
@@ -915,12 +984,12 @@ function renderPage()
915 $tagList=array(); 984 $tagList=array();
916 foreach($tags as $key=>$value) 985 foreach($tags as $key=>$value)
917 { 986 {
918 $tagList[$key] = array('count'=>$value,'size'=>max(40*$value/$maxcount,8)); 987 $tagList[$key] = array('count'=>$value,'size'=>max(40*$value/$maxcount,8));
919 } 988 }
920 $PAGE = new pageBuilder; 989 $PAGE = new pageBuilder;
921 $PAGE->assign('tags',$tagList); 990 $PAGE->assign('tags',$tagList);
922 $PAGE->renderPage('tagcloud'); 991 $PAGE->renderPage('tagcloud');
923 exit; 992 exit;
924 } 993 }
925 994
926 // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...) 995 // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
@@ -960,6 +1029,21 @@ function renderPage()
960 exit; 1029 exit;
961 } 1030 }
962 1031
1032 // -------- User wants to see only private links (toggle)
1033 if (isset($_GET['privateonly']))
1034 {
1035 if (empty($_SESSION['privateonly']))
1036 {
1037 $_SESSION['privateonly']=1; // See only private links
1038 }
1039 else
1040 {
1041 unset($_SESSION['privateonly']); // See all links
1042 }
1043 header('Location: '.(empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']));
1044 exit;
1045 }
1046
963 // --------- Daily (all links form a specific day) ---------------------- 1047 // --------- Daily (all links form a specific day) ----------------------
964 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=daily')) 1048 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=daily'))
965 { 1049 {
@@ -1000,7 +1084,7 @@ function renderPage()
1000 $fill[$index]+=$length; 1084 $fill[$index]+=$length;
1001 } 1085 }
1002 $PAGE = new pageBuilder; 1086 $PAGE = new pageBuilder;
1003 $PAGE->assign('linksToDisplay',$linksToDisplay); 1087 $PAGE->assign('linksToDisplay',$linksToDisplay);
1004 $PAGE->assign('col1',$columns[0]); 1088 $PAGE->assign('col1',$columns[0]);
1005 $PAGE->assign('col2',$columns[1]); 1089 $PAGE->assign('col2',$columns[1]);
1006 $PAGE->assign('col3',$columns[2]); 1090 $PAGE->assign('col3',$columns[2]);
@@ -1021,10 +1105,10 @@ function renderPage()
1021 header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. 1105 header('Location: ?do=login&post='.urlencode($_GET['post']).(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link.
1022 exit; 1106 exit;
1023 } 1107 }
1024 $PAGE = new pageBuilder; 1108 $PAGE = new pageBuilder;
1025 buildLinkList($PAGE); // Compute list of links to display 1109 buildLinkList($PAGE); // Compute list of links to display
1026 $PAGE->renderPage('linklist'); 1110 $PAGE->renderPage('linklist');
1027 exit; // Never remove this one ! All operations below are reserved for logged in user. 1111 exit; // Never remove this one ! All operations below are reserved for logged in user.
1028 } 1112 }
1029 1113
1030 // -------- All other functions are reserved for the registered user: 1114 // -------- All other functions are reserved for the registered user:
@@ -1032,9 +1116,9 @@ function renderPage()
1032 // -------- Display the Tools menu if requested (import/export/bookmarklet...) 1116 // -------- Display the Tools menu if requested (import/export/bookmarklet...)
1033 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tools')) 1117 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tools'))
1034 { 1118 {
1035 $PAGE = new pageBuilder; 1119 $PAGE = new pageBuilder;
1036 $PAGE->assign('pageabsaddr',indexUrl()); 1120 $PAGE->assign('pageabsaddr',indexUrl());
1037 $PAGE->renderPage('tools'); 1121 $PAGE->renderPage('tools');
1038 exit; 1122 exit;
1039 } 1123 }
1040 1124
@@ -1058,10 +1142,10 @@ function renderPage()
1058 } 1142 }
1059 else // show the change password form. 1143 else // show the change password form.
1060 { 1144 {
1061 $PAGE = new pageBuilder; 1145 $PAGE = new pageBuilder;
1062 $PAGE->assign('token',getToken()); 1146 $PAGE->assign('token',getToken());
1063 $PAGE->renderPage('changepassword'); 1147 $PAGE->renderPage('changepassword');
1064 exit; 1148 exit;
1065 } 1149 }
1066 } 1150 }
1067 1151
@@ -1084,15 +1168,15 @@ function renderPage()
1084 } 1168 }
1085 else // Show the configuration form. 1169 else // Show the configuration form.
1086 { 1170 {
1087 $PAGE = new pageBuilder; 1171 $PAGE = new pageBuilder;
1088 $PAGE->assign('token',getToken()); 1172 $PAGE->assign('token',getToken());
1089 $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES)); 1173 $PAGE->assign('title',htmlspecialchars( empty($GLOBALS['title']) ? '' : $GLOBALS['title'] , ENT_QUOTES));
1090 $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES)); 1174 $PAGE->assign('redirector',htmlspecialchars( empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'] , ENT_QUOTES));
1091 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']); 1175 list($timezone_form,$timezone_js) = templateTZform($GLOBALS['timezone']);
1092 $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ? 1176 $PAGE->assign('timezone_form',$timezone_form); // FIXME: put entire tz form generation in template ?
1093 $PAGE->assign('timezone_js',$timezone_js); 1177 $PAGE->assign('timezone_js',$timezone_js);
1094 $PAGE->renderPage('configure'); 1178 $PAGE->renderPage('configure');
1095 exit; 1179 exit;
1096 } 1180 }
1097 } 1181 }
1098 1182
@@ -1101,10 +1185,10 @@ function renderPage()
1101 { 1185 {
1102 if (empty($_POST['fromtag'])) 1186 if (empty($_POST['fromtag']))
1103 { 1187 {
1104 $PAGE = new pageBuilder; 1188 $PAGE = new pageBuilder;
1105 $PAGE->assign('token',getToken()); 1189 $PAGE->assign('token',getToken());
1106 $PAGE->renderPage('changetag'); 1190 $PAGE->renderPage('changetag');
1107 exit; 1191 exit;
1108 } 1192 }
1109 if (!tokenOk($_POST['token'])) die('Wrong token.'); 1193 if (!tokenOk($_POST['token'])) die('Wrong token.');
1110 1194
@@ -1148,8 +1232,8 @@ function renderPage()
1148 // -------- User wants to add a link without using the bookmarklet: show form. 1232 // -------- User wants to add a link without using the bookmarklet: show form.
1149 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) 1233 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink'))
1150 { 1234 {
1151 $PAGE = new pageBuilder; 1235 $PAGE = new pageBuilder;
1152 $PAGE->renderPage('addlink'); 1236 $PAGE->renderPage('addlink');
1153 exit; 1237 exit;
1154 } 1238 }
1155 1239
@@ -1207,12 +1291,12 @@ function renderPage()
1207 { 1291 {
1208 $link = $LINKSDB[$_GET['edit_link']]; // Read database 1292 $link = $LINKSDB[$_GET['edit_link']]; // Read database
1209 if (!$link) { header('Location: ?'); exit; } // Link not found in database. 1293 if (!$link) { header('Location: ?'); exit; } // Link not found in database.
1210 $PAGE = new pageBuilder; 1294 $PAGE = new pageBuilder;
1211 $PAGE->assign('link',$link); 1295 $PAGE->assign('link',$link);
1212 $PAGE->assign('link_is_new',false); 1296 $PAGE->assign('link_is_new',false);
1213 $PAGE->assign('token',getToken()); // XSRF protection. 1297 $PAGE->assign('token',getToken()); // XSRF protection.
1214 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')); 1298 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
1215 $PAGE->renderPage('editlink'); 1299 $PAGE->renderPage('editlink');
1216 exit; 1300 exit;
1217 } 1301 }
1218 1302
@@ -1246,12 +1330,12 @@ function renderPage()
1246 $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>0); 1330 $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>0);
1247 } 1331 }
1248 1332
1249 $PAGE = new pageBuilder; 1333 $PAGE = new pageBuilder;
1250 $PAGE->assign('link',$link); 1334 $PAGE->assign('link',$link);
1251 $PAGE->assign('link_is_new',$link_is_new); 1335 $PAGE->assign('link_is_new',$link_is_new);
1252 $PAGE->assign('token',getToken()); // XSRF protection. 1336 $PAGE->assign('token',getToken()); // XSRF protection.
1253 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')); 1337 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
1254 $PAGE->renderPage('editlink'); 1338 $PAGE->renderPage('editlink');
1255 exit; 1339 exit;
1256 } 1340 }
1257 1341
@@ -1260,9 +1344,9 @@ function renderPage()
1260 { 1344 {
1261 if (empty($_GET['what'])) 1345 if (empty($_GET['what']))
1262 { 1346 {
1263 $PAGE = new pageBuilder; 1347 $PAGE = new pageBuilder;
1264 $PAGE->renderPage('export'); 1348 $PAGE->renderPage('export');
1265 exit; 1349 exit;
1266 } 1350 }
1267 $exportWhat=$_GET['what']; 1351 $exportWhat=$_GET['what'];
1268 if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???'); 1352 if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???');
@@ -1313,10 +1397,10 @@ HTML;
1313 // -------- Show upload/import dialog: 1397 // -------- Show upload/import dialog:
1314 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=import')) 1398 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=import'))
1315 { 1399 {
1316 $PAGE = new pageBuilder; 1400 $PAGE = new pageBuilder;
1317 $PAGE->assign('token',getToken()); 1401 $PAGE->assign('token',getToken());
1318 $PAGE->assign('maxfilesize',getMaxFileSize()); 1402 $PAGE->assign('maxfilesize',getMaxFileSize());
1319 $PAGE->renderPage('import'); 1403 $PAGE->renderPage('import');
1320 exit; 1404 exit;
1321 } 1405 }
1322 1406
@@ -1430,14 +1514,24 @@ function buildLinkList($PAGE)
1430 $search_crits=explode(' ',trim($_GET['searchtags'])); 1514 $search_crits=explode(' ',trim($_GET['searchtags']));
1431 $search_type='tags'; 1515 $search_type='tags';
1432 } 1516 }
1433 elseif (isset($_SERVER["QUERY_STRING"]) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER["QUERY_STRING"])) // Detect smallHashes in URL 1517 elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/',$_SERVER['QUERY_STRING'])) // Detect smallHashes in URL
1434 { 1518 {
1435 $linksToDisplay = $LINKSDB->filterSmallHash(substr(trim($_SERVER["QUERY_STRING"], '/'),0,6)); 1519 $linksToDisplay = $LINKSDB->filterSmallHash(substr(trim($_SERVER["QUERY_STRING"], '/'),0,6));
1436 $search_type='permalink'; 1520 $search_type='permalink';
1437 } 1521 }
1438 else 1522 else
1439 $linksToDisplay = $LINKSDB; // otherwise, display without filtering. 1523 $linksToDisplay = $LINKSDB; // otherwise, display without filtering.
1440 1524
1525 // Option: Show only private links
1526 if (!empty($_SESSION['privateonly']))
1527 {
1528 $tmp = array();
1529 foreach($linksToDisplay as $linkdate=>$link)
1530 {
1531 if ($link['private']!=0) $tmp[$linkdate]=$link;
1532 }
1533 $linksToDisplay=$tmp;
1534 }
1441 1535
1442 // ---- Handle paging. 1536 // ---- Handle paging.
1443 /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ??? 1537 /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess ???
@@ -1494,16 +1588,18 @@ function buildLinkList($PAGE)
1494 return; 1588 return;
1495} 1589}
1496 1590
1497// Returns the HTML code to display a thumbnail for a link 1591// Compute the thumbnail for a link.
1592//
1498// with a link to the original URL. 1593// with a link to the original URL.
1499// Understands various services (youtube.com...) 1594// Understands various services (youtube.com...)
1500// Input: $url = url for which the thumbnail must be found. 1595// Input: $url = url for which the thumbnail must be found.
1501// $href = if provided, this URL will be followed instead of $url 1596// $href = if provided, this URL will be followed instead of $url
1502// Returns '' if no thumbnail available. 1597// Returns an associative array with thumbnail attributes (src,href,width,height,style,alt)
1503function thumbnail($url,$href=false) 1598// Some of them may be missing.
1599// Return an empty array if no thumbnail available.
1600function computeThumbnail($url,$href=false)
1504{ 1601{
1505 if (!$GLOBALS['config']['ENABLE_THUMBNAILS']) return ''; 1602 if (!$GLOBALS['config']['ENABLE_THUMBNAILS']) return array();
1506
1507 if ($href==false) $href=$url; 1603 if ($href==false) $href=$url;
1508 1604
1509 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. 1605 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link.
@@ -1513,38 +1609,47 @@ function thumbnail($url,$href=false)
1513 if ($domain=='youtube.com' || $domain=='www.youtube.com') 1609 if ($domain=='youtube.com' || $domain=='www.youtube.com')
1514 { 1610 {
1515 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail 1611 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail
1516 if (!empty($params['v'])) return '<a href="'.htmlspecialchars($href).'"><img src="http://img.youtube.com/vi/'.htmlspecialchars($params['v']).'/default.jpg" width="120" height="90" alt="YouTube thumbnail"></a>'; 1612 if (!empty($params['v'])) return array('src'=>'http://img.youtube.com/vi/'.$params['v'].'/default.jpg',
1613 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
1517 } 1614 }
1518 if ($domain=='youtu.be') // Youtube short links 1615 if ($domain=='youtu.be') // Youtube short links
1519 { 1616 {
1520 $path = parse_url($url,PHP_URL_PATH); 1617 $path = parse_url($url,PHP_URL_PATH);
1521 return '<a href="'.htmlspecialchars($href).'"><img src="http://img.youtube.com/vi'.htmlspecialchars($path).'/default.jpg" width="120" height="90" alt="YouTube thumbnail"></a>'; 1618 return array('src'=>'http://img.youtube.com/vi'.$path.'/default.jpg',
1619 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
1522 } 1620 }
1523 if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting 1621 if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting
1524 { 1622 {
1525 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename. 1623 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename.
1526 if (!empty($params) && !empty($params['img'])) return '<a href="'.htmlspecialchars($href).'"><img src="http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']).'" style:"max-width:120px; max-height:150px" alt="pix.toile-libre.org thumbnail"></a>'; 1624 if (!empty($params) && !empty($params['img'])) return array('src'=>'http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']),
1625 'href'=>$href,'style'=>'max-width:120px; max-height:150px','alt'=>'pix.toile-libre.org thumbnail');
1527 } 1626 }
1528 1627
1529 if ($domain=='imgur.com') 1628 if ($domain=='imgur.com')
1530 { 1629 {
1531 $path = parse_url($url,PHP_URL_PATH); 1630 $path = parse_url($url,PHP_URL_PATH);
1532 if (startsWith($path,'/a/')) return ''; // Thumbnails for albums are not available. 1631 if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available.
1533 if (startsWith($path,'/r/')) return '<a href="'.htmlspecialchars($href).'"><img src="http://i.imgur.com/'.htmlspecialchars(basename($path)).'s.jpg" width="90" height="90" alt="imgur.com thumbnail"></a>'; 1632 if (startsWith($path,'/r/')) return array('src'=>'http://i.imgur.com/'.basename($path).'s.jpg',
1534 if (startsWith($path,'/gallery/')) return '<a href="'.htmlspecialchars($href).'"><img src="http://i.imgur.com'.htmlspecialchars(substr($path,8)).'s.jpg" width="90" height="90" alt="imgur.com thumbnail"></a>'; 1633 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1535 if (substr_count($path,'/')==1) return '<a href="'.htmlspecialchars($href).'"><img src="http://i.imgur.com/'.htmlspecialchars(substr($path,1)).'s.jpg" width="90" height="90" alt="imgur.com thumbnail"></a>'; 1634 if (startsWith($path,'/gallery/')) return array('src'=>'http://i.imgur.com'.substr($path,8).'s.jpg',
1635 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1636
1637 if (substr_count($path,'/')==1) return array('src'=>'http://i.imgur.com/'.substr($path,1).'s.jpg',
1638 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1536 } 1639 }
1537 if ($domain=='i.imgur.com') 1640 if ($domain=='i.imgur.com')
1538 { 1641 {
1539 $pi = pathinfo(parse_url($url,PHP_URL_PATH)); 1642 $pi = pathinfo(parse_url($url,PHP_URL_PATH));
1540 if (!empty($pi['filename'])) return '<a href="'.htmlspecialchars($href).'"><img src="http://i.imgur.com/'.htmlspecialchars($pi['filename']).'s.jpg" width="90" height="90" alt="imgur.com thumbnail"></a>'; 1643 if (!empty($pi['filename'])) return array('src'=>'http://i.imgur.com/'.$pi['filename'].'s.jpg',
1644 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1541 } 1645 }
1542 if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') 1646 if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com')
1543 { 1647 {
1544 if (strpos($url,'dailymotion.com/video/')!==false) 1648 if (strpos($url,'dailymotion.com/video/')!==false)
1545 { 1649 {
1546 $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url); 1650 $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url);
1547 return '<a href="'.htmlspecialchars($href).'"><img src="'.htmlspecialchars($thumburl).'" width="120" style="height:auto;" alt="DailyMotion thumbnail"></a>'; 1651 return array('src'=>$thumburl,
1652 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'DailyMotion thumbnail');
1548 } 1653 }
1549 } 1654 }
1550 if (endsWith($domain,'.imageshack.us')) 1655 if (endsWith($domain,'.imageshack.us'))
@@ -1553,7 +1658,8 @@ function thumbnail($url,$href=false)
1553 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') 1658 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
1554 { 1659 {
1555 $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext; 1660 $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext;
1556 return '<a href="'.htmlspecialchars($href).'"><img src="'.htmlspecialchars($thumburl).'" width="120" style="height:auto;" alt="imageshack.us thumbnail"></a>'; 1661 return array('src'=>$thumburl,
1662 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'imageshack.us thumbnail');
1557 } 1663 }
1558 } 1664 }
1559 1665
@@ -1561,7 +1667,7 @@ function thumbnail($url,$href=false)
1561 // So we deport the thumbnail generation in order not to slow down page generation 1667 // So we deport the thumbnail generation in order not to slow down page generation
1562 // (and we also cache the thumbnail) 1668 // (and we also cache the thumbnail)
1563 1669
1564 if (!$GLOBALS['config']['ENABLE_LOCALCACHE']) return ''; // If local cache is disabled, no thumbnails for services which require the use a local cache. 1670 if (!$GLOBALS['config']['ENABLE_LOCALCACHE']) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache.
1565 1671
1566 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') 1672 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')
1567 || $domain=='vimeo.com' 1673 || $domain=='vimeo.com'
@@ -1572,20 +1678,21 @@ function thumbnail($url,$href=false)
1572 if ($domain=='vimeo.com') 1678 if ($domain=='vimeo.com')
1573 { // Make sure this vimeo url points to a video (/xxx... where xxx is numeric) 1679 { // Make sure this vimeo url points to a video (/xxx... where xxx is numeric)
1574 $path = parse_url($url,PHP_URL_PATH); 1680 $path = parse_url($url,PHP_URL_PATH);
1575 if (!preg_match('!/\d+.+?!',$path)) return ''; // This is not a single video URL. 1681 if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL.
1576 } 1682 }
1577 if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) 1683 if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com'))
1578 { // Make sure this url points to a single comic (/xxx... where xxx is numeric) 1684 { // Make sure this url points to a single comic (/xxx... where xxx is numeric)
1579 $path = parse_url($url,PHP_URL_PATH); 1685 $path = parse_url($url,PHP_URL_PATH);
1580 if (!preg_match('!/\d+.+?!',$path)) return ''; 1686 if (!preg_match('!/\d+.+?!',$path)) return array();
1581 } 1687 }
1582 if ($domain=='ted.com' || endsWith($domain,'.ted.com')) 1688 if ($domain=='ted.com' || endsWith($domain,'.ted.com'))
1583 { // Make sure this TED url points to a video (/talks/...) 1689 { // Make sure this TED url points to a video (/talks/...)
1584 $path = parse_url($url,PHP_URL_PATH); 1690 $path = parse_url($url,PHP_URL_PATH);
1585 if ("/talks/" !== substr($path,0,7)) return ''; // This is not a single video URL. 1691 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
1586 } 1692 }
1587 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) 1693 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
1588 return '<a href="'.htmlspecialchars($href).'"><img src="?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url).'" width="120" style="height:auto;" alt="thumbnail"></a>'; 1694 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url),
1695 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1589 } 1696 }
1590 1697
1591 // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif 1698 // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif
@@ -1595,12 +1702,68 @@ function thumbnail($url,$href=false)
1595 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') 1702 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
1596 { 1703 {
1597 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation) 1704 $sign = hash_hmac('sha256', $url, $GLOBALS['salt']); // We use the salt to sign data (it's random, secret, and specific to each installation)
1598 return '<a href="'.htmlspecialchars($href).'"><img src="?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url).'" width="120" style="height:auto;"></a>'; 1705 return array('src'=>indexUrl().'?do=genthumbnail&hmac='.htmlspecialchars($sign).'&url='.urlencode($url),
1706 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1599 } 1707 }
1600 return ''; // No thumbnail. 1708 return array(); // No thumbnail.
1709
1710}
1711
1712
1713// Returns the HTML code to display a thumbnail for a link
1714// with a link to the original URL.
1715// Understands various services (youtube.com...)
1716// Input: $url = url for which the thumbnail must be found.
1717// $href = if provided, this URL will be followed instead of $url
1718// Returns '' if no thumbnail available.
1719function thumbnail($url,$href=false)
1720{
1721 $t = computeThumbnail($url,$href);
1722 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1723
1724 $html='<a href="'.htmlspecialchars($t['href']).'"><img src="'.htmlspecialchars($t['src']).'"';
1725 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
1726 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
1727 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
1728 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
1729 $html.='></a>';
1730 return $html;
1731}
1732
1601 1733
1734// Returns the HTML code to display a thumbnail for a link
1735// for the picture wall (using lazy image loading)
1736// Understands various services (youtube.com...)
1737// Input: $url = url for which the thumbnail must be found.
1738// $href = if provided, this URL will be followed instead of $url
1739// Returns '' if no thumbnail available.
1740function lazyThumbnail($url,$href=false)
1741{
1742 $t = computeThumbnail($url,$href);
1743 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1744
1745 $html='<a href="'.htmlspecialchars($t['href']).'">';
1746
1747 // Lazy image (only loaded by javascript when in the viewport).
1748 $html.='<img class="lazyimage" src="#" data-original="'.htmlspecialchars($t['src']).'"';
1749 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
1750 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
1751 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
1752 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
1753 $html.='>';
1754
1755 // No-javascript fallback:
1756 $html.='<noscript><img src="'.htmlspecialchars($t['src']).'"';
1757 if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
1758 if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
1759 if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
1760 if (!empty($t['alt'])) $html.=' alt="'.htmlspecialchars($t['alt']).'"';
1761 $html.='></noscript></a>';
1762
1763 return $html;
1602} 1764}
1603 1765
1766
1604// ----------------------------------------------------------------------------------------------- 1767// -----------------------------------------------------------------------------------------------
1605// Installation 1768// Installation
1606// This function should NEVER be called if the file data/config.php exists. 1769// This function should NEVER be called if the file data/config.php exists.
@@ -1861,11 +2024,11 @@ function genThumbnail()
1861 // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page 2024 // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page
1862 // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html 2025 // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html
1863 // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" /> 2026 // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" />
1864 list($httpstatus,$headers,$data) = getHTTP($url,5); 2027 list($httpstatus,$headers,$data) = getHTTP($url,5);
1865 if (strpos($httpstatus,'200 OK')!==false) 2028 if (strpos($httpstatus,'200 OK')!==false)
1866 { 2029 {
1867 // Extract the link to the thumbnail 2030 // Extract the link to the thumbnail
1868 preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)[^s]!',$data,$matches); 2031 preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!',$data,$matches);
1869 if (!empty($matches[1])) 2032 if (!empty($matches[1]))
1870 { // Let's download the image. 2033 { // Let's download the image.
1871 $imageurl=$matches[1]; 2034 $imageurl=$matches[1];
@@ -1982,6 +2145,7 @@ if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=g
1982$LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in). 2145$LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
1983if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) 2146if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI)
1984if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE']; 2147if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE'];
2148if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=dailyrss')) { showDailyRSS(); exit; }
1985if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; } 2149if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; }
1986if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; } 2150if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; }
1987renderPage(); 2151renderPage();
diff --git a/tpl/daily.html b/tpl/daily.html
index 9ee74739..bdaf2032 100644
--- a/tpl/daily.html
+++ b/tpl/daily.html
@@ -7,14 +7,15 @@
7 <div class="dailyAbout"> 7 <div class="dailyAbout">
8 All links of one day<br>in a single page.<br> 8 All links of one day<br>in a single page.<br>
9 <a href="?do=daily&day={$previousday}"><b>&lt;</b>Previous day</a> - 9 <a href="?do=daily&day={$previousday}"><b>&lt;</b>Previous day</a> -
10 <a href="?do=daily&day={$nextday}">Next day<b>&gt;</b></a> 10 <a href="?do=daily&day={$nextday}">Next day<b>&gt;</b></a><br><br>
11 <a href="?do=dailyrss" title="1 RSS entry per day"><img src="images/feed-icon-14x14.png#" width="14" height="14" style="position:relative;top:3px; margin-right:4px;">Daily RSS Feed</a>
11 </div> 12 </div>
12 <div class="dailyTitle"><img src="../images/floral_left.png" width="51" height="50"> The Shaarli Daily <img src="../images/floral_right.png" width="51" height="50"></div> 13 <div class="dailyTitle"><img src="../images/floral_left.png" width="51" height="50" class="nomobile"> The Shaarli Daily <img src="../images/floral_right.png" width="51" height="50" class="nomobile"></div>
13 <div class="dailyDate">&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097; {$day} &#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;</div> 14 <div class="dailyDate"><span class="nomobile">&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;</span> {$day} <span class="nomobile">&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;&#x0097;</span></div>
14 <div style="clear:both;"></div> 15 <div style="clear:both;"></div>
15 16
16 {if="$linksToDisplay"} 17 {if="$linksToDisplay"}
17 <div style="float:left;position:relative; width:33%; padding-left:1%;"> 18 <div id="daily_col1">
18 {loop="col1"} 19 {loop="col1"}
19 <div class="dailyEntry"> 20 <div class="dailyEntry">
20 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div> 21 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div>
@@ -26,7 +27,7 @@
26 {/loop} 27 {/loop}
27 </div> 28 </div>
28 29
29 <div style="float:left;position:relative; width:33%;"> 30 <div id="daily_col2">
30 {loop="col2"} 31 {loop="col2"}
31 <div class="dailyEntry"> 32 <div class="dailyEntry">
32 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div> 33 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div>
@@ -38,7 +39,7 @@
38 {/loop} 39 {/loop}
39 </div> 40 </div>
40 41
41 <div style="float:left;position:relative; width:33%;"> 42 <div id="daily_col3">
42 {loop="col3"} 43 {loop="col3"}
43 <div class="dailyEntry"> 44 <div class="dailyEntry">
44 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div> 45 <div style="float:right;position:relative;top:-1px;"><a href="?{$value.linkdate|smallHash}"><img src="../images/squiggle2.png" width="25" height="26" title="permalink" alt="permalink"></a></div>
diff --git a/tpl/dailyrss.html b/tpl/dailyrss.html
new file mode 100644
index 00000000..436e1cd2
--- /dev/null
+++ b/tpl/dailyrss.html
@@ -0,0 +1,8 @@
1{loop="links"}
2 <h3><a href="{$value.url}">{$value.title|htmlspecialchars}</a></h3>
3 <small>{if="!$GLOBALS['config']['HIDE_TIMESTAMPS']"}{$value.localdate|htmlspecialchars} - {/if}{if="$value.tags"}{$value.tags|htmlspecialchars}{/if}<br>
4 {$value.url|htmlspecialchars}</small><br>
5 {if="$value.thumbnail"}{$value.thumbnail}{/if}<br>
6 {if="$value.description"}{$value.formatedDescription}{/if}
7 <br><br><hr>
8{/loop} \ No newline at end of file
diff --git a/tpl/includes.html b/tpl/includes.html
index 7cfb07f2..5319f452 100644
--- a/tpl/includes.html
+++ b/tpl/includes.html
@@ -1,5 +1,7 @@
1<title>{$pagetitle}</title> 1<title>{$pagetitle}</title>
2<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 2<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
3<meta name="format-detection" content="telephone=no" />
4<meta name="viewport" content="width=device-width,initial-scale=1.0" />
3<link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" /> 5<link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" />
4<link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" /> 6<link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" />
5<link href="images/favicon.ico#" rel="shortcut icon" type="image/x-icon" /> 7<link href="images/favicon.ico#" rel="shortcut icon" type="image/x-icon" />
diff --git a/tpl/linklist.html b/tpl/linklist.html
index dc5b5398..1067d6fc 100644
--- a/tpl/linklist.html
+++ b/tpl/linklist.html
@@ -5,8 +5,8 @@
5<div id="pageheader"> 5<div id="pageheader">
6 {include="page.header"} 6 {include="page.header"}
7 <div id="headerform" style="width:100%; white-space:nowrap;"> 7 <div id="headerform" style="width:100%; white-space:nowrap;">
8 <form method="GET" class="searchform" name="searchform" style="display:inline;"><input type="text" name="searchterm" style="width:30%" value=""> <input type="submit" value="Search" class="bigbutton"></form> 8 <form method="GET" class="searchform" name="searchform" style="display:inline;"><input type="text" id="searchform_value" name="searchterm" style="width:30%" value=""> <input type="submit" value="Search" class="bigbutton"></form>
9 <form method="GET" class="tagfilter" name="tagfilter" style="display:inline;margin-left:24px;"><input type="text" name="searchtags" id="searchtags" style="width:10%" value=""> <input type="submit" value="Filter by tag" class="bigbutton"></form> 9 <form method="GET" class="tagfilter" name="tagfilter" style="display:inline;margin-left:24px;"><input type="text" name="searchtags" id="tagfilter_value" style="width:10%" value=""> <input type="submit" value="Filter by tag" class="bigbutton"></form>
10 </div> 10 </div>
11</div> 11</div>
12 12
@@ -60,16 +60,14 @@
60 60
61 {include="page.footer"} 61 {include="page.footer"}
62<script> 62<script>
63$('a.qrcode').click(function(){ 63$(document).ready(function() {
64 hide_qrcode(); 64 $('a.qrcode').click(function(){
65 var link = $(this).attr('href'); 65 hide_qrcode();
66 $(this).after('<div class="qrcode" onclick="hide_qrcode();return false;"><img src="'+link+'#" width="200" height="200"><br>click to close</div>'); 66 var link = $(this).attr('href');
67 $(this).after('<div class="qrcode" onclick="hide_qrcode();return false;"><img src="'+link+'#" width="200" height="200"><br>click to close</div>');
68 });
67}); 69});
68 70function hide_qrcode() { $('div.qrcode').remove(); }
69function hide_qrcode()
70{
71 $('div.qrcode').remove();
72}
73</script> 71</script>
74</body> 72</body>
75</html> \ No newline at end of file 73</html> \ No newline at end of file
diff --git a/tpl/linklist.paging.html b/tpl/linklist.paging.html
index 27e7440d..b1f9871f 100644
--- a/tpl/linklist.paging.html
+++ b/tpl/linklist.paging.html
@@ -1,9 +1,14 @@
1 <div class="paging"> 1<div class="paging">
2 <div style="float:right; padding-right:5px;"> 2{if="isLoggedIn()"}
3 Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a> 3 <div id="paging_privatelinks">
4 <form method="GET" style="display:inline;" class="linksperpage"><input type="text" name="linksperpage" size="2" style="height:15px;"></form> 4 <a href="?privateonly"><img src="images/private_16x16.png#" width="16" height="16" title="See private links only (toggle)" alt="See private links only (toggle)"></a>
5 </div> 5 </div>
6 {if="$previous_page_url"} <a href="{$previous_page_url}">&#x25C4;Older</a> {/if} 6{/if}
7 <span style="color:#fff; padding:0 20 0 20;">page {$page_current} / {$page_max} </span> 7 <div id="paging_linksperpage">
8 {if="$next_page_url"} <a href="{$next_page_url}">Newer&#x25BA;</a> {/if} 8 Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a>
9 </div> \ No newline at end of file 9 <form method="GET" style="display:inline;" class="linksperpage"><input type="text" name="linksperpage" size="2" style="height:15px;"></form>
10 </div>
11 {if="$previous_page_url"} <a href="{$previous_page_url}" id="paging_older">&#x25C4;Older</a> {/if}
12 <div id="paging_current">page {$page_current} / {$page_max} </div>
13 {if="$next_page_url"} <a href="{$next_page_url}" id="paging_newer">Newer&#x25BA;</a> {/if}
14</div> \ No newline at end of file
diff --git a/tpl/page.header.html b/tpl/page.header.html
index 0c304367..3ad2f205 100644
--- a/tpl/page.header.html
+++ b/tpl/page.header.html
@@ -1,12 +1,12 @@
1 1
2 <div id="logo" title="Share your links !" onclick="document.location='?';"></div> 2 <div id="logo" title="Share your links !" onclick="document.location='?';"></div>
3 <div style="float:right; font-style:italic; color:#bbb; text-align:right; padding:0 5 0 0;">Shaare your links...<br>{$linkcount} links</div> 3 <div style="float:right; font-style:italic; color:#bbb; text-align:right; padding:0 5 0 0;" class="nomobile">Shaare your links...<br>{$linkcount} links</div>
4 <span id="shaarli_title"><a href="?">{$shaarlititle}</a></span> 4 <span id="shaarli_title"><a href="?">{$shaarlititle}</a></span>
5 5
6{if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"} 6{if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"}
7 {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore} 7 {ignore} When called as a popup from bookmarklet, do not display menu. {/ignore}
8{else} 8{else}
9 <a href="?">Home</a> 9 <a href="?" class="nomobile">Home</a>
10 {if="isLoggedIn()"} 10 {if="isLoggedIn()"}
11 <a href="?do=logout">Logout</a><a href="?do=tools">Tools</a><a href="?do=addlink"><b>Add link</b></a> 11 <a href="?do=logout">Logout</a><a href="?do=tools">Tools</a><a href="?do=addlink"><b>Add link</b></a>
12 {elseif="$GLOBALS['config']['OPEN_SHAARLI']"} 12 {elseif="$GLOBALS['config']['OPEN_SHAARLI']"}
@@ -14,8 +14,8 @@
14 {else} 14 {else}
15 <a href="?do=login">Login</a> 15 <a href="?do=login">Login</a>
16 {/if} 16 {/if}
17 <a href="{$feedurl}?do=rss{$searchcrits}">RSS Feed</a> 17 <a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a>
18 <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;">ATOM Feed</a> 18 <a href="{$feedurl}?do=atom{$searchcrits}" style="padding-left:10px;" class="nomobile">ATOM Feed</a>
19 <a href="?do=tagcloud">Tag cloud</a> 19 <a href="?do=tagcloud">Tag cloud</a>
20 <a href="?do=picwall{$searchcrits}">Picture wall</a> 20 <a href="?do=picwall{$searchcrits}">Picture wall</a>
21 <a href="?do=daily">Daily</a> 21 <a href="?do=daily">Daily</a>
diff --git a/tpl/picwall.html b/tpl/picwall.html
index da6df762..2083a629 100644
--- a/tpl/picwall.html
+++ b/tpl/picwall.html
@@ -1,6 +1,8 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html>
3<head>{include="includes"}</head> 3<head>{include="includes"}
4<script src="inc/jquery.lazyload.min.js#"></script>
5</head>
4<body> 6<body>
5<div id="pageheader">{include="page.header"}</div> 7<div id="pageheader">{include="page.header"}</div>
6<center> 8<center>
@@ -14,4 +16,9 @@
14</center> 16</center>
15{include="page.footer"} 17{include="page.footer"}
16</body> 18</body>
19<script>
20$(document).ready(function() {
21 $("img.lazyimage").show().lazyload();
22});
23</script>
17</html> \ No newline at end of file 24</html> \ No newline at end of file