diff --git a/doc/Copy-a-Shaarli-installation-over-SSH-SCP,-serve-it-locally-with-php-cli.html b/doc/Copy-a-Shaarli-installation-over-SSH-SCP,-serve-it-locally-with-php-cli.html new file mode 100644 index 0000000..25e4bc6 --- /dev/null +++ b/doc/Copy-a-Shaarli-installation-over-SSH-SCP,-serve-it-locally-with-php-cli.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + +

Example bash script:

+
#!/bin/bash
+#Description: Copy a Shaarli installation over SSH/SCP, serve it locally with php-cli
+#Will create a local-shaarli/ directory when you run it, backup your Shaarli there, and serve it locally.
+#Will NOT download linked pages. It's just a directly usable backup/copy/mirror of your Shaarli
+#Requires: ssh, scp and a working SSH access to the server where your Shaarli is installed
+#Usage: ./local-shaarli.sh
+#Author: nodiscc (nodiscc@gmail.com)
+#License: MIT (http://opensource.org/licenses/MIT)
+set -o errexit
+set -o nounset
+
+##### CONFIG #################
+#The port used by php's local server
+php_local_port=7431
+
+#Name of the SSH server and path where Shaarli is installed
+#TODO: pass these as command-line arguments
+remotehost="my.ssh.server"
+remote_shaarli_dir="/var/www/shaarli"
+
+
+###### FUNCTIONS #############
+_main() {
+    _CBSyncShaarli
+    _CBServeShaarli
+}
+
+_CBSyncShaarli() {
+    remote_temp_dir=$(ssh $remotehost mktemp -d)
+    remote_ssh_user=$(ssh $remotehost whoami)
+    ssh -t "$remotehost" sudo cp -r "$remote_shaarli_dir" "$remote_temp_dir"
+    ssh -t "$remotehost" sudo chown -R "$remote_ssh_user":"$remote_ssh_user" "$remote_temp_dir"
+    scp -rq "$remotehost":"$remote_temp_dir" local-shaarli
+    ssh "$remotehost" rm -r "$remote_temp_dir"
+}
+
+_CBServeShaarli() {
+    #TODO: allow serving a previously downloaded Shaarli
+    #TODO: ask before overwriting local copy, if it exists
+    cd local-shaarli/
+    php -S localhost:${php_local_port}
+    echo "Please go to http://localhost:${php_local_port}"
+}
+
+
+##### MAIN #################
+
+_main
+

This outputs:

+
$ ./local-shaarli.sh
+PHP 5.6.0RC4 Development Server started at Mon Sep  1 21:56:19 2014
+Listening on http://localhost:7431
+Document root is /home/user/local-shaarli/shaarli
+Press Ctrl-C to quit.
+
+[Mon Sep  1 21:56:27 2014] ::1:57868 [200]: /
+[Mon Sep  1 21:56:27 2014] ::1:57869 [200]: /index.html
+[Mon Sep  1 21:56:37 2014] ::1:57881 [200]: /...
+ + diff --git a/doc/Download-CSS-styles-for-shaarlis-listed-in-an-opml-file.html b/doc/Download-CSS-styles-for-shaarlis-listed-in-an-opml-file.html new file mode 100644 index 0000000..0f32fb8 --- /dev/null +++ b/doc/Download-CSS-styles-for-shaarlis-listed-in-an-opml-file.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + +

Download CSS styles for shaarlis listed in an opml file

+

Example php script:

+
<!---- ?php -->
+<!---- Copyright (c) 2014 Nicolas Delsaux (https://github.com/Riduidel) -->
+<!---- License: zlib (http://www.gzip.org/zlib/zlib_license.html) -->
+
+/**
+ * Source: https://github.com/Riduidel
+ * Download css styles for shaarlis listed in an opml file
+ */
+define("SHAARLI_RSS_OPML", "https://www.ecirtam.net/shaarlirss/custom/people.opml");
+
+define("THEMES_TEMP_FOLDER", "new_themes");
+
+if(!file_exists(THEMES_TEMP_FOLDER)) {
+    mkdir(THEMES_TEMP_FOLDER);
+}
+
+function siteUrl($pathInSite) {
+    $indexPos = strpos($pathInSite, "index.php");
+    if(!$indexPos) {
+        return $pathInSite;
+    } else {
+        return substr($pathInSite, 0, $indexPos);
+    }
+}
+
+function createShaarliHashFromOPMLL($opmlFile) {
+    $result = array();
+    $opml = file_get_contents($opmlFile);
+    $opmlXml = simplexml_load_string($opml);
+    $outlineElements = $opmlXml->xpath("body/outline");
+    foreach($outlineElements as $site) {
+        $siteUrl = siteUrl((string) $site['htmlUrl']);
+        $result[$siteUrl]=((string) $site['text']);
+    }
+    return $result;
+}
+
+function getSiteFolder($url) {
+    $domain = parse_url($url,  PHP_URL_HOST);
+    return THEMES_TEMP_FOLDER."/".str_replace(".", "_", $domain);
+}
+
+function get_http_response_code($theURL) {
+     $headers = get_headers($theURL);
+     return substr($headers[0], 9, 3);
+}
+
+/**
+ * This makes the code PHP-5 only (particularly the call to "get_headers")
+ */
+function copyUserStyleFrom($url, $name, $knownStyles) {
+    $userStyle = $url."inc/user.css";
+    if(in_array($url, $knownStyles)) {
+        // TODO add log message
+    } else {
+        $statusCode = get_http_response_code($userStyle);
+        if(intval($statusCode)<300) {
+            $styleSheet = file_get_contents($userStyle);
+            $siteFolder = getSiteFolder($url);
+            if(!file_exists($siteFolder)) {
+                mkdir($siteFolder);
+            }
+            if(!file_exists($siteFolder.'/user.css')) {
+                // Copy stylesheet
+                file_put_contents($siteFolder.'/user.css', $styleSheet);
+            }
+            if(!file_exists($siteFolder.'/README.md')) {
+                // Then write a readme.md file
+                file_put_contents($siteFolder.'/README.md', 
+                    "User style from ".$name."\n"
+                    ."============================="
+                    ."\n\n"
+                    ."This stylesheet was downloaded from ".$userStyle." on ".date(DATE_RFC822)
+                    );
+            }
+            if(!file_exists($siteFolder.'/config.ini')) {
+                // Write a config file containing useful informations
+                file_put_contents($siteFolder.'/config.ini', 
+                    "site_url=".$url."\n"
+                    ."site_name=".$name."\n"
+                    );
+            }
+            if(!file_exists($siteFolder.'/home.png')) {
+                // And finally copy generated thumbnail
+                $homeThumb = $siteFolder.'/home.png';
+                file_put_contents($siteFolder.'/home.png', file_get_contents(getThumbnailUrl($url)));
+            }
+            echo 'Theme have been downloaded from  <a href="'.$url.'">'.$url.'</a> into '.$siteFolder
+                .'. It looks like <img src="'.$homeThumb.'"><br/>';
+        }
+    }
+}
+
+function getThumbnailUrl($url) {
+    return 'http://api.webthumbnail.org/?url='.$url;
+}
+
+function copyUserStylesFrom($urlToNames, $knownStyles) {
+    foreach($urlToNames as $url => $name) {
+        copyUserStyleFrom($url, $name, $knownStyles);
+    }
+}
+
+/**
+ * Reading directory list, courtesy of http://www.laughing-buddha.net/php/dirlist/
+ * @param directory the directory we want to list files of
+ * @return a simple array containing the list of absolute file paths. Notice that current file (".") and parent one("..")
+ * are not listed here
+ */
+function getDirectoryList ($directory)  {
+    $realPath = realpath($directory);
+    // create an array to hold directory list
+    $results = array();
+    // create a handler for the directory
+    $handler = opendir($directory);
+    // open directory and walk through the filenames
+    while ($file = readdir($handler)) {
+        // if file isn't this directory or its parent, add it to the results
+        if ($file != "." && $file != "..") {
+            $results[] = realpath($realPath . "/" . $file);
+        }
+    }
+    // tidy up: close the handler
+    closedir($handler);
+    // done!
+    return $results;
+}
+
+/**
+ * Start in themes folder and look in all subfolders for config.ini files. 
+ * These config.ini files allow us not to download styles again and again
+ */
+function findKnownStyles() {
+    $result = array();
+    $subFolders = getDirectoryList("themes");
+    foreach($subFolders as $folder) {
+        $configFile = $folder."/config.ini";
+        if(file_exists($configFile)) {
+            $iniParameters = parse_ini_file($configFile);
+            array_push($result, $iniParameters['site_url']);
+        }
+    }
+    return $result;
+}
+
+$knownStyles = findKnownStyles();
+copyUserStylesFrom(createShaarliHashFromOPMLL(SHAARLI_RSS_OPML), $knownStyles);
+
+<!--- ? ---->
+ + diff --git a/doc/Example-patch---add-new-via-field-for-links.html b/doc/Example-patch---add-new-via-field-for-links.html new file mode 100644 index 0000000..7df9d25 --- /dev/null +++ b/doc/Example-patch---add-new-via-field-for-links.html @@ -0,0 +1,201 @@ + + + + + + + + + + + + +

Example patch to add a new field ("via") for links, an input field to set the "via" property from the "edit link" dialog, and display the "via" field in the link list display. Untested, use at your own risk

+

Thanks to @Knah-Tsaeb in https://github.com/sebsauvage/Shaarli/pull/158

+
From e0f363c18e8fe67990ed2bb1a08652e24e70bbcb Mon Sep 17 00:00:00 2001
+From: Knah Tsaeb <knah-tsaeb@knah-tsaeb.org>
+Date: Fri, 11 Oct 2013 15:18:37 +0200
+Subject: [PATCH] Add a "via"/origin property for links, add new input in "edit link" dialog
+Thanks to:
+* https://github.com/Knah-Tsaeb/Shaarli/commit/040eb18ec8cdabd5ea855e108f81f97fbf0478c4
+* https://github.com/Knah-Tsaeb/Shaarli/commit/4123658eae44d7564d1128ce52ddd5689efee813
+* https://github.com/Knah-Tsaeb/Shaarli/commit/f1a8ca9cc8fe49b119d51b2d8382cc1a34542f96
+
+---
+ index.php         | 43 ++++++++++++++++++++++++++++++++-----------
+ tpl/editlink.html |  1 +
+ tpl/linklist.html |  1 +
+ 3 files changed, 34 insertions(+), 11 deletions(-)
+
+diff --git a/index.php b/index.php
+index 6fae2f8..53f798e 100644
+--- a/index.php
++++ b/index.php
+@@ -436,6 +436,12 @@ if (isset($_POST['login']))
+ // ------------------------------------------------------------------------------------------
+ // Misc utility functions:
+ 
++// Try to get just domain for @via
++function getJustDomain($url){
++    $parts = parse_url($url);   
++    return trim($parts['host']);
++    }
++
+ // Returns the server URL (including port and http/https), without path.
+ // e.g. "http://myserver.com:8080"
+ // You can append $_SERVER['SCRIPT_NAME'] to get the current script URL.
+@@ -799,7 +805,8 @@ class linkdb implements Iterator, Countable, ArrayAccess
+             $found=   (strpos(strtolower($l['title']),$s)!==false)
+                    || (strpos(strtolower($l['description']),$s)!==false)
+                    || (strpos(strtolower($l['url']),$s)!==false)
+-                   || (strpos(strtolower($l['tags']),$s)!==false);
++                   || (strpos(strtolower($l['tags']),$s)!==false)
++                   || (!empty($l['via']) && (strpos(strtolower($l['via']),$s)!==false));
+             if ($found) $filtered[$l['linkdate']] = $l;
+         }
+         krsort($filtered);
+@@ -814,7 +821,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
+         $t = str_replace(',',' ',($casesensitive?$tags:strtolower($tags)));
+         $searchtags=explode(' ',$t);
+         $filtered=array();
+-        foreach($this->links as $l)
++        foreach($this-> links as $l)
+         {
+             $linktags = explode(' ',($casesensitive?$l['tags']:strtolower($l['tags'])));
+             if (count(array_intersect($linktags,$searchtags)) == count($searchtags))
+@@ -905,7 +912,7 @@ function showRSS()
+     else $linksToDisplay = $LINKSDB;
+     $nblinksToDisplay = 50;  // Number of links to display.
+     if (!empty($_GET['nb']))  // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
+-    { 
++    {
+         $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
+     }
+ 
+@@ -944,7 +951,12 @@ function showRSS()
+         // If user wants permalinks first, put the final link in description
+         if ($usepermalinks===true) $descriptionlink = '(<a href="'.$absurl.'">Link</a>)';
+         if (strlen($link['description'])>0) $descriptionlink = '<br>'.$descriptionlink;
+-        echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";
++        if(!empty($link['via'])){
++          $via = '<br>Origine => <a href="'.htmlspecialchars($link['via']).'">'.htmlspecialchars(getJustDomain($link['via'])).'</a>';
++        } else {
++         $via = '';
++        }
++        echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$via.$descriptionlink.']]></description>'."\n</item>\n";
+         $i++;
+     }
+     echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
+@@ -980,7 +992,7 @@ function showATOM()
+     else $linksToDisplay = $LINKSDB;
+     $nblinksToDisplay = 50;  // Number of links to display.
+     if (!empty($_GET['nb']))  // In URL, you can specificy the number of links. Example: nb=200 or nb=all for all links.
+-    { 
++    {
+         $nblinksToDisplay = $_GET['nb']=='all' ? count($linksToDisplay) : max($_GET['nb']+0,1) ;
+     }
+ 
+@@ -1006,11 +1018,16 @@ function showATOM()
+ 
+         // Add permalink in description
+         $descriptionlink = htmlspecialchars('(<a href="'.$guid.'">Permalink</a>)');
++        if(isset($link['via']) && !empty($link['via'])){
++          $via = htmlspecialchars('</br> Origine => <a href="'.$link['via'].'">'.getJustDomain($link['via']).'</a>');
++        } else {
++          $via = '';
++        }
+         // If user wants permalinks first, put the final link in description
+         if ($usepermalinks===true) $descriptionlink = htmlspecialchars('(<a href="'.$absurl.'">Link</a>)');
+         if (strlen($link['description'])>0) $descriptionlink = '&lt;br&gt;'.$descriptionlink;
+ 
+-        $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))))).$descriptionlink."</content>\n";
++        $entries.='<content type="html">'.htmlspecialchars(nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))))).$descriptionlink.$via."</content>\n";
+         if ($link['tags']!='') // Adding tags to each ATOM entry (as mentioned in ATOM specification)
+         {
+             foreach(explode(' ',$link['tags']) as $tag)
+@@ -1478,7 +1495,7 @@ function renderPage()
+         if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?'))
+             $url = 'http://'.$url;
+         $link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0),
+-                      'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
++                      'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags), 'via'=>trim($_POST['lf_via']));
+         if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
+         $LINKSDB[$linkdate] = $link;
+         $LINKSDB->savedb(); // Save to disk.
+@@ -1556,7 +1573,8 @@ function renderPage()
+             $title = (empty($_GET['title']) ? '' : $_GET['title'] ); // Get title if it was provided in URL (by the bookmarklet).
+             $description = (empty($_GET['description']) ? '' : $_GET['description']); // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
+             $tags = (empty($_GET['tags']) ? '' : $_GET['tags'] ); // Get tags if it was provided in URL
+-            $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL 
++            $via = (empty($_GET['via']) ? '' : $_GET['via'] );
++            $private = (!empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0); // Get private if it was provided in URL
+             if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
+             // 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.)
+             if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
+@@ -1567,7 +1585,7 @@ function renderPage()
+                     {
+                         // Look for charset in html header.
+                        preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
+- 
++
+                        // If found, extract encoding.
+                        if (!empty($meta[0]))
+                        {
+@@ -1577,7 +1595,7 @@ function renderPage()
+                            $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8';
+                        }
+                        else { $html_charset = 'utf-8'; }
+- 
++
+                        // Extract title
+                        $title = html_extract_title($data);
+                        if (!empty($title))
+@@ -1592,7 +1610,7 @@ function renderPage()
+                 $url='?'.smallHash($linkdate);
+                 $title='Note: ';
+             }
+-            $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>$private);
++            $link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'via' => $via,'private'=>$private);
+         }
+ 
+         $PAGE = new pageBuilder;
+@@ -1842,6 +1860,9 @@ function buildLinkList($PAGE,$LINKSDB)
+         $taglist = explode(' ',$link['tags']);
+         uasort($taglist, 'strcasecmp');
+         $link['taglist']=$taglist;
++        if(!empty($link['via'])){
++          $link['via']=htmlspecialchars($link['via']);
++        }
+         $linkDisp[$keys[$i]] = $link;
+         $i++;
+     }
+diff --git a/tpl/editlink.html b/tpl/editlink.html
+index 4a2c30c..14d4f9c 100644
+--- a/tpl/editlink.html
++++ b/tpl/editlink.html
+@@ -16,6 +16,7 @@
+            <i>Title</i><br><input type="text" name="lf_title" value="{$link.title|htmlspecialchars}" style="width:100%"><br>
+            <i>Description</i><br><textarea name="lf_description" rows="4" cols="25" style="width:100%">{$link.description|htmlspecialchars}</textarea><br>
+            <i>Tags</i><br><input type="text" id="lf_tags" name="lf_tags" value="{$link.tags|htmlspecialchars}" style="width:100%"><br>
++           <i>Origine</i><br><input type="text" name="lf_via" value="{$link.via|htmlspecialchars}" style="width:100%"><br>
+            {if condition="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"}
+             <input type="checkbox" checked="checked" name="lf_private" id="lf_private">
+             &nbsp;<label for="lf_private"><i>Private</i></label><br>
+diff --git a/tpl/linklist.html b/tpl/linklist.html
+index ddc38cb..0a8475f 100644
+--- a/tpl/linklist.html
++++ b/tpl/linklist.html
+@@ -43,6 +43,7 @@
+                 <span class="linktitle"><a href="{$redirector}{$value.url|htmlspecialchars}">{$value.title|htmlspecialchars}</a></span>
+                 <br>
+                 {if="$value.description"}<div class="linkdescription"{if condition="$search_type=='permalink'"} style="max-height:none !important;"{/if}>{$value.description}</div>{/if}
++                {if condition="isset($value.via) && !empty($value.via)"}<div><a href="{$value.via}">Origine => {$value.via|getJustDomain}</a></div>{/if}
+                 {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
+                     <span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{$value.localdate|htmlspecialchars} - permalink</a> - </span>
+                 {else}
+-- 
+2.1.1
+ + diff --git a/doc/Home.html b/doc/Home.html new file mode 100644 index 0000000..e4d117f --- /dev/null +++ b/doc/Home.html @@ -0,0 +1,368 @@ + + + + + + + + + + + + +

Shaarli wiki

+

Welcome to the Shaarli wiki! Here you can find some info on how to use, configure, tweak and solve problems with your Shaarli. For general info, read the README.

+

If you have any questions or ideas, please join the chat (also reachable via IRC), post them in our general discussion or read the current issues. If you've found a bug, please create a new issue.

+

If you'd like a feature added, see if it fits in the list of Ideas for Plugins and update the corresponding bug report.

+

Note: This documentation is available online at https://github.com/shaarli/Shaarli/wiki, and locally in the doc/ directory of your Shaarli installation.

+
+ + + + + + + + + +
+

Basic Usage

+

Add the sharing button (bookmarklet) to your browser

+ +

This bookmarklet button in compatible with Firefox, Opera, Chrome and Safari. Under Opera, you can't drag'n drop the button: You have to right-click on it and add a bookmark to your personal toolbar.

+

+ + +

Other usage examples

+

Shaarli can be used:

+ +

Using Shaarli as a blog, notepad, pastebin...

+ +

RSS Feeds or Picture Wall for a specific search/tag

+

It is possible to filter RSS/ATOM feeds and Picture Wall on a Shaarli to only display results of a specific search, or for a specific tag. For example, if you want to subscribe only to links tagged photography:

+ +

+

Configuration

+

Main data/options.php file

+

To change the configuration, create the file data/options.php, example:

+
    <?php
+    $GLOBALS['config']['LINKS_PER_PAGE'] = 30;
+    $GLOBALS['config']['HIDE_TIMESTAMPS'] = true;
+    $GLOBALS['config']['ENABLE_THUMBNAILS'] = false;  
+    ?>
+

Do not edit config options in index.php! Your changes would be lost when you upgrade. The following parameters are available (parameters (default value)):

+ +

Changing theme

+ +

See also:

+ +

Changing template

+

| 💥 | This feature is currently being worked on and will be improved in the next releases. Experimental. |
|---------|---------|

+ +

$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/my-template/' ;

+

You can find a list of compatible templates in Related Software

+

Backup

+

You have two ways of backing up your database:

+ +

Troubleshooting

+

I forgot my password !

+

Delete the file data/config.php and display the page again. You will be asked for a new login/password.

+

I'm locked out - Login bruteforce protection

+

Login form is protected against brute force attacks: 4 failed logins will ban the IP address from login for 30 minutes. Banned IPs can still browse links.

+

To remove the current IP bans, delete the file data/ipbans.php

+

List of all login attempts

+

The file data/log.txt shows all logins (successful or failed) and bans/lifted bans.
Search for failed in this file to look for unauthorized login attempts.

+

Exporting from Diigo

+

If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.)

+

Importing from SemanticScuttle

+

To correctly import the tags from a SemanticScuttle HTML export, edit the HTML file before importing and replace all occurences of tags= (lowercase) to TAGS= (uppercase).

+

Importing from Mister Wong

+

See this issue for import tweaks.

+

Hosting problems

+ +
php 1
+SetEnv PHP_VER 5
+ +
//list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive.
+// FIXME: Decode charset according to charset specified in either 1) HTTP response headers or 2) <head> in html 
+//if (strpos($status,'200 OK')) $title=html_extract_title($data);
+ +

Dates are not properly formatted

+

Shaarli tries to sniff the language of the browser (using HTTP_ACCEPT_LANGUAGE headers) and choose a date format accordingly. But Shaarli can only use the date formats (and more generaly speaking, the locales) provided by the webserver. So even if you have a browser in French, you may end up with dates in US format (it's the case on sebsauvage.net :-( )

+

Problems on CentOS servers

+

On CentOS/RedHat derivatives, you may need to install the php-mbstring package.

+

My session expires ! I can't stay logged in

+

This can be caused by several things:

+ +

Sessions do not seem to work correctly on your server

+

Follow the instructions in the error message. Make sure you are accessing shaarli via a direct IP address or a proper hostname. If you have no dots in the hostname (e.g. localhost or http://my-webserver/shaarli/), some browsers will not store cookies at all (this respects the HTTP cookie specification).

+

pubsubhubbub support

+

Download publisher.php at the root of your Shaarli installation and set $GLOBALS['config']['PUBSUBHUB_URL'] in your config.php

+

Notes

+

Various hacks

+ + + +
$data = "tZNdb9MwFIb... <Commented content inside datastore.php>";
+$out = unserialize(gzinflate(base64_decode($data)));
+echo "<pre>"; // Pretty printing is love, pretty printing is life
+print_r($out);
+echo "</pre>";
+exit;
+

This will output the internal representation of the datastore, "unobfuscated" (if this can really be considered obfuscation)

+

Related software

+

Unofficial but relatedd work on Shaarli. If you maintain one of these, please get in touch with us to help us find a way to adapt your work to our fork. TODO contact repos owners to see if they'd like to standardize their work for the community fork.

+ +

Other links

+ +

FAQ

+

Why did you create Shaarli ?

+

I was a StumbleUpon user. Then I got fed up with they big toolbar. I switched to delicious, which was lighter, faster and more beautiful. Until Yahoo bought it. Then the export API broke all the time, delicious became slow and was ditched by Yahoo. I switched to Diigo, which is not bad, but does too much. And Diigo is sslllooooowww and their Firefox extension a bit buggy. And… oh… their Firefox addon sends to Diigo every single URL you visit (Don't believe me ? Use Tamper Data and open any page).

+

Enough is enough. Saving simple links should not be a complicated heavy thing. I ditched them all and wrote my own: Shaarli. It's simple, but it does the job and does it well. And my data is not hosted on a foreign server, but on my server.

+

Why use Shaarli and not Delicious/Diigo ?

+

With Shaarli:

+ +

What does Shaarli mean ?

+

Shaarli is for shaaring your links.

+

Technical details

+ +

Directory structure

+

Here is the directory structure of Shaarli and the purpose of the different files:

+
    index.php : Main program.
+    COPYING : Shaarli license.
+    inc/ : Includes (libraries, CSS…)
+        shaarli.css : Shaarli stylesheet.
+        jquery.min.js : jQuery javascript library.
+        jquery-ui.min.js : jQuery-UI javascript library.
+        jquery-MIT-LICENSE.txt: jQuery license.
+        jquery.lazyload.min.js: LazyLoad javascript library.
+        rain.tpl.class.php : RainTPL templating library.
+    tpl/ : RainTPL templates for Shaarli. They are used to build the pages.
+    images/ : Images and icons used in Shaarli.
+    data/ : Directory where data is stored (bookmark database, configuration, logs, banlist…)
+        config.php : Shaarli configuration (login, password, timezone, title…)
+        datastore.php : Your link database (compressed).
+        ipban.php : IP address ban system data.
+        lastupdatecheck.txt : Update check timestamp file (used to check every 24 hours if a new version of Shaarli is available).
+        log.txt : login/IPban log.
+    cache/ : Directory containing the thumbnails cache. This directory is automatically created. You can erase it anytime you want.
+    tmp/ : Temporary directory for compiled RainTPL templates. This directory is automatically created. You can erase it anytime you want.
+

Why not use a real database ? Files are slow !

+

Does browsing this page feel slow ? Try browsing older pages, too.

+

It's not slow at all, is it ? And don't forget the database contains more than 16000 links, and it's on a shared host, with 32000 visitors/day for my website alone. And it's still damn fast. Why ?

+

The data file is only 3.7 Mb. It's read 99% of the time, and is probably already in the operation system disk cache. So generating a page involves no I/O at all most of the time.

+

Wiki - TODO

+ +

...

+ + diff --git a/doc/Ideas-for-plugins.html b/doc/Ideas-for-plugins.html new file mode 100644 index 0000000..8ca3572 --- /dev/null +++ b/doc/Ideas-for-plugins.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + +

Please list here ideas for potential plugins. Do not include lengthy discussion about why/how the plugin should work, but link instead to an issue where this would have been discussed.
By listing these ideas here, we can keep the issues list a bit more clean, and have a centralized place where people wanting to contribute can find potential enhancement ideas.
Also have a look at https://github.com/shaarli/Shaarli/issues/14 for other suggestions.

+ + + diff --git a/doc/_Sidebar.html b/doc/_Sidebar.html new file mode 100644 index 0000000..b9e2ed3 --- /dev/null +++ b/doc/_Sidebar.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + +