diff options
-rw-r--r-- | index.php | 96 |
1 files changed, 72 insertions, 24 deletions
@@ -1,5 +1,5 @@ | |||
1 | <?php | 1 | <?php |
2 | // Shaarli 0.0.12 beta - Shaare your links... | 2 | // Shaarli 0.0.13 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 |
@@ -16,16 +16,17 @@ define('IPBANS_FILENAME',DATADIR.'/ipbans.php'); // File storage for failures an | |||
16 | define('BAN_AFTER',4); // Ban IP after this many failures. | 16 | define('BAN_AFTER',4); // Ban IP after this many failures. |
17 | define('BAN_DURATION',1800); // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) | 17 | define('BAN_DURATION',1800); // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes) |
18 | define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login | 18 | define('OPEN_SHAARLI',false); // If true, anyone can add/edit/delete links without having to login |
19 | |||
20 | |||
21 | // ----------------------------------------------------------------------------------------------- | ||
22 | // Program config (touch at your own risks !) | ||
19 | if (get_magic_quotes_gpc()) | 23 | if (get_magic_quotes_gpc()) |
20 | { | 24 | { |
21 | header('Content-Type: text/plain; charset=utf-8'); | 25 | header('Content-Type: text/plain; charset=utf-8'); |
22 | echo "ERROR: magic_quotes_gpc is ON in your php config. This is *BAD*. You *MUST* disable it, either by changing the value in php.ini,\n"; | 26 | echo "ERROR: magic_quotes_gpc is ON in your php config. This is *BAD*. You *MUST* disable it, either by changing the value in php.ini,\n"; |
23 | echo "or by adding the following line in .htaccess: php_flag magic_quotes_gpc Off"; exit; | 27 | echo "or by adding ONE the following line in .htaccess (depending on your host):\n\nphp_flag magic_quotes_gpc Off\nor\nSetEnv MAGIC_QUOTES 0"; exit; |
24 | } | 28 | } |
25 | checkphpversion(); | 29 | checkphpversion(); |
26 | |||
27 | // ----------------------------------------------------------------------------------------------- | ||
28 | // Program config (touch at your own risks !) | ||
29 | error_reporting(E_ALL^E_WARNING); // See all error except warnings. | 30 | error_reporting(E_ALL^E_WARNING); // See all error except warnings. |
30 | //error_reporting(-1); // See all errors (for debugging only) | 31 | //error_reporting(-1); // See all errors (for debugging only) |
31 | $STARTTIME = microtime(true); // Measure page execution time. | 32 | $STARTTIME = microtime(true); // Measure page execution time. |
@@ -35,7 +36,7 @@ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); | |||
35 | header("Cache-Control: no-store, no-cache, must-revalidate"); | 36 | header("Cache-Control: no-store, no-cache, must-revalidate"); |
36 | header("Cache-Control: post-check=0, pre-check=0", false); | 37 | header("Cache-Control: post-check=0, pre-check=0", false); |
37 | header("Pragma: no-cache"); | 38 | header("Pragma: no-cache"); |
38 | define('shaarli_version','0.0.12 beta'); | 39 | define('shaarli_version','0.0.13 beta'); |
39 | if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); } | 40 | if (!is_dir(DATADIR)) { mkdir(DATADIR,0705); chmod(DATADIR,0705); } |
40 | if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. | 41 | if (!is_file(DATADIR.'/.htaccess')) { file_put_contents(DATADIR.'/.htaccess',"Allow from none\nDeny from all\n"); } // Protect data files. |
41 | if (!is_file(CONFIG_FILE)) install(); | 42 | if (!is_file(CONFIG_FILE)) install(); |
@@ -58,7 +59,9 @@ function checkphpversion() | |||
58 | { | 59 | { |
59 | list($match,$major,$minor,$release) = $matches; | 60 | list($match,$major,$minor,$release) = $matches; |
60 | if ($major>=5 && $minor>=1) return; // 5.1.x or higher is ok. | 61 | if ($major>=5 && $minor>=1) return; // 5.1.x or higher is ok. |
61 | die('Your server supports php '.$ver.'. Shaarli requires at last php 5.1, and thus cannot run. Sorry.'); | 62 | header('Content-Type: text/plain; charset=utf-8'); |
63 | echo 'Your server supports php '.$ver.'. Shaarli requires at last php 5.1, and thus cannot run. Sorry.'; | ||
64 | exit; | ||
62 | } | 65 | } |
63 | // if cannot check php version... well, at your own risks. | 66 | // if cannot check php version... well, at your own risks. |
64 | } | 67 | } |
@@ -516,8 +519,8 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
516 | $tags=array(); | 519 | $tags=array(); |
517 | foreach($this->links as $link) | 520 | foreach($this->links as $link) |
518 | foreach(explode(' ',$link['tags']) as $tag) | 521 | foreach(explode(' ',$link['tags']) as $tag) |
519 | if (!empty($tag)) $tags[$tag]=0; | 522 | if (!empty($tag)) $tags[$tag]=(empty($tags[$tag]) ? 1 : $tags[$tag]+1); |
520 | ksort($tags); // FIXME: sort by usage ? That would be better. | 523 | arsort($tags); // Sort tags by usage (most used tag first) |
521 | return $tags; | 524 | return $tags; |
522 | } | 525 | } |
523 | 526 | ||
@@ -528,16 +531,23 @@ class linkdb implements Iterator, Countable, ArrayAccess | |||
528 | function showRSS() | 531 | function showRSS() |
529 | { | 532 | { |
530 | global $LINKSDB; | 533 | global $LINKSDB; |
534 | |||
535 | // Optionnaly filter the results: | ||
536 | $linksToDisplay=array(); | ||
537 | if (!empty($_GET['searchterm'])) $linksToDisplay = $LINKSDB->filterFulltext($_GET['searchterm']); | ||
538 | elseif (!empty($_GET['searchtags'])) $linksToDisplay = $LINKSDB->filterTags($_GET['searchtags']); | ||
539 | else $linksToDisplay = $LINKSDB; | ||
540 | |||
531 | header('Content-Type: application/xhtml+xml; charset=utf-8'); | 541 | header('Content-Type: application/xhtml+xml; charset=utf-8'); |
532 | $pageaddr=htmlspecialchars(serverUrl().$_SERVER["SCRIPT_NAME"]); | 542 | $pageaddr=htmlspecialchars(serverUrl().$_SERVER["SCRIPT_NAME"]); |
533 | echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">'; | 543 | echo '<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">'; |
534 | echo '<channel><title>Shared links on '.$pageaddr.'</title><link>'.$pageaddr.'</link>'; | 544 | echo '<channel><title>Shared links on '.$pageaddr.'</title><link>'.$pageaddr.'</link>'; |
535 | echo '<description>Shared links</description><language></language><copyright>'.$pageaddr.'</copyright>'."\n\n"; | 545 | echo '<description>Shared links</description><language></language><copyright>'.$pageaddr.'</copyright>'."\n\n"; |
536 | $i=0; | 546 | $i=0; |
537 | $keys=array(); foreach($LINKSDB as $key=>$value) { $keys[]=$key; } // No, I can't use array_keys(). | 547 | $keys=array(); foreach($linksToDisplay as $key=>$value) { $keys[]=$key; } // No, I can't use array_keys(). |
538 | while ($i<50 && $i<count($keys)) | 548 | while ($i<50 && $i<count($keys)) |
539 | { | 549 | { |
540 | $link = $LINKSDB[$keys[$i]]; | 550 | $link = $linksToDisplay[$keys[$i]]; |
541 | $rfc822date = linkdate2rfc822($link['linkdate']); | 551 | $rfc822date = linkdate2rfc822($link['linkdate']); |
542 | echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid>'.htmlspecialchars($link['url']).'</guid><link>'.htmlspecialchars($link['url']).'</link><pubDate>'.htmlspecialchars($rfc822date).'</pubDate>'; | 552 | echo '<item><title>'.htmlspecialchars($link['title']).'</title><guid>'.htmlspecialchars($link['url']).'</guid><link>'.htmlspecialchars($link['url']).'</link><pubDate>'.htmlspecialchars($rfc822date).'</pubDate>'; |
543 | echo '<description><![CDATA['.htmlspecialchars($link['description']).']]></description></item>'."\n"; | 553 | echo '<description><![CDATA['.htmlspecialchars($link['description']).']]></description></item>'."\n"; |
@@ -579,6 +589,7 @@ function renderPage() | |||
579 | // -------- User wants to logout. | 589 | // -------- User wants to logout. |
580 | if (startswith($_SERVER["QUERY_STRING"],'do=logout')) | 590 | if (startswith($_SERVER["QUERY_STRING"],'do=logout')) |
581 | { | 591 | { |
592 | invalidateCaches(); | ||
582 | logout(); | 593 | logout(); |
583 | header('Location: ?'); | 594 | header('Location: ?'); |
584 | exit; | 595 | exit; |
@@ -654,10 +665,10 @@ HTML; | |||
654 | $pageabsaddr=serverUrl().$_SERVER["SCRIPT_NAME"]; // Why doesn't php have a built-in function for that ? | 665 | $pageabsaddr=serverUrl().$_SERVER["SCRIPT_NAME"]; // Why doesn't php have a built-in function for that ? |
655 | // The javascript code for the bookmarklet: | 666 | // The javascript code for the bookmarklet: |
656 | $toolbar= <<<HTML | 667 | $toolbar= <<<HTML |
657 | <div id="headerform"> | 668 | <div id="headerform"><br> |
658 | <a href="?do=import"><b>Import</b></a> - <small>Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</small><br> | 669 | <a href="?do=import"><b>Import</b></a> - Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)<br><br> |
659 | <a href="?do=export"><b>Export</b></a> - <small>Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</small><br> | 670 | <a href="?do=export"><b>Export</b></a> - Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)<br><br> |
660 | <a class="smallbutton" style="color:black;" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&source=bookmarklet','_blank','menubar=no,height=400,width=608,toolbar=no,scrollbars=no,status=no');})();">Shaare link</a> - <small>Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....). Then click "Shaare link" button in any page you want to share.</small> | 671 | <a class="smallbutton" style="color:black;" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&source=bookmarklet','_blank','menubar=no,height=400,width=608,toolbar=no,scrollbars=no,status=no');})();">Shaare link</a> - Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....). Then click "Shaare link" button in any page you want to share.<br><br> |
661 | </div> | 672 | </div> |
662 | HTML; | 673 | HTML; |
663 | $data = array('pageheader'=>$toolbar,'body'=>'','onload'=>''); | 674 | $data = array('pageheader'=>$toolbar,'body'=>'','onload'=>''); |
@@ -685,6 +696,7 @@ HTML; | |||
685 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. | 696 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. |
686 | $LINKSDB[$linkdate] = $link; | 697 | $LINKSDB[$linkdate] = $link; |
687 | $LINKSDB->savedb(); // save to disk | 698 | $LINKSDB->savedb(); // save to disk |
699 | invalidateCaches(); | ||
688 | 700 | ||
689 | // If we are called from the bookmarklet, we must close the popup: | 701 | // If we are called from the bookmarklet, we must close the popup: |
690 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } | 702 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } |
@@ -713,6 +725,7 @@ HTML; | |||
713 | $linkdate=$_POST['lf_linkdate']; | 725 | $linkdate=$_POST['lf_linkdate']; |
714 | unset($LINKSDB[$linkdate]); | 726 | unset($LINKSDB[$linkdate]); |
715 | $LINKSDB->savedb(); // save to disk | 727 | $LINKSDB->savedb(); // save to disk |
728 | invalidateCaches(); | ||
716 | // If we are called from the bookmarklet, we must close the popup: | 729 | // If we are called from the bookmarklet, we must close the popup: |
717 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } | 730 | if (isset($_GET['source']) && $_GET['source']=='bookmarklet') { echo '<script language="JavaScript">self.close();</script>'; exit; } |
718 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); | 731 | $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); |
@@ -766,9 +779,25 @@ HTML; | |||
766 | 779 | ||
767 | // -------- Export as Netscape Bookmarks HTML file. | 780 | // -------- Export as Netscape Bookmarks HTML file. |
768 | if (startswith($_SERVER["QUERY_STRING"],'do=export')) | 781 | if (startswith($_SERVER["QUERY_STRING"],'do=export')) |
769 | { | 782 | { |
783 | if (empty($_GET['what'])) | ||
784 | { | ||
785 | $toolbar= <<<HTML | ||
786 | <div id="headerform"><br> | ||
787 | <a href="?do=export&what=all"><b>Export all</b></a> - Export all links<br><br> | ||
788 | <a href="?do=export&what=public"><b>Export public</b></a> - Export public links only<br><br> | ||
789 | <a href="?do=export&what=private"><b>Export private</b></a> - Export private links only<br><br> | ||
790 | </div> | ||
791 | HTML; | ||
792 | $data = array('pageheader'=>$toolbar,'body'=>'','onload'=>''); | ||
793 | templatePage($data); | ||
794 | exit; | ||
795 | } | ||
796 | $exportWhat=$_GET['what']; | ||
797 | if (!array_intersect(array('all','public','private'),array($exportWhat))) die('What are you trying to export ???'); | ||
798 | |||
770 | header('Content-Type: text/html; charset=utf-8'); | 799 | header('Content-Type: text/html; charset=utf-8'); |
771 | header('Content-disposition: attachment; filename=bookmarks_'.strval(date('Ymd_His')).'.html'); | 800 | header('Content-disposition: attachment; filename=bookmarks_'.$exportWhat.'_'.strval(date('Ymd_His')).'.html'); |
772 | echo <<<HTML | 801 | echo <<<HTML |
773 | <!DOCTYPE NETSCAPE-Bookmark-file-1> | 802 | <!DOCTYPE NETSCAPE-Bookmark-file-1> |
774 | <!-- This is an automatically generated file. | 803 | <!-- This is an automatically generated file. |
@@ -780,12 +809,17 @@ HTML; | |||
780 | HTML; | 809 | HTML; |
781 | foreach($LINKSDB as $link) | 810 | foreach($LINKSDB as $link) |
782 | { | 811 | { |
783 | echo '<DT><A HREF="'.htmlspecialchars($link['url']).'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"'; | 812 | if ($exportWhat=='all' || |
784 | if ($link['tags']!='') echo ' TAGS="'.htmlspecialchars(str_replace(' ',',',$link['tags'])).'"'; | 813 | ($exportWhat=='private' && $link['private']!=0) || |
785 | echo '>'.htmlspecialchars($link['title'])."</A>\n"; | 814 | ($exportWhat=='public' && $link['private']==0)) |
786 | if ($link['description']!='') echo '<DD>'.htmlspecialchars($link['description'])."\n"; | 815 | { |
816 | echo '<DT><A HREF="'.htmlspecialchars($link['url']).'" ADD_DATE="'.linkdate2timestamp($link['linkdate']).'" PRIVATE="'.$link['private'].'"'; | ||
817 | if ($link['tags']!='') echo ' TAGS="'.htmlspecialchars(str_replace(' ',',',$link['tags'])).'"'; | ||
818 | echo '>'.htmlspecialchars($link['title'])."</A>\n"; | ||
819 | if ($link['description']!='') echo '<DD>'.htmlspecialchars($link['description'])."\n"; | ||
820 | } | ||
787 | } | 821 | } |
788 | echo '<!-- Shaarli bookmarks export on '.date('Y/m/d H:i:s')."-->\n"; | 822 | echo '<!-- Shaarli '.$exportWhat.' bookmarks export on '.date('Y/m/d H:i:s')."-->\n"; |
789 | exit; | 823 | exit; |
790 | } | 824 | } |
791 | 825 | ||
@@ -817,7 +851,8 @@ Import Netscape html bookmarks (as exported from Firefox/Chrome/Opera/delicious/ | |||
817 | <input type="hidden" name="token" value="{$token}"> | 851 | <input type="hidden" name="token" value="{$token}"> |
818 | <input type="file" name="filetoupload" size="80"> | 852 | <input type="file" name="filetoupload" size="80"> |
819 | <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}"> | 853 | <input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}"> |
820 | <input type="submit" name="import_file" value="Import" class="bigbutton"> | 854 | <input type="submit" name="import_file" value="Import" class="bigbutton"><br> |
855 | <input type="checkbox" name="private"> Import all links as private | ||
821 | </form> | 856 | </form> |
822 | </div> | 857 | </div> |
823 | HTML; | 858 | HTML; |
@@ -847,6 +882,7 @@ function importFile() | |||
847 | $filename=$_FILES['filetoupload']['name']; | 882 | $filename=$_FILES['filetoupload']['name']; |
848 | $filesize=$_FILES['filetoupload']['size']; | 883 | $filesize=$_FILES['filetoupload']['size']; |
849 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); | 884 | $data=file_get_contents($_FILES['filetoupload']['tmp_name']); |
885 | $private = (empty($_POST['private']) ? 0 : 1); // Should the links be imported as private ? | ||
850 | 886 | ||
851 | // Sniff file type: | 887 | // Sniff file type: |
852 | $type='unknown'; | 888 | $type='unknown'; |
@@ -876,11 +912,16 @@ function importFile() | |||
876 | elseif ($attr=='PRIVATE') $link['private']=($value=='0'?0:1); | 912 | elseif ($attr=='PRIVATE') $link['private']=($value=='0'?0:1); |
877 | elseif ($attr=='TAGS') $link['tags']=str_replace(',',' ',$value); | 913 | elseif ($attr=='TAGS') $link['tags']=str_replace(',',' ',$value); |
878 | } | 914 | } |
879 | if ($link['linkdate']!='' && $link['url']!='') $LINKSDB[$link['linkdate']] = $link; | 915 | if ($link['linkdate']!='' && $link['url']!='' && empty($LINKSDB[$link['linkdate']])) |
916 | { | ||
917 | if ($private==1) $link['private']=1; | ||
918 | $LINKSDB[$link['linkdate']] = $link; | ||
919 | } | ||
880 | } | 920 | } |
881 | } | 921 | } |
882 | $import_count = count($LINKSDB)-$before; | 922 | $import_count = count($LINKSDB)-$before; |
883 | $LINKSDB->savedb(); | 923 | $LINKSDB->savedb(); |
924 | invalidateCaches(); | ||
884 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully imported: '.$import_count.' new links.");document.location=\'?\';</script>'; | 925 | echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully imported: '.$import_count.' new links.");document.location=\'?\';</script>'; |
885 | } | 926 | } |
886 | else | 927 | else |
@@ -1200,6 +1241,13 @@ function processWS() | |||
1200 | } | 1241 | } |
1201 | } | 1242 | } |
1202 | 1243 | ||
1244 | // Invalidate caches when the database is changed or the user logs out. | ||
1245 | // (eg. tags cache). | ||
1246 | function invalidateCaches() | ||
1247 | { | ||
1248 | unset($_SESSION['tags']); | ||
1249 | } | ||
1250 | |||
1203 | $LINKSDB=new linkdb(isLoggedIn() || OPEN_SHAARLI); // Read links from database (and filter private links if used it not logged in). | 1251 | $LINKSDB=new linkdb(isLoggedIn() || OPEN_SHAARLI); // Read links from database (and filter private links if used it not logged in). |
1204 | if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) | 1252 | if (startswith($_SERVER["QUERY_STRING"],'ws=')) { processWS(); exit; } // Webservices (for jQuery/jQueryUI) |
1205 | if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE; | 1253 | if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=LINKS_PER_PAGE; |