]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Merge commit '1826e383ecf501302974132fd443cf1ca06e10f6' into v0.10
authorVirtualTam <virtualtam@flibidi.net>
Sat, 23 Feb 2019 15:27:33 +0000 (16:27 +0100)
committerVirtualTam <virtualtam@flibidi.net>
Sat, 23 Feb 2019 15:27:33 +0000 (16:27 +0100)
98 files changed:
Makefile
README.md
application/ApplicationUtils.php
application/Base64Url.php
application/FeedBuilder.php
application/HttpUtils.php
application/Languages.php
application/LinkDB.php
application/LinkFilter.php
application/LinkUtils.php
application/NetscapeBookmarkUtils.php
application/PageBuilder.php
application/PluginManager.php
application/Router.php
application/Thumbnailer.php
application/Updater.php
application/Url.php
application/Utils.php
application/api/ApiMiddleware.php
application/api/controllers/ApiController.php
application/api/controllers/History.php
application/api/controllers/Info.php
application/api/exceptions/ApiException.php
application/api/exceptions/ApiLinkNotFoundException.php
application/api/exceptions/ApiTagNotFoundException.php
application/config/ConfigPhp.php
application/config/ConfigPlugin.php
application/security/LoginManager.php
assets/default/js/base.js
assets/default/scss/shaarli.scss
composer.json
composer.lock
doc/custom_theme/main.html [new file with mode: 0644]
doc/md/Community-&-Related-software.md
doc/md/Server-configuration.md
doc/md/Sharing-content.md
doc/md/images/icon.png [new file with mode: 0644]
doc/md/index.md
inc/languages/fr/LC_MESSAGES/shaarli.po
index.php
mkdocs.yml
phpcs.xml [new file with mode: 0644]
plugins/archiveorg/archiveorg.php
plugins/demo_plugin/demo_plugin.php
plugins/isso/comment.png [new file with mode: 0644]
plugins/isso/isso.php
plugins/isso/isso_button.html [new file with mode: 0644]
plugins/markdown/markdown.php
plugins/pubsubhubbub/pubsubhubbub.php
plugins/qrcode/qrcode.php
plugins/wallabag/wallabag.php
tests/ApplicationUtilsTest.php
tests/CacheTest.php
tests/FeedBuilderTest.php
tests/HttpUtils/GetIpAdressFromProxyTest.php
tests/LinkDBTest.php
tests/LinkFilterTest.php
tests/LinkUtilsTest.php
tests/NetscapeBookmarkUtils/BookmarkExportTest.php
tests/RouterTest.php
tests/ThumbnailerTest.php
tests/Updater/DummyUpdater.php
tests/Updater/UpdaterTest.php
tests/Url/CleanupUrlTest.php
tests/Url/GetUrlSchemeTest.php
tests/Url/UnparseUrlTest.php
tests/Url/UrlTest.php
tests/UtilsTest.php
tests/api/ApiUtilsTest.php
tests/api/controllers/history/HistoryTest.php
tests/api/controllers/info/InfoTest.php
tests/api/controllers/links/GetLinksTest.php
tests/api/controllers/links/PostLinkTest.php
tests/api/controllers/links/PutLinkTest.php
tests/api/controllers/tags/PutTagTest.php
tests/languages/de/UtilsDeTest.php
tests/languages/fr/LanguagesFrTest.php
tests/plugins/PluginIssoTest.php
tests/plugins/PluginMarkdownTest.php
tests/plugins/PluginQrcodeTest.php
tests/plugins/resources/hashtags.md [new file with mode: 0644]
tests/plugins/resources/hashtags.raw [new file with mode: 0644]
tests/plugins/resources/markdown.html
tests/plugins/resources/markdown.md
tests/security/SessionManagerTest.php
tests/utils/ReferenceLinkDB.php
tests/utils/config/configPhp.php
tpl/default/addlink.html
tpl/default/daily.html
tpl/default/includes.html
tpl/default/linklist.html
tpl/default/linklist.paging.html
tpl/default/page.header.html
tpl/default/picwall.html
tpl/vintage/daily.html
tpl/vintage/includes.html
tpl/vintage/linklist.html
tpl/vintage/picwall.html

index 56cf09b2e0abdbcad70e1553bf17db4a763011a3..b758d1fd276e91fd523b3b1410c89341a6ed906a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,6 @@
 # Makefile for PHP code analysis & testing, documentation and release generation
 
 BIN = vendor/bin
-PHP_SOURCE = index.php application tests plugins
-PHP_COMMA_SOURCE = index.php,application,tests,plugins
 
 all: static_analysis_summary check_permissions test
 
@@ -17,14 +15,6 @@ docker_%:
        rsync -az /shaarli/ ~/shaarli/
        cd ~/shaarli && make $*
 
-##
-# Concise status of the project
-# These targets are non-blocking: || exit 0
-##
-
-static_analysis_summary: code_sniffer_source copy_paste mess_detector_summary
-       @echo
-
 ##
 # PHP_CodeSniffer
 # Detects PHP syntax errors
@@ -32,70 +22,26 @@ static_analysis_summary: code_sniffer_source copy_paste mess_detector_summary
 # - http://pear.php.net/manual/en/package.php.php-codesniffer.usage.php
 # - http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php
 ##
+PHPCS := $(BIN)/phpcs
 
-code_sniffer: code_sniffer_full
+code_sniffer:
+       @$(PHPCS)
 
 ### - errors filtered by coding standard: PEAR, PSR1, PSR2, Zend...
 PHPCS_%:
-       @$(BIN)/phpcs $(PHP_SOURCE) --report-full --report-width=200 --standard=$*
+       @$(PHPCS) --report-full --report-width=200 --standard=$*
 
 ### - errors by Git author
 code_sniffer_blame:
-       @$(BIN)/phpcs $(PHP_SOURCE) --report-gitblame
+       @$(PHPCS) --report-gitblame
 
 ### - all errors/warnings
 code_sniffer_full:
-       @$(BIN)/phpcs $(PHP_SOURCE) --report-full --report-width=200
+       @$(PHPCS) --report-full --report-width=200
 
 ### - errors grouped by kind
 code_sniffer_source:
-       @$(BIN)/phpcs $(PHP_SOURCE) --report-source || exit 0
-
-##
-# PHP Copy/Paste Detector
-# Detects code redundancy
-# Documentation: https://github.com/sebastianbergmann/phpcpd
-##
-
-copy_paste:
-       @echo "-----------------------"
-       @echo "PHP COPY/PASTE DETECTOR"
-       @echo "-----------------------"
-       @$(BIN)/phpcpd $(PHP_SOURCE) || exit 0
-       @echo
-
-##
-# PHP Mess Detector
-# Detects PHP syntax errors, sorted by category
-# Rules documentation: http://phpmd.org/rules/index.html
-##
-MESS_DETECTOR_RULES = cleancode,codesize,controversial,design,naming,unusedcode
-
-mess_title:
-       @echo "-----------------"
-       @echo "PHP MESS DETECTOR"
-       @echo "-----------------"
-
-###  - all warnings
-mess_detector: mess_title
-       @$(BIN)/phpmd $(PHP_COMMA_SOURCE) text $(MESS_DETECTOR_RULES) | sed 's_.*\/__'
-
-### - all warnings + HTML output contains links to PHPMD's documentation
-mess_detector_html:
-       @$(BIN)/phpmd $(PHP_COMMA_SOURCE) html $(MESS_DETECTOR_RULES) \
-       --reportfile phpmd.html || exit 0
-
-### - warnings grouped by message, sorted by descending frequency order
-mess_detector_grouped: mess_title
-       @$(BIN)/phpmd $(PHP_SOURCE) text $(MESS_DETECTOR_RULES) \
-       | cut -f 2 | sort | uniq -c | sort -nr
-
-### - summary: number of warnings by rule set
-mess_detector_summary: mess_title
-       @for rule in $$(echo $(MESS_DETECTOR_RULES) | tr ',' ' '); do \
-               warnings=$$($(BIN)/phpmd $(PHP_COMMA_SOURCE) text $$rule | wc -l); \
-               printf "$$warnings\t$$rule\n"; \
-       done;
+       @$(PHPCS) --report-source || exit 0
 
 ##
 # Checks source file & script permissions
index 2ff5460068291b8ad15a10a6ed1251a072705e5b..0e23e33d395a1d8bf4d52b6c1af46ad1a5dc0fb2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ _It is designed to be personal (single-user), fast and handy._
 [![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7)
 [![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
 &bull;
-[![](https://img.shields.io/badge/latest-v0.10.1-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.1)
+[![](https://img.shields.io/badge/latest-v0.10.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2)
 [![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
 &bull;
 [![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli)
index 911873a071ca7813f7ef9e3fa75d8ce18b2f8ec8..a3b2dcb18effa309c6d776961913cfe6f1385de0 100644 (file)
@@ -24,7 +24,7 @@ class ApplicationUtils
      *
      * @return mixed the version code from the repository if available, else 'false'
      */
-    public static function getLatestGitVersionCode($url, $timeout=2)
+    public static function getLatestGitVersionCode($url, $timeout = 2)
     {
         list($headers, $data) = get_http_response($url, $timeout);
 
@@ -86,13 +86,14 @@ class ApplicationUtils
      *
      * @return mixed the new version code if available and greater, else 'false'
      */
-    public static function checkUpdate($currentVersion,
-                                       $updateFile,
-                                       $checkInterval,
-                                       $enableCheck,
-                                       $isLoggedIn,
-                                       $branch='stable')
-    {
+    public static function checkUpdate(
+        $currentVersion,
+        $updateFile,
+        $checkInterval,
+        $enableCheck,
+        $isLoggedIn,
+        $branch = 'stable'
+    ) {
         // Do not check versions for visitors
         // Do not check if the user doesn't want to
         // Do not check with dev version
index 61590e43769f5830fd7111c121147f73ad853a96..54d0fcd52accef3639e94fde87d52b5b32678399 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace Shaarli;
 
-
 /**
  * URL-safe Base64 operations
  *
@@ -17,7 +16,8 @@ class Base64Url
      *
      * @return string Base64Url-encoded data
      */
-    public static function encode($data) {
+    public static function encode($data)
+    {
         return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
     }
 
@@ -28,7 +28,8 @@ class Base64Url
      *
      * @return string Decoded data
      */
-    public static function decode($data) {
+    public static function decode($data)
+    {
         return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
     }
 }
index ebae18b41c27ac011ff0cb12f8ad69fc6728d13e..73fafcbed31cb733ac0bf99611313d861a1b60a0 100644 (file)
@@ -163,7 +163,8 @@ class FeedBuilder
             $upDate = $link['updated'];
             $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM);
         } else {
-            $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);;
+            $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);
+            ;
         }
 
         // Save the more recent item.
@@ -261,7 +262,6 @@ class FeedBuilder
         }
         if ($this->feedType == self::$FEED_RSS) {
             return $date->format(DateTime::RSS);
-
         }
         return $date->format(DateTime::ATOM);
     }
index e9282506188f4d19969e9a49ac27664171c79938..9c438160dccdbe022d1f5398b41f27162774643f 100644 (file)
@@ -7,7 +7,8 @@
  * @param int             $timeout           network timeout (in seconds)
  * @param int             $maxBytes          maximum downloaded bytes (default: 4 MiB)
  * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION).
- *                                           Can be used to add download conditions on the headers (response code, content type, etc.).
+ *                                           Can be used to add download conditions on the
+ *                                           headers (response code, content type, etc.).
  *
  * @return array HTTP response headers, downloaded content
  *
@@ -64,29 +65,30 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF
     }
 
     // General cURL settings
-    curl_setopt($ch, CURLOPT_AUTOREFERER,       true);
-    curl_setopt($ch, CURLOPT_FOLLOWLOCATION,    true);
-    curl_setopt($ch, CURLOPT_HEADER,            true);
+    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
+    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+    curl_setopt($ch, CURLOPT_HEADER, true);
     curl_setopt(
         $ch,
         CURLOPT_HTTPHEADER,
         array('Accept-Language: ' . $acceptLanguage)
     );
-    curl_setopt($ch, CURLOPT_MAXREDIRS,         $maxRedirs);
-    curl_setopt($ch, CURLOPT_RETURNTRANSFER,    true);
-    curl_setopt($ch, CURLOPT_TIMEOUT,           $timeout);
-    curl_setopt($ch, CURLOPT_USERAGENT,         $userAgent);
+    curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+    curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
 
     if (is_callable($curlWriteFunction)) {
         curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction);
     }
 
     // Max download size management
-    curl_setopt($ch, CURLOPT_BUFFERSIZE,        1024*16);
-    curl_setopt($ch, CURLOPT_NOPROGRESS,        false);
-    curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,
-        function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes)
-        {
+    curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16);
+    curl_setopt($ch, CURLOPT_NOPROGRESS, false);
+    curl_setopt(
+        $ch,
+        CURLOPT_PROGRESSFUNCTION,
+        function ($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) {
             if (version_compare(phpversion(), '5.5', '<')) {
                 // PHP version lower than 5.5
                 // Callback has 4 arguments
@@ -232,7 +234,6 @@ function get_redirected_headers($url, $redirectionLimit = 3)
         && !empty($headers)
         && (strpos($headers[0], '301') !== false || strpos($headers[0], '302') !== false)
         && !empty($headers['Location'])) {
-
         $redirection = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location'];
         if ($redirection != $url) {
             $redirection = getAbsoluteUrl($url, $redirection);
index 4fa324265e9fa70a350e8173f5c8f7a06ced2bae..b9c5d0e8c3cbf627a590516d448ab9c85343a4fb 100644 (file)
@@ -92,7 +92,7 @@ class Languages
     /**
      * Initialize the translator using php gettext extension (gettext dependency act as a wrapper).
      */
-    protected function initGettextTranslator ()
+    protected function initGettextTranslator()
     {
         $this->translator = new GettextTranslator();
         $this->translator->setLanguage($this->language);
@@ -125,7 +125,8 @@ class Languages
             $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po');
             $translations->setDomain('shaarli');
             $this->translator->loadTranslations($translations);
-        } catch (\InvalidArgumentException $e) {}
+        } catch (\InvalidArgumentException $e) {
+        }
 
         // Default extension translation from the current theme
         $theme = $this->conf->get('theme');
@@ -137,7 +138,8 @@ class Languages
                 );
                 $translations->setDomain($theme);
                 $this->translator->loadTranslations($translations);
-            } catch (\InvalidArgumentException $e) {}
+            } catch (\InvalidArgumentException $e) {
+            }
         }
 
         // Extension translations (plugins, themes, etc.).
@@ -147,10 +149,13 @@ class Languages
             }
 
             try {
-                $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po');
+                $extension = Translations::fromPoFile(
+                    $translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'
+                );
                 $extension->setDomain($domain);
                 $this->translator->loadTranslations($extension);
-            } catch (\InvalidArgumentException $e) {}
+            } catch (\InvalidArgumentException $e) {
+            }
         }
     }
 
index cd0f29671f9b199c213b4333e8eeab23eedcadb7..4bbc2950e223ab86817cd8fa63c74a866bc7db03 100644 (file)
@@ -107,8 +107,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess
         $hidePublicLinks,
         $redirector = '',
         $redirectorEncode = true
-    )
-    {
+    ) {
         $this->datastore = $datastore;
         $this->loggedIn = $isLoggedIn;
         $this->hidePublicLinks = $hidePublicLinks;
@@ -250,11 +249,14 @@ class LinkDB implements Iterator, Countable, ArrayAccess
             'id' => 1,
             'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'),
             'url'=>'https://shaarli.readthedocs.io',
-            'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login.
+            'description'=>t(
+                'Welcome to Shaarli! This is your first public bookmark. '
+                .'To edit or delete me, you must first login.
 
 To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page.
 
-You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'),
+You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'
+            ),
             'private'=>0,
             'created'=> new DateTime(),
             'tags'=>'opensource software'
@@ -317,8 +319,7 @@ You use the community supported version of the original Shaarli project, by Seba
                 } else {
                     $link['real_url'] .= $link['url'];
                 }
-            }
-            else {
+            } else {
                 $link['real_url'] = $link['url'];
             }
 
@@ -403,7 +404,8 @@ You use the community supported version of the original Shaarli project, by Seba
      *
      * @return array list of shaare found.
      */
-    public function filterDay($request) {
+    public function filterDay($request)
+    {
         $linkFilter = new LinkFilter($this->links);
         return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request);
     }
@@ -420,8 +422,12 @@ You use the community supported version of the original Shaarli project, by Seba
      *
      * @return array filtered links, all links if no suitable filter was provided.
      */
-    public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all', $untaggedonly = false)
-    {
+    public function filterSearch(
+        $filterRequest = array(),
+        $casesensitive = false,
+        $visibility = 'all',
+        $untaggedonly = false
+    ) {
         // Filter link database according to parameters.
         $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
         $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
@@ -492,8 +498,7 @@ You use the community supported version of the original Shaarli project, by Seba
         $delete = empty($to);
         // True for case-sensitive tag search.
         $linksToAlter = $this->filterSearch(['searchtags' => $from], true);
-        foreach($linksToAlter as $key => &$value)
-        {
+        foreach ($linksToAlter as $key => &$value) {
             $tags = preg_split('/\s+/', trim($value['tags']));
             if (($pos = array_search($from, $tags)) !== false) {
                 if ($delete) {
@@ -536,7 +541,10 @@ You use the community supported version of the original Shaarli project, by Seba
     {
         $order = $order === 'ASC' ? -1 : 1;
         // Reorder array by dates.
-        usort($this->links, function($a, $b) use ($order) {
+        usort($this->links, function ($a, $b) use ($order) {
+            if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
+                return $a['sticky'] ? -1 : 1;
+            }
             return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
         });
 
index e52239b84c89f4c1b387b02dd399e3c25e4bf23a..8f147974e9d5cf232f3cf3f509b3401fcf5e6b84 100644 (file)
@@ -62,7 +62,7 @@ class LinkFilter
             $visibility = 'all';
         }
 
-        switch($type) {
+        switch ($type) {
             case self::$FILTER_HASH:
                 return $this->filterSmallHash($request);
             case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext"
@@ -205,7 +205,6 @@ class LinkFilter
 
         // Iterate over every stored link.
         foreach ($this->links as $id => $link) {
-
             // ignore non private links when 'privatonly' is on.
             if ($visibility !== 'all') {
                 if (! $link['private'] && $visibility === 'private') {
@@ -257,11 +256,11 @@ class LinkFilter
     private static function tag2regex($tag)
     {
         $len = strlen($tag);
-        if(!$len || $tag === "-" || $tag === "*"){
+        if (!$len || $tag === "-" || $tag === "*") {
             // nothing to search, return empty regex
             return '';
         }
-        if($tag[0] === "-") {
+        if ($tag[0] === "-") {
             // query is negated
             $i = 1; // use offset to start after '-' character
             $regex = '(?!'; // create negative lookahead
@@ -271,14 +270,14 @@ class LinkFilter
         }
         $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning
         // iterate over string, separating it into placeholder and content
-        for(; $i < $len; $i++){
-            if($tag[$i] === '*'){
+        for (; $i < $len; $i++) {
+            if ($tag[$i] === '*') {
                 // placeholder found
                 $regex .= '[^ ]*?';
             } else {
                 // regular characters
                 $offset = strpos($tag, '*', $i);
-                if($offset === false){
+                if ($offset === false) {
                     // no placeholder found, set offset to end of string
                     $offset = $len;
                 }
@@ -310,19 +309,19 @@ class LinkFilter
     {
         // get single tags (we may get passed an array, even though the docs say different)
         $inputTags = $tags;
-        if(!is_array($tags)) {
+        if (!is_array($tags)) {
             // we got an input string, split tags
             $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY);
         }
 
-        if(!count($inputTags)){
+        if (!count($inputTags)) {
             // no input tags
             return $this->noFilter($visibility);
         }
 
         // build regex from all tags
         $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/';
-        if(!$casesensitive) {
+        if (!$casesensitive) {
             // make regex case insensitive
             $re .= 'i';
         }
@@ -342,7 +341,7 @@ class LinkFilter
                 }
             }
             $search = $link['tags']; // build search string, start with tags of current link
-            if(strlen(trim($link['description'])) && strpos($link['description'], '#') !== false){
+            if (strlen(trim($link['description'])) && strpos($link['description'], '#') !== false) {
                 // description given and at least one possible tag found
                 $descTags = array();
                 // find all tags in the form of #tag in the description
@@ -351,13 +350,13 @@ class LinkFilter
                     $link['description'],
                     $descTags
                 );
-                if(count($descTags[1])){
+                if (count($descTags[1])) {
                     // there were some tags in the description, add them to the search string
                     $search .= ' ' . implode(' ', $descTags[1]);
                 }
             };
             // match regular expression with search string
-            if(!preg_match($re, $search)){
+            if (!preg_match($re, $search)) {
                 // this entry does _not_ match our regex
                 continue;
             }
index 4df5c0cababf247357d89ebd002e4b49a7910cf6..d56e019f4246f40d94f7a2eb5b8f715d10c7f1a1 100644 (file)
@@ -23,7 +23,7 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get
      *
      * @return int|bool length of $data or false if we need to stop the download
      */
-    return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) {
+    return function (&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) {
         $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE);
         if (!empty($responseCode) && in_array($responseCode, [301, 302])) {
             $isRedirected = true;
@@ -201,7 +201,8 @@ function space2nbsp($text)
 
  * @return string formatted description.
  */
-function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') {
+function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '')
+{
     return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl)));
 }
 
index b4d16d00bb991e2e9b031a2bb9f5c1d352030176..84dd2b2083e36f8d20755fbdb2d97502c83b15cd 100644 (file)
@@ -72,18 +72,20 @@ class NetscapeBookmarkUtils
     private static function importStatus(
         $filename,
         $filesize,
-        $importCount=0,
-        $overwriteCount=0,
-        $skipCount=0,
-        $duration=0
-    )
-    {
+        $importCount = 0,
+        $overwriteCount = 0,
+        $skipCount = 0,
+        $duration = 0
+    ) {
         $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize);
         if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) {
             $status .= t('has an unknown file format. Nothing was imported.');
         } else {
             $status .= vsprintf(
-                t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'),
+                t(
+                    'was successfully processed in %d seconds: '
+                    .'%d links imported, %d links overwritten, %d links skipped.'
+                ),
                 [$duration, $importCount, $overwriteCount, $skipCount]
             );
         }
index b1abe0d05fd724182cea8a0ecda659f93734f6c2..2ca95832c60eef3c234b85c3d7a102af1cf7eafa 100644 (file)
@@ -78,7 +78,6 @@ class PageBuilder
             );
             $this->tpl->assign('newVersion', escape($version));
             $this->tpl->assign('versionError', '');
-
         } catch (Exception $exc) {
             logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage());
             $this->tpl->assign('newVersion', '');
@@ -101,7 +100,7 @@ class PageBuilder
             'version_hash',
             ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt'))
         );
-        $this->tpl->assign('scripturl', index_url($_SERVER));
+        $this->tpl->assign('index_url', index_url($_SERVER));
         $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
         $this->tpl->assign('visibility', $visibility);
         $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
@@ -163,7 +162,7 @@ class PageBuilder
             $this->initialize();
         }
 
-        if (empty($data) || !is_array($data)){
+        if (empty($data) || !is_array($data)) {
             return false;
         }
 
index cf6038453e499e7e4030b68b1fffd13bfadbb79b..1ed4db4b30c09448227d028caf484a699bb0265a 100644 (file)
@@ -75,8 +75,7 @@ class PluginManager
 
             try {
                 $this->loadPlugin($dirs[$index], $plugin);
-            }
-            catch (PluginFileNotFoundException $e) {
+            } catch (PluginFileNotFoundException $e) {
                 error_log($e->getMessage());
             }
         }
index bf86b884db07243a215c86748d5f97cc9c1b811c..beb3165b49e128e1a8cb5f60bbb0427fa3a80b5e 100644 (file)
@@ -37,6 +37,8 @@ class Router
 
     public static $PAGE_DELETELINK = 'delete_link';
 
+    public static $PAGE_PINLINK = 'pin';
+
     public static $PAGE_EXPORT = 'export';
 
     public static $PAGE_IMPORT = 'import';
@@ -146,6 +148,10 @@ class Router
             return self::$PAGE_DELETELINK;
         }
 
+        if (startsWith($query, 'do='. self::$PAGE_PINLINK)) {
+            return self::$PAGE_PINLINK;
+        }
+
         if (startsWith($query, 'do='. self::$PAGE_EXPORT)) {
             return self::$PAGE_EXPORT;
         }
index 7d0d9c33203f9724afdde1b3db102d54aaa53d2b..37ed97a18bf521f6b6e66b0bc4e32a5083a8f01d 100644 (file)
@@ -58,7 +58,10 @@ class Thumbnailer
             $this->conf->set('thumbnails.enabled', false);
             $this->conf->write(true);
             // TODO: create a proper error handling system able to catch exceptions...
-            die(t('php-gd extension must be loaded to use thumbnails. Thumbnails are now disabled. Please reload the page.'));
+            die(t(
+                'php-gd extension must be loaded to use thumbnails. '
+                .'Thumbnails are now disabled. Please reload the page.'
+            ));
         }
 
         $this->wt = new WebThumbnailer();
index 480bff8267956ad82e12d3a4169e7b09f60ee135..6b94c5e34af485e8327628206cb9c19aa6c6f7f7 100644 (file)
@@ -183,7 +183,7 @@ class Updater
             }
         }
 
-        try{
+        try {
             $this->conf->write($this->isLoggedIn);
             return true;
         } catch (IOException $e) {
@@ -517,6 +517,26 @@ class Updater
 
         return true;
     }
+
+    /**
+     * Set sticky = false on all links
+     *
+     * @return bool true if the update is successful, false otherwise.
+     */
+    public function updateMethodSetSticky()
+    {
+        foreach ($this->linkDB as $key => $link) {
+            if (isset($link['sticky'])) {
+                return true;
+            }
+            $link['sticky'] = false;
+            $this->linkDB[$key] = $link;
+        }
+
+        $this->linkDB->save($this->conf->get('resource.page_cache'));
+
+        return true;
+    }
 }
 
 /**
index 6b9870f0c1bb468ebafd67e2d71123411f598a0c..3b7f19c207770679d2fff77a709210529ffa2afa 100644 (file)
@@ -34,8 +34,8 @@ function unparse_url($parsedUrl)
  */
 function cleanup_url($url)
 {
-  $obj_url = new Url($url);
-  return $obj_url->cleanup();
+    $obj_url = new Url($url);
+    return $obj_url->cleanup();
 }
 
 /**
@@ -47,8 +47,8 @@ function cleanup_url($url)
  */
 function get_url_scheme($url)
 {
-  $obj_url = new Url($url);
-  return $obj_url->getScheme();
+    $obj_url = new Url($url);
+    return $obj_url->getScheme();
 }
 
 /**
@@ -217,7 +217,7 @@ class Url
         }
 
         $this->parts['query'] = implode('&', $queryParams);
-    }    
+    }
 
     /**
      * Removes undesired fragments
@@ -269,7 +269,8 @@ class Url
      *
      * @return string the URL scheme or false if none is provided.
      */
-    public function getScheme() {
+    public function getScheme()
+    {
         if (!isset($this->parts['scheme'])) {
             return false;
         }
@@ -281,7 +282,8 @@ class Url
      *
      * @return string the URL host or false if none is provided.
      */
-    public function getHost() {
+    public function getHost()
+    {
         if (empty($this->parts['host'])) {
             return false;
         }
@@ -293,7 +295,8 @@ class Url
      *
      * @return true is HTTP, false otherwise.
      */
-    public function isHttp() {
+    public function isHttp()
+    {
         return strpos(strtolower($this->parts['scheme']), 'http') !== false;
     }
 }
index 97b12fcf5b5e1d8beb94ab507f3f32479b89e747..925e1a22c909011a342b3dc3ced3c9f0f5d9a3da 100644 (file)
@@ -97,7 +97,7 @@ function escape($input)
 
     if (is_array($input)) {
         $out = array();
-        foreach($input as $key => $value) {
+        foreach ($input as $key => $value) {
             $out[$key] = escape($value);
         }
         return $out;
@@ -355,10 +355,13 @@ function return_bytes($val)
     $val = trim($val);
     $last = strtolower($val[strlen($val)-1]);
     $val = intval(substr($val, 0, -1));
-    switch($last) {
-        case 'g': $val *= 1024;
-        case 'm': $val *= 1024;
-        case 'k': $val *= 1024;
+    switch ($last) {
+        case 'g':
+            $val *= 1024;
+        case 'm':
+            $val *= 1024;
+        case 'k':
+            $val *= 1024;
     }
     return $val;
 }
@@ -452,6 +455,7 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
  *
  * @return string Text translated.
  */
-function t($text, $nText = '', $nb = 1, $domain = 'shaarli') {
+function t($text, $nText = '', $nb = 1, $domain = 'shaarli')
+{
     return dn__($domain, $text, $nText, $nb);
 }
index ff2093930c69b5e5ad0c22f03eee816d2f27e8ce..66eac133649cf8c020c64164f7864b43ddf33a40 100644 (file)
@@ -65,7 +65,7 @@ class ApiMiddleware
         try {
             $this->checkRequest($request);
             $response = $next($request, $response);
-        } catch(ApiException $e) {
+        } catch (ApiException $e) {
             $e->setResponse($response);
             $e->setDebug($this->conf->get('dev.debug', false));
             $response = $e->getApiResponse();
@@ -98,7 +98,8 @@ class ApiMiddleware
      *
      * @throws ApiAuthorizationException The token couldn't be validated.
      */
-    protected function checkToken($request) {
+    protected function checkToken($request)
+    {
         if (! $request->hasHeader('Authorization')) {
             throw new ApiAuthorizationException('JWT token not provided');
         }
index 3be85b983a1fc62b6d2c272486c460e2f3664959..9edefcf627f1f4fe7026f07f7e438bdd3d68080e 100644 (file)
@@ -41,7 +41,7 @@ abstract class ApiController
 
     /**
      * ApiController constructor.
-     * 
+     *
      * Note: enabling debug mode displays JSON with readable formatting.
      *
      * @param Container $ci Slim container.
index 5cc453bfadd1decd5649d5cceab43ab342140b53..4582e8b27f8597a1a03c482161c58a1febf7706d 100644 (file)
@@ -35,8 +35,7 @@ class History extends ApiController
         $offset = $request->getParam('offset');
         if (empty($offset)) {
             $offset = 0;
-        }
-        elseif (ctype_digit($offset)) {
+        } elseif (ctype_digit($offset)) {
             $offset = (int) $offset;
         } else {
             throw new ApiBadParametersException('Invalid offset');
index 25433f720cf972b7ac370e05a2c67835443d7194..f37dcae5330d77b67d7bcaf277f63178d0895703 100644 (file)
@@ -7,7 +7,7 @@ use Slim\Http\Response;
 
 /**
  * Class Info
- * 
+ *
  * REST API Controller: /info
  *
  * @package Api\Controllers
@@ -17,7 +17,7 @@ class Info extends ApiController
 {
     /**
      * Service providing various information about Shaarli instance.
-     * 
+     *
      * @param Request  $request  Slim request.
      * @param Response $response Slim response.
      *
index c8490e0cb2e032f3a48ec29e1c6b4b8efd3dfaff..d6b66323279f86e4dd886c0477f9c41e4f67a9bb 100644 (file)
@@ -10,7 +10,8 @@ use Slim\Http\Response;
  * Parent Exception related to the API, able to generate a valid Response (ResponseInterface).
  * Also can include various information in debug mode.
  */
-abstract class ApiException extends \Exception {
+abstract class ApiException extends \Exception
+{
 
     /**
      * @var Response instance from Slim.
@@ -27,7 +28,7 @@ abstract class ApiException extends \Exception {
      *
      * @return Response Final response to give.
      */
-    public abstract function getApiResponse();
+    abstract public function getApiResponse();
 
     /**
      * Creates ApiResponse body.
@@ -36,7 +37,8 @@ abstract class ApiException extends \Exception {
      *
      * @return array|string response body
      */
-    protected function getApiResponseBody() {
+    protected function getApiResponseBody()
+    {
         if ($this->debug !== true) {
             return $this->getMessage();
         }
index de7e14f5c9cd16822bec865b5773a5d31ccc80d8..c727f4f0c9eec47e2fad40849b1fb418ef4b9adf 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace Shaarli\Api\Exceptions;
 
-
 use Slim\Http\Response;
 
 /**
index eed5afa57c1ba1eafbb08806a8d5605819950afe..eee152fe273375d3b5bd765f198552e363e21ee8 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace Shaarli\Api\Exceptions;
 
-
 use Slim\Http\Response;
 
 /**
index 8add8bcd776009e1cff804f3b8f47e5417ff4c6f..9625fe1a98156397b0210b80cf95bce8dd3a7821 100644 (file)
@@ -104,12 +104,20 @@ class ConfigPhp implements ConfigIO
 
         // Store all $conf['config']
         foreach ($conf['config'] as $key => $value) {
-            $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL;
+            $configStr .= '$GLOBALS[\'config\'][\''
+                . $key
+                .'\'] = '
+                .var_export($conf['config'][$key], true).';'
+                . PHP_EOL;
         }
 
         if (isset($conf['plugins'])) {
             foreach ($conf['plugins'] as $key => $value) {
-                $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($conf['plugins'][$key], true).';'. PHP_EOL;
+                $configStr .= '$GLOBALS[\'plugins\'][\''
+                    . $key
+                    .'\'] = '
+                    .var_export($conf['plugins'][$key], true).';'
+                    . PHP_EOL;
             }
         }
 
index b3d9752bcb4a9ed276dd58026be69d256c1bdf1e..dbb249374a7262053b5e03000d38b746c9772563 100644 (file)
@@ -34,8 +34,7 @@ function save_plugin_config($formData)
         // If there is no order, it means a disabled plugin has been enabled.
         if (isset($formData['order_' . $key])) {
             $plugins[(int) $formData['order_' . $key]] = $key;
-        }
-        else {
+        } else {
             $newEnabledPlugins[] = $key;
         }
     }
index d6784d6da6424e266a21702b7f0686746e91fe20..0f3154835d734d74c035f169035d9d684b50dcfb 100644 (file)
@@ -95,7 +95,6 @@ class LoginManager
             // The user client has a valid stay-signed-in cookie
             // Session information is updated with the current client information
             $this->sessionManager->storeLoginInfo($clientIpId);
-
         } elseif ($this->sessionManager->hasSessionExpired()
             || $this->sessionManager->hasClientIpChanged($clientIpId)
         ) {
index 8bf79d3e4c612efac7e815ca421b3c2acff14023..99e03370a763c0bdd8214ce43a18a6eccaab394c 100644 (file)
@@ -422,12 +422,12 @@ function init(description) {
   /**
    * Bulk actions
    */
-  const linkCheckboxes = document.querySelectorAll('.delete-checkbox');
+  const linkCheckboxes = document.querySelectorAll('.link-checkbox');
   const bar = document.getElementById('actions');
   [...linkCheckboxes].forEach((checkbox) => {
     checkbox.style.display = 'inline-block';
-    checkbox.addEventListener('click', () => {
-      const linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
+    checkbox.addEventListener('change', () => {
+      const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
       const count = [...linkCheckedCheckboxes].length;
       if (count === 0 && bar.classList.contains('open')) {
         bar.classList.toggle('open');
@@ -444,7 +444,7 @@ function init(description) {
       event.preventDefault();
 
       const links = [];
-      const linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
+      const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
       [...linkCheckedCheckboxes].forEach((checkbox) => {
         links.push({
           id: checkbox.value,
@@ -466,6 +466,25 @@ function init(description) {
     });
   }
 
+  /**
+   * Select all button
+   */
+  const selectAllButtons = document.querySelectorAll('.select-all-button');
+  [...selectAllButtons].forEach((selectAllButton) => {
+    selectAllButton.addEventListener('click', (e) => {
+      e.preventDefault();
+      const checked = selectAllButton.classList.contains('filter-off');
+      [...selectAllButtons].forEach((selectAllButton2) => {
+        selectAllButton2.classList.toggle('filter-off');
+        selectAllButton2.classList.toggle('filter-on');
+      });
+      [...linkCheckboxes].forEach((linkCheckbox) => {
+        linkCheckbox.checked = checked;
+        linkCheckbox.dispatchEvent(new Event('change'));
+      });
+    });
+  });
+
   /**
    * Tag list operations
    *
@@ -548,7 +567,7 @@ function init(description) {
       event.preventDefault();
       const block = findParent(event.target, 'div', { class: 'tag-list-item' });
       const tag = block.getAttribute('data-tag');
-      const refreshedToken = document.getElementById('token');
+      const refreshedToken = document.getElementById('token').value;
 
       if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) {
         const xhr = new XMLHttpRequest();
index 6b286f1ecdebdea0893f6a33ea04da1073472c52..760d8d6ab7272a5d8c929ccb8425fc5a8f7f054d 100644 (file)
@@ -381,8 +381,6 @@ body,
   box-shadow: 0 1px 0 $light-shadow, 0 1px 4px $dark-shadow inset;
   background: $almost-white;
   padding: 5px 5px 3px 15px;
-  width: 20%;
-  height: 20px;
   color: $dark-grey;
 }
 
@@ -742,7 +740,7 @@ body,
     font-size: 1em;
   }
 
-  .delete-checkbox {
+  .link-checkbox {
     display: none;
   }
 }
@@ -757,6 +755,14 @@ body,
   font-size: 1.3em;
 }
 
+.pin-link {
+  font-size: 1.3em;
+}
+
+.pinned-link {
+  color: $blue !important;
+}
+
 .linklist-item-description {
   position: relative;
   padding: 0 10px;
@@ -850,6 +856,10 @@ body,
   margin: 0 7px;
 }
 
+.ctrl-delete {
+  margin: 0 7px 0 0;
+}
+
 // 64em -> lg
 @media screen and (max-width: 64em) {
   .linklist-item-infos-url {
index 99ef0b5e6da82c9799f4ac583e5bd750a274b89a..dccf83b62b5862371b1447ea9d0fbc571721337b 100644 (file)
@@ -16,7 +16,7 @@
     },
     "require": {
         "php": ">=5.6",
-        "shaarli/netscape-bookmark-parser": "^2.0",
+        "shaarli/netscape-bookmark-parser": "^2.1",
         "erusev/parsedown": "^1.6",
         "slim/slim": "^3.0",
         "arthurhoaro/web-thumbnailer": "^1.1",
         "gettext/gettext": "^4.4"
     },
     "require-dev": {
-        "phpmd/phpmd" : "@stable",
+        "phpunit/phpcov": "*",
         "phpunit/phpunit": "^5.0",
-        "sebastian/phpcpd": "*",
-        "squizlabs/php_codesniffer": "2.*",
-        "phpunit/phpcov": "*"
+        "squizlabs/php_codesniffer": "2.*"
     },
     "autoload": {
         "psr-4": {
index 22c97fa95b8f31c62067b526eb7ca04effc60371..c43dad6f18c9a1e459c502960463ba458d9fbd2b 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "da7a0c081b61d949154c5d2e5370cbab",
+    "content-hash": "3876b34296fedb365517b785af8384de",
     "packages": [
         {
             "name": "arthurhoaro/web-thumbnailer",
         },
         {
             "name": "gettext/gettext",
-            "version": "v4.6.0",
+            "version": "v4.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/oscarotero/Gettext.git",
-                "reference": "cae84aff39a87e07bd6e5cddb5adb720a0ffa357"
+                "reference": "854ff5f5aaf92d2af7080ba8fc15718b27b5c89a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/cae84aff39a87e07bd6e5cddb5adb720a0ffa357",
-                "reference": "cae84aff39a87e07bd6e5cddb5adb720a0ffa357",
+                "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/854ff5f5aaf92d2af7080ba8fc15718b27b5c89a",
+                "reference": "854ff5f5aaf92d2af7080ba8fc15718b27b5c89a",
                 "shasum": ""
             },
             "require": {
                 "po",
                 "translation"
             ],
-            "time": "2018-06-26T16:51:09+00:00"
+            "time": "2018-08-27T15:40:19+00:00"
         },
         {
             "name": "gettext/languages",
             "source": {
                 "type": "git",
                 "url": "https://github.com/pubsubhubbub/php-publisher.git",
-                "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5"
+                "reference": "047b0faf6219071527a45942d6fef4dbc6d1d884"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/5008fc529b057251b48f4d17a10fdb20047ea8f5",
-                "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5",
+                "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/047b0faf6219071527a45942d6fef4dbc6d1d884",
+                "reference": "047b0faf6219071527a45942d6fef4dbc6d1d884",
                 "shasum": ""
             },
             "require": {
+                "ext-curl": "*",
                 "php": "~5.4 || ~7.0"
             },
             "type": "library",
                 "data",
                 "feeds",
                 "publishers",
-                "pubsubhubbub"
+                "pubsubhubbub",
+                "websub"
             ],
-            "time": "2018-05-22T11:56:26+00:00"
+            "time": "2018-10-09T05:20:28+00:00"
         },
         {
             "name": "shaarli/netscape-bookmark-parser",
-            "version": "v2.0.5",
+            "version": "v2.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/shaarli/netscape-bookmark-parser.git",
-                "reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315"
+                "reference": "819008ee42c4dd7e45d988176a4a22d6ed689577"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/ea6911a0ea3dd372fa7002593c5aef9c15a49315",
-                "reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315",
+                "url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/819008ee42c4dd7e45d988176a4a22d6ed689577",
+                "reference": "819008ee42c4dd7e45d988176a4a22d6ed689577",
                 "shasum": ""
             },
             "require": {
                 "katzgrau/klogger": "~1.0",
-                "php": ">=5.3.4"
+                "php": ">=5.6"
             },
             "require-dev": {
-                "phpunit/phpunit": "4.8.*"
+                "phpunit/phpunit": "^5.0"
             },
             "type": "library",
             "autoload": {
                 "bookmark",
                 "link",
                 "netscape",
-                "parse"
+                "parser"
             ],
-            "time": "2018-01-30T17:34:48+00:00"
+            "time": "2018-10-06T14:43:38+00:00"
         },
         {
             "name": "slim/slim",
-            "version": "3.10.0",
+            "version": "3.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slimphp/Slim.git",
-                "reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748"
+                "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slimphp/Slim/zipball/d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
-                "reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
+                "url": "https://api.github.com/repos/slimphp/Slim/zipball/d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
+                "reference": "d378e70431e78ee92ee32ddde61ecc72edf5dc0a",
                 "shasum": ""
             },
             "require": {
                 "micro",
                 "router"
             ],
-            "time": "2018-04-19T19:29:08+00:00"
+            "time": "2018-09-16T10:54:21+00:00"
         }
     ],
     "packages-dev": [
             ],
             "time": "2017-10-19T19:58:43+00:00"
         },
-        {
-            "name": "pdepend/pdepend",
-            "version": "2.5.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/pdepend/pdepend.git",
-                "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/pdepend/pdepend/zipball/9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
-                "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.7",
-                "symfony/config": "^2.3.0|^3|^4",
-                "symfony/dependency-injection": "^2.3.0|^3|^4",
-                "symfony/filesystem": "^2.3.0|^3|^4"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.8|^5.7",
-                "squizlabs/php_codesniffer": "^2.0.0"
-            },
-            "bin": [
-                "src/bin/pdepend"
-            ],
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "PDepend\\": "src/main/php/PDepend"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "description": "Official version of pdepend to be handled with Composer",
-            "time": "2017-12-13T13:21:38+00:00"
-        },
         {
             "name": "phpdocumentor/reflection-common",
             "version": "1.0.1",
             ],
             "time": "2017-07-14T14:27:02+00:00"
         },
-        {
-            "name": "phpmd/phpmd",
-            "version": "2.6.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpmd/phpmd.git",
-                "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpmd/phpmd/zipball/4e9924b2c157a3eb64395460fcf56b31badc8374",
-                "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374",
-                "shasum": ""
-            },
-            "require": {
-                "ext-xml": "*",
-                "pdepend/pdepend": "^2.5",
-                "php": ">=5.3.9"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.0",
-                "squizlabs/php_codesniffer": "^2.0"
-            },
-            "bin": [
-                "src/bin/phpmd"
-            ],
-            "type": "project",
-            "autoload": {
-                "psr-0": {
-                    "PHPMD\\": "src/main/php"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Manuel Pichler",
-                    "email": "github@manuel-pichler.de",
-                    "homepage": "https://github.com/manuelpichler",
-                    "role": "Project Founder"
-                },
-                {
-                    "name": "Other contributors",
-                    "homepage": "https://github.com/phpmd/phpmd/graphs/contributors",
-                    "role": "Contributors"
-                },
-                {
-                    "name": "Marc Würth",
-                    "email": "ravage@bluewin.ch",
-                    "homepage": "https://github.com/ravage84",
-                    "role": "Project Maintainer"
-                }
-            ],
-            "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.",
-            "homepage": "http://phpmd.org/",
-            "keywords": [
-                "mess detection",
-                "mess detector",
-                "pdepend",
-                "phpmd",
-                "pmd"
-            ],
-            "time": "2017-01-20T14:41:10+00:00"
-        },
         {
             "name": "phpspec/prophecy",
             "version": "1.8.0",
             "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
             "time": "2017-02-18T15:18:39+00:00"
         },
-        {
-            "name": "sebastian/phpcpd",
-            "version": "3.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/phpcpd.git",
-                "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564",
-                "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.6|^7.0",
-                "phpunit/php-timer": "^1.0.6",
-                "sebastian/finder-facade": "^1.1",
-                "sebastian/version": "^1.0|^2.0",
-                "symfony/console": "^2.7|^3.0|^4.0"
-            },
-            "bin": [
-                "phpcpd"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Copy/Paste Detector (CPD) for PHP code.",
-            "homepage": "https://github.com/sebastianbergmann/phpcpd",
-            "time": "2017-11-16T08:49:28+00:00"
-        },
         {
             "name": "sebastian/recursion-context",
             "version": "2.0.0",
             ],
             "time": "2017-05-22T02:43:20+00:00"
         },
-        {
-            "name": "symfony/config",
-            "version": "v3.4.14",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/config.git",
-                "reference": "7b08223b7f6abd859651c56bcabf900d1627d085"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/7b08223b7f6abd859651c56bcabf900d1627d085",
-                "reference": "7b08223b7f6abd859651c56bcabf900d1627d085",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5.9|>=7.0.8",
-                "symfony/filesystem": "~2.8|~3.0|~4.0",
-                "symfony/polyfill-ctype": "~1.8"
-            },
-            "conflict": {
-                "symfony/dependency-injection": "<3.3",
-                "symfony/finder": "<3.3"
-            },
-            "require-dev": {
-                "symfony/dependency-injection": "~3.3|~4.0",
-                "symfony/event-dispatcher": "~3.3|~4.0",
-                "symfony/finder": "~3.3|~4.0",
-                "symfony/yaml": "~3.0|~4.0"
-            },
-            "suggest": {
-                "symfony/yaml": "To use the yaml reference dumper"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.4-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Component\\Config\\": ""
-                },
-                "exclude-from-classmap": [
-                    "/Tests/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony Config Component",
-            "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
-        },
         {
             "name": "symfony/console",
-            "version": "v3.4.14",
+            "version": "v3.4.17",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73"
+                "reference": "3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/6b217594552b9323bcdcfc14f8a0ce126e84cd73",
-                "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73",
+                "url": "https://api.github.com/repos/symfony/console/zipball/3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b",
+                "reference": "3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
+            "time": "2018-10-02T16:33:53+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v3.4.14",
+            "version": "v3.4.17",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc"
+                "reference": "0a612e9dfbd2ccce03eb174365f31ecdca930ff6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/d5a058ff6ecad26b30c1ba452241306ea34c65cc",
-                "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/0a612e9dfbd2ccce03eb174365f31ecdca930ff6",
+                "reference": "0a612e9dfbd2ccce03eb174365f31ecdca930ff6",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
-        },
-        {
-            "name": "symfony/dependency-injection",
-            "version": "v3.4.14",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "1c0e679e522591fd744fdf242fec41a43d62b2b1"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1c0e679e522591fd744fdf242fec41a43d62b2b1",
-                "reference": "1c0e679e522591fd744fdf242fec41a43d62b2b1",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5.9|>=7.0.8",
-                "psr/container": "^1.0"
-            },
-            "conflict": {
-                "symfony/config": "<3.3.7",
-                "symfony/finder": "<3.3",
-                "symfony/proxy-manager-bridge": "<3.4",
-                "symfony/yaml": "<3.4"
-            },
-            "provide": {
-                "psr/container-implementation": "1.0"
-            },
-            "require-dev": {
-                "symfony/config": "~3.3|~4.0",
-                "symfony/expression-language": "~2.8|~3.0|~4.0",
-                "symfony/yaml": "~3.4|~4.0"
-            },
-            "suggest": {
-                "symfony/config": "",
-                "symfony/expression-language": "For using expressions in service container configuration",
-                "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
-                "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
-                "symfony/yaml": ""
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.4-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Component\\DependencyInjection\\": ""
-                },
-                "exclude-from-classmap": [
-                    "/Tests/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony DependencyInjection Component",
-            "homepage": "https://symfony.com",
-            "time": "2018-07-29T15:19:31+00:00"
-        },
-        {
-            "name": "symfony/filesystem",
-            "version": "v3.4.14",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/filesystem.git",
-                "reference": "a59f917e3c5d82332514cb4538387638f5bde2d6"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/a59f917e3c5d82332514cb4538387638f5bde2d6",
-                "reference": "a59f917e3c5d82332514cb4538387638f5bde2d6",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.5.9|>=7.0.8",
-                "symfony/polyfill-ctype": "~1.8"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.4-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Component\\Filesystem\\": ""
-                },
-                "exclude-from-classmap": [
-                    "/Tests/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Fabien Potencier",
-                    "email": "fabien@symfony.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony Filesystem Component",
-            "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
+            "time": "2018-10-02T16:33:53+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v3.4.14",
+            "version": "v3.4.17",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a"
+                "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/8a84fcb207451df0013b2c74cbbf1b62d47b999a",
-                "reference": "8a84fcb207451df0013b2c74cbbf1b62d47b999a",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/54ba444dddc5bd5708a34bd095ea67c6eb54644d",
+                "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
+            "time": "2018-10-03T08:46:40+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
         },
         {
             "name": "symfony/yaml",
-            "version": "v3.4.14",
+            "version": "v3.4.17",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2"
+                "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/810af2d35fc72b6cf5c01116806d2b65ccaaf2e2",
-                "reference": "810af2d35fc72b6cf5c01116806d2b65ccaaf2e2",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/640b6c27fed4066d64b64d5903a86043f4a4de7f",
+                "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2018-07-26T11:19:56+00:00"
+            "time": "2018-10-02T16:33:53+00:00"
         },
         {
             "name": "theseer/fdomdocument",
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": {
-        "pubsubhubbub/publisher": 20,
-        "phpmd/phpmd": 0
+        "pubsubhubbub/publisher": 20
     },
     "prefer-stable": false,
     "prefer-lowest": false,
diff --git a/doc/custom_theme/main.html b/doc/custom_theme/main.html
new file mode 100644 (file)
index 0000000..cc2a703
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+
+{#
+The entry point for the ReadTheDocs Theme.
+Any theme customisations should override this file to redefine blocks defined in
+the various templates. The custom theme should only need to define a main.html
+which `{% extends "base.html" %}` and defines various blocks which will replace
+the blocks defined in base.html and its included child templates.
+#}
+
+{%- block site_meta %}
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+{%- if 'media.readthedocs.org' not in config.extra_css[0] %}
+<meta name="robots" content="noindex, nofollow">
+{%- endif %}
+
+{% if page and page.is_homepage %}<meta name="description" content="{{ config.site_description }}">{% endif %}
+{% if config.site_author %}<meta name="author" content="{{ config.site_author }}">{% endif %}
+{%- endblock %}
index 49c20c9cebcdfcef5fbb530327af74f86ebbde52..67fdd70f9919dbd997db752d75b7625c07abc33e 100644 (file)
@@ -51,7 +51,7 @@ See [Theming](Theming) for a list of community-contributed themes, and an instal
 - [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among French shaarliers: [shaarli.fr](http://shaarli.fr/))
 - [Shaarlimages](https://github.com/BoboTiG/shaarlimages) - An image-oriented aggregator for Shaarlis
 - [mknexen/shaarli-api](https://github.com/mknexen/shaarli-api) - A REST API for Shaarli
-- [Self dead link](https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming).
+- [Self dead link](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming).
 - [Bookmark Archiver](https://github.com/pirate/bookmark-archiver) - Save an archived copy of all websites starred using browser bookmarks/Shaarli/Delicious/Instapaper/Unmark.it/Pocket/Pinboard. Outputs browseable html. 
 
 ## Alternatives to Shaarli
index e281dc859239cae2ead0801671e6c8ccf1dc197a..78083a466729e2b7466f9d017823a99edaa160fa 100644 (file)
@@ -18,7 +18,7 @@ Version | Status | Shaarli compatibility
 7.2 | Supported | Yes
 7.1 | Supported | Yes
 7.0 | Supported | Yes
-5.6 | Supported | Yes
+5.6 | EOL: 2018-12-31 | Yes (up to Shaarli 0.10.x)
 5.5 | EOL: 2016-07-10 | Yes
 5.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x)
 5.3 | EOL: 2014-08-14 | Yes (up to Shaarli 0.8.x)
@@ -397,6 +397,7 @@ http {
 ```
 
 ## Proxies
+
 If Shaarli is served behind a proxy (i.e. there is a proxy server between clients and the web server hosting Shaarli), please refer to the proxy server documentation for proper configuration. In particular, you have to ensure that the following server variables are properly set:
 
 - `X-Forwarded-Proto`
@@ -405,6 +406,12 @@ If Shaarli is served behind a proxy (i.e. there is a proxy server between client
 
 See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues.
 
+## Robots and crawlers
+
+Shaarli disallows indexing and crawling of your local documentation pages by search engines, using `<meta name="robots">` HTML tags.
+Your Shaarli instance and other pages you host may still be indexed by various robots on the public Internet.
+You may want to setup a robots.txt file or other crawler control mechanism on your server.
+See [[1]](https://en.wikipedia.org/wiki/Robots_exclusion_standard), [[2]](https://support.google.com/webmasters/answer/6062608?hl=en) and [[3]](https://developers.google.com/search/reference/robots_meta_tag)
 
 ## See also
 
index 4910ff6c67ea44bd5171c67adfcc33ac1c094c08..9a16fc621944f0e40477eff39e5ee4ea30ee2b68 100644 (file)
@@ -15,7 +15,6 @@ While logged in to your Shaarli, you can add new Shaares in several ways:
 
  * [+Shaare button](#shaare-button)
  * [Bookmarklet](#bookmarklet)
- * [Firefox Share](#firefox-share)
  * Third-party [apps and browser addons](Community-&-Related-software.md#mobile-apps)
  * [REST API](https://shaarli.github.io/api-documentation/)
 
@@ -52,22 +51,6 @@ bookmarklet in your browser! The same `New Shaare` dialog as above is displayed.
 ![](images/bookmarklet.png)
 
 
-### Firefox Share
-
-Before using Firefox Share, you must first add Shaarli as a sharing provider:
-
-- Click the `Tools` button in the top bar
-- Click the `✚Add to Firefox social` button and accept the activation.
-
-Once this is done, you can share any URL you are visiting by clicking the Firefox
-_Share_ button ![images/firefoxshare.png](images/firefoxshare.png)
-
-| Note | Firefox Share is no longer available for Firefox 57 and later versions. |
-|---------|---------|
-
-| Note | Your Shaarli instance must be hosted on an HTTPS (SSL/TLS secure connection) enabled server for Firefox Share to work. Firefox Share will not work over plaintext HTTP connections. |
-|---------|---------|
-
 --------------------------------------------------------------------------------
 
 ## Editing Shaares
diff --git a/doc/md/images/icon.png b/doc/md/images/icon.png
new file mode 100644 (file)
index 0000000..530d746
Binary files /dev/null and b/doc/md/images/icon.png differ
index c18332b410feba194590b221e1150e4e073c50de..220eeec144b85ddd787f7235d328182c607853db 100644 (file)
@@ -1,25 +1,19 @@
-# [Shaarli](https://github.com/shaarli/Shaarli/) documentation
+# <img src="images/icon.png" width="20px" height="20px"> Shaarli
 
 The personal, minimalist, super-fast, database free, bookmarking service.
 
 Do you want to share the links you discover?
-Shaarli is a minimalist link sharing service that you can install on your own server.
+Shaarli is a minimalist bookmark manager and link sharing service that you can install on your own server.
 It is designed to be personal (single-user), fast and handy.
 
 <!-- TODO screenshots -->
 
-Here you can find some info on how to use, configure, tweak and solve problems with your Shaarli.
-For general information, read the [README](https://github.com/shaarli/Shaarli/blob/master/README.md).
+Visit the pages in the sidebar to find information on how to setup, use, configure, tweak and troubleshoot Shaarli.
 
-If you have any questions or ideas, please join the [chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)), post them in our [general discussion](https://github.com/shaarli/Shaarli/issues/308) or read the current [issues](https://github.com/shaarli/Shaarli/issues).
-
-If you've found a bug, please create a [new issue](https://github.com/shaarli/Shaarli/issues/new).
-
-If you would like a feature added to Shaarli, check the issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin).
 
 * [GitHub project page](https://github.com/shaarli/Shaarli)
-* [Online documentation](https://shaarli.readthedocs.io/) (this page)
-* [Latest Shaarli releases](https://github.com/shaarli/Shaarli/releases)
+* [Online documentation](https://shaarli.readthedocs.io/)
+* [Latest releases](https://github.com/shaarli/Shaarli/releases)
 * [Changelog](https://github.com/shaarli/Shaarli/blob/master/CHANGELOG.md)
 
 
@@ -30,87 +24,70 @@ It runs the latest development version of Shaarli and is updated/reset daily.
 
 Login: `demo`; Password: `demo`
 
-<!-- TODO review everything below this point -->
-
-
 ## Features
 
 Shaarli can be used:
 
-- to share, comment and save interesting links and news.
-- to bookmark useful/frequent personal links (as private links) and share them between computers.
-- as a minimal blog/microblog/writing platform (no character limit).
-- as a read-it-later list (for example items tagged `readlater`).
-- to draft and save articles/posts/ideas.
-- to keep code snippets.
-- to keep notes and documentation.
-- as a shared clipboard/notepad/pastebin between machines.
-- as a todo list.
-- to store playlists (e.g. with the `music` or `video` tags).
+- to share, comment and save interesting links and news
+- to bookmark useful/frequent links and share them between computers
+- as a minimal blog/microblog/writing platform
+- as a read-it-later list
+- to draft and save articles/posts/ideas
+- to keep notes, documentation and code snippets
+- as a shared clipboard/notepad/pastebin between machines
+- as a todo list
+- to store media playlists
 - to keep extracts/comments from webpages that may disappear.
-- to keep track of ongoing discussions (for example items tagged `discussion`).
-- [to feed RSS aggregators](http://shaarli.chassegnouf.net/?9Efeiw) (planets) with specific tags.
-- to feed other social networks, blogs... using RSS feeds and external services (dlvr.it, ifttt.com ...).
+- to keep track of ongoing discussions
+- to feed other blogs, aggregators, social networks... using RSS feeds
 
-### Interface
+### Edit, view and search your links
 
-- minimalist design (simple is beautiful)
+- Minimalist design
 - FAST
-- ATOM and RSS feeds
-- views:
-    - paginated link list (with image and video thumbnails)
-    - tag cloud
-    - picture wall: image and video thumbnails (with lazy loading)
-    - daily: newspaper-like daily digest
-    - daily RSS feed
-- permalinks for easy reference
-- links can be public or private
-- thumbnail generation for images and video services
+- Customizable link titles and descriptions
+- Tags to organize your links (features tag autocompletion, renaming, merging and deletion)
+- Search by tag or using the full-text search
+- Public and private links (visible only to logged-in users)
+- Unique permalinks for easy reference
+- Paginated link list (with image and video thumbnails)
+- Tag cloud and list views
+- Picture wall: image and video thumbnails view (with lazy loading)
+- ATOM and RSS feeds (can also be filtered using tags or text search)
+- Daily: newspaper-like daily digest (and daily RSS feed)
 - URL cleanup: automatic removal of `?utm_source=...`, `fb=...`
-- extensible through [plugins](https://shaarli.readthedocs.io/en/master/Plugins/#plugin-usage)
-
-### Tag, view and search your links
-
-- add a custom title and description to archived links
-- add tags to classify and search links
-  - features tag autocompletion, renaming, merging and deletion
-- full-text and tag search
+- Extensible through [plugins](https://shaarli.readthedocs.io/en/master/Plugins/#plugin-usage)
 
 ### Easy setup
 
-- dead-simple installation: drop the files, open the page
-- links are stored in a file
-    - compact storage
-    - no database required
-    - easy backup: simply copy the datastore file
-- import and export links as Netscape bookmarks
+- Dead-simple installation: drop the files, open the page
+- Links are stored in a file (no database required, easy backup: simply copy the datastore file)
+- Import and export links as Netscape bookmarks compatible with most Web browsers
 
 ### Accessibility
 
-- bookmarlet to share links in one click
-- support for mobile browsers
-- degrades gracefully with Javascript disabled
-- easy page customization through HTML/CSS/RainTPL
+- Bookmarklet and other tools to share links in one click
+- Support for mobile browsers
+- Degrades gracefully with Javascript disabled
+- Easy page customization through HTML/CSS/RainTPL
 
 ### Security
 
-- discreet pop-up notification when a new release is available
-- bruteforce protection on the login form
-- protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) and session cookie hijacking
+- Discreet pop-up notification when a new release is available
+- Bruteforce protection on the login form
+- Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) and session cookie hijacking
 
 <!-- TODO Limitations -->
 
 ### REST API
 
-Easily extensible by any client using the REST API exposed by Shaarli.
-
-See the [API documentation](http://shaarli.github.io/api-documentation/).
+- Easily extensible by any client using the REST API exposed by Shaarli ([API documentation](http://shaarli.github.io/api-documentation/)).
 
 ## About
 
 ### Shaarli community fork
 
-This friendly fork is maintained by the Shaarli community at https://github.com/shaarli/Shaarli
+This friendly fork is maintained by the Shaarli community at <https://github.com/shaarli/Shaarli>
 
 This is a community fork of the original [Shaarli](https://github.com/sebsauvage/Shaarli/) project by [Sébastien Sauvage](http://sebsauvage.net/).
 
@@ -123,16 +100,15 @@ in this repository, and will keep maintaining the project for the foreseeable
 future, while keeping Shaarli simple and efficient.
 
 
-### Contributing
+### Contributing and getting help
 
-If you'd like to help, please:
+Feedback is very appreciated!
 
-- have a look at the open [issues](https://github.com/shaarli/Shaarli/issues)
-and [pull requests](https://github.com/shaarli/Shaarli/pulls)
-- feel free to report bugs (feedback is much appreciated)
-- suggest new features and improvements to both code and [documentation](https://github.com/shaarli/Shaarli/tree/master/doc/md/)
-- propose solutions to existing problems
-- submit pull requests :-)
+- If you have any questions or ideas, please join the [chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)), post them in our [general discussion](https://github.com/shaarli/Shaarli/issues/308) or read the current [issues](https://github.com/shaarli/Shaarli/issues).
+- Have a look at the open [issues](https://github.com/shaarli/Shaarli/issues) and [pull requests](https://github.com/shaarli/Shaarli/pulls)
+- If you would like a feature added to Shaarli, check the issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin).
+- If you've found a bug, please create a [new issue](https://github.com/shaarli/Shaarli/issues/new).
+- Feel free to propose solutions to existing problems, help us improve the documentation and translations, and submit pull requests :-)
 
 
 ### License
index 155eb52ee2de3c71c5ca8f0ca6d2689d09baba7c..102c80da93636a5de87d265c4cbc25100dd739de 100644 (file)
@@ -1,15 +1,15 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: Shaarli\n"
-"POT-Creation-Date: 2018-07-17 13:04+0200\n"
-"PO-Revision-Date: 2018-07-17 13:07+0200\n"
+"POT-Creation-Date: 2018-10-06 13:08+0200\n"
+"PO-Revision-Date: 2018-10-06 13:08+0200\n"
 "Last-Translator: \n"
 "Language-Team: Shaarli\n"
 "Language: fr_FR\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 2.0.9\n"
+"X-Generator: Poedit 2.1.1\n"
 "X-Poedit-Basepath: ../../../..\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "X-Poedit-SourceCharset: UTF-8\n"
@@ -48,7 +48,7 @@ msgstr "le fichier n'est pas accessible en Ã©criture"
 #: application/Cache.php:16
 #, php-format
 msgid "Cannot purge %s: no directory"
-msgstr "Impossible de purger %s: le répertoire n'existe pas"
+msgstr "Impossible de purger %s : le répertoire n'existe pas"
 
 #: application/FeedBuilder.php:151
 msgid "Direct link"
@@ -98,17 +98,15 @@ msgstr "Vous devez utiliser un entier comme clé."
 
 #: application/LinkDB.php:145
 msgid "Array offset and link ID must be equal."
-msgstr "La clé du tableau et l'ID du lien doivent Ãªtre Ã©gaux."
+msgstr "La clé du tableau et l'ID du lien doivent Ãªtre identiques."
 
 #: application/LinkDB.php:251
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
 msgid ""
 "The personal, minimalist, super-fast, database free, bookmarking service"
 msgstr ""
-"Le gestionnaire de marque-page personnel, minimaliste, et sans base de "
+"Le gestionnaire de marque-pages personnel, minimaliste, et sans base de "
 "données"
 
 #: application/LinkDB.php:253
@@ -125,11 +123,11 @@ msgstr ""
 "Bienvenue sur Shaarli ! Ceci est votre premier marque-page public. Pour me "
 "modifier ou me supprimer, vous devez d'abord vous connecter.\n"
 "\n"
-"Pour apprendre comment utiliser Shaarli, consultez le lien Â« Documentation Â» "
-"en bas de page.\n"
+"Pour apprendre Ã  utiliser Shaarli, consultez le lien Â« Documentation Â» en "
+"bas de page.\n"
 "\n"
 "Vous utilisez la version supportée par la communauté du projet original "
-"Shaarli, de Sébastien Sauvage."
+"Shaarli de Sébastien Sauvage."
 
 #: application/LinkDB.php:267
 msgid "My secret stuff... - Pastebin.com"
@@ -185,14 +183,14 @@ msgid ""
 "php-gd extension must be loaded to use thumbnails. Thumbnails are now "
 "disabled. Please reload the page."
 msgstr ""
-"php-gd extension must be loaded to use thumbnails. Thumbnails are now "
-"disabled. Please reload the page."
+"l'extension php-gd doit Ãªtre chargée pour utiliser les miniatures. Les "
+"miniatures sont désormais désactivées. Rechargez la page."
 
 #: application/Updater.php:86
 msgid "Couldn't retrieve Updater class methods."
 msgstr "Impossible de récupérer les méthodes de la classe Updater."
 
-#: application/Updater.php:514 index.php:1023
+#: application/Updater.php:514 index.php:1022
 msgid ""
 "You have enabled or changed thumbnails mode. <a href=\"?do=thumbs_update"
 "\">Please synchronize them</a>."
@@ -200,17 +198,17 @@ msgstr ""
 "Vous avez activé ou changé le mode de miniatures. <a href=\"?do=thumbs_update"
 "\">Merci de les synchroniser</a>."
 
-#: application/Updater.php:566
+#: application/Updater.php:586
 msgid "An error occurred while running the update "
 msgstr "Une erreur s'est produite lors de l'exécution de la mise Ã  jour "
 
-#: application/Updater.php:606
+#: application/Updater.php:626
 msgid "Updates file path is not set, can't write updates."
 msgstr ""
 "Le chemin vers le fichier de mise Ã  jour n'est pas défini, impossible "
 "d'écrire les mises Ã  jour."
 
-#: application/Updater.php:611
+#: application/Updater.php:631
 msgid "Unable to write updates in "
 msgstr "Impossible d'écrire les mises Ã  jour dans "
 
@@ -286,74 +284,66 @@ msgstr "NON. Vous Ãªtes banni pour le moment. Revenez plus tard."
 
 #: index.php:273
 msgid "Wrong login/password."
-msgstr "Nom d'utilisateur ou mot de passe incorrects."
+msgstr "Nom d'utilisateur ou mot de passe incorrect(s)."
 
-#: index.php:483 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:46
+#: index.php:482 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:46
 msgid "Daily"
 msgstr "Quotidien"
 
-#: index.php:589 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
+#: index.php:588 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
 #: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:75
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:99
 msgid "Login"
 msgstr "Connexion"
 
-#: index.php:606 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:41
+#: index.php:605 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:41
 msgid "Picture wall"
 msgstr "Mur d'images"
 
-#: index.php:683 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
+#: index.php:682 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
 #: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
 msgid "Tag cloud"
 msgstr "Nuage de tags"
 
-#: index.php:716 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
+#: index.php:715 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
 msgid "Tag list"
 msgstr "Liste des tags"
 
-#: index.php:941 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
+#: index.php:940 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
 msgid "Tools"
 msgstr "Outils"
 
-#: index.php:950
+#: index.php:949
 msgid "You are not supposed to change a password on an Open Shaarli."
 msgstr ""
 "Vous n'êtes pas censé modifier le mot de passe d'un Shaarli en mode ouvert."
 
-#: index.php:955 index.php:997 index.php:1085 index.php:1116 index.php:1221
+#: index.php:954 index.php:996 index.php:1084 index.php:1116 index.php:1221
 msgid "Wrong token."
 msgstr "Jeton invalide."
 
-#: index.php:960
+#: index.php:959
 msgid "The old password is not correct."
 msgstr "L'ancien mot de passe est incorrect."
 
-#: index.php:980
+#: index.php:979
 msgid "Your password has been changed"
 msgstr "Votre mot de passe a Ã©té modifié"
 
-#: index.php:985
-#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
+#: index.php:984 tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
 msgid "Change password"
-msgstr "Modification du mot de passe"
+msgstr "Modifier le mot de passe"
 
-#: index.php:1043
+#: index.php:1042
 msgid "Configuration was saved."
-msgstr "La configuration a Ã©té sauvegardé."
+msgstr "La configuration a Ã©té sauvegardée."
 
-#: index.php:1068 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
+#: index.php:1067 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
 msgid "Configure"
 msgstr "Configurer"
 
-#: index.php:1079 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
+#: index.php:1078 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
 msgid "Manage tags"
 msgstr "Gérer les tags"
@@ -381,7 +371,6 @@ msgid "Edit"
 msgstr "Modifier"
 
 #: index.php:1281 index.php:1351
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26
 msgid "Shaare"
 msgstr "Shaare"
@@ -390,15 +379,19 @@ msgstr "Shaare"
 msgid "Note: "
 msgstr "Note : "
 
-#: index.php:1360 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
+#: index.php:1359
+msgid "Invalid link ID provided"
+msgstr ""
+
+#: index.php:1379
 msgid "Export"
 msgstr "Exporter"
 
-#: index.php:1422 tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
+#: index.php:1441
 msgid "Import"
 msgstr "Importer"
 
-#: index.php:1432
+#: index.php:1451
 #, php-format
 msgid ""
 "The file you are trying to upload is probably bigger than what this "
@@ -408,20 +401,20 @@ msgstr ""
 "le serveur web peut accepter (%s). Merci de l'envoyer en parties plus "
 "légères."
 
-#: index.php:1471 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
+#: index.php:1490 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
 msgid "Plugin administration"
-msgstr "Administration des extensions"
+msgstr "Administration des plugins"
 
-#: index.php:1523 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
+#: index.php:1542 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
 msgid "Thumbnails update"
 msgstr "Mise Ã  jour des miniatures"
 
-#: index.php:1695
+#: index.php:1714
 msgid "Search: "
 msgstr "Recherche : "
 
-#: index.php:1735
+#: index.php:1754
 #, php-format
 msgid ""
 "<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
@@ -431,16 +424,16 @@ msgid ""
 "custom hostname without a dot causes cookie storage to fail. We recommend "
 "accessing your server via it's IP address or Fully Qualified Domain Name.<br>"
 msgstr ""
-"<pre>Les sesssions ne semble pas fonctionner sur ce serveur.<br>Assurez vous "
-"que la variable Â« session.save_path Â» est correctement définie dans votre "
-"fichier de configuration PHP, et que vous y avez les droits d'écriture."
-"<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains navigateurs, "
-"accéder Ã  votre serveur depuis un nom d'hôte comme Â« localhost Â» ou autre "
-"nom personnalisé sans point '.' entraine l'échec de la sauvegarde des "
-"cookies. Nous vous recommandons d'accéder Ã  votre serveur depuis son adresse "
-"IP ou un <em>Fully Qualified Domain Name</em>.<br>"
-
-#: index.php:1745
+"<pre>Les sesssions ne semblent pas fonctionner sur ce serveur.<br>Assurez "
+"vous que la variable Â« session.save_path Â» est correctement définie dans "
+"votre fichier de configuration PHP, et que vous avez les droits d'écriture "
+"dessus.<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains "
+"navigateurs, accéder Ã  votre serveur depuis un nom d'hôte comme Â« localhost "
+"» ou autre nom personnalisé sans point '.' entraine l'échec de la sauvegarde "
+"des cookies. Nous vous recommandons d'accéder Ã  votre serveur depuis son "
+"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>"
+
+#: index.php:1764
 msgid "Click to try again."
 msgstr "Cliquer ici pour réessayer."
 
@@ -455,7 +448,7 @@ msgstr "Shaare"
 
 #: plugins/addlink_toolbar/addlink_toolbar.php:50
 msgid "Adds the addlink input on the linklist page."
-msgstr "Ajout le formulaire d'ajout de liens sur la page principale."
+msgstr "Ajoute le formulaire d'ajout de liens sur la page principale."
 
 #: plugins/archiveorg/archiveorg.php:23
 msgid "View on archive.org"
@@ -471,7 +464,7 @@ msgid ""
 "developers."
 msgstr ""
 "Une extension de démonstration couvrant tous les cas d'utilisation pour les "
-"designers et les développeurs."
+"designers de thèmes et les développeurs d'extensions."
 
 #: plugins/isso/isso.php:20
 msgid ""
@@ -481,12 +474,13 @@ msgstr ""
 "Erreur de l'extension Isso : Merci de définir le paramètre Â« ISSO_SERVER Â» "
 "dans la page d'administration des extensions."
 
-#: plugins/isso/isso.php:63
+#: plugins/isso/isso.php:90
 msgid "Let visitor comment your shaares on permalinks with Isso."
 msgstr ""
-"Permet aux visiteurs de commenter vos shaares sur les permaliens avec Isso."
+"Permettre aux visiteurs de commenter vos shaares sur les permaliens avec "
+"Isso."
 
-#: plugins/isso/isso.php:64
+#: plugins/isso/isso.php:91
 msgid "Isso server URL (without 'http://')"
 msgstr "URL du serveur Isso (sans 'http://')"
 
@@ -578,7 +572,7 @@ msgstr "Active la publication de flux vers PubSubHubbub."
 
 #: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68
 msgid "For each link, add a QRCode icon."
-msgstr "Pour chaque liens, ajouter une icône de QRCode."
+msgstr "Pour chaque lien, ajouter une icône de QRCode."
 
 #: plugins/wallabag/wallabag.php:21
 msgid ""
@@ -603,35 +597,17 @@ msgstr "Version de l'API Wallabag (1 ou 2)"
 #: tests/LanguagesTest.php:214 tests/LanguagesTest.php:227
 #: tests/languages/fr/LanguagesFrTest.php:160
 #: tests/languages/fr/LanguagesFrTest.php:173
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:85
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:85
 msgid "Search"
 msgid_plural "Search"
 msgstr[0] "Rechercher"
 msgstr[1] "Rechercher"
 
-#: tmp/404.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
-msgid "Sorry, nothing to see here."
-msgstr "Désolé, il y a rien Ã  voir ici."
-
 #: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
 msgid "URL or leave empty to post a note"
 msgstr "URL ou laisser vide pour créer une note"
 
-#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
-msgid "Current password"
-msgstr "Mot de passe actuel"
-
-#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
-msgid "New password"
-msgstr "Nouveau mot de passe"
-
-#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
-msgid "Change"
-msgstr "Changer"
-
 #: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
 msgid "Tag"
 msgstr "Tag"
 
@@ -661,6 +637,34 @@ msgstr "Vous pouvez aussi modifier les tags dans la"
 msgid "tag list"
 msgstr "liste des tags"
 
+#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:143
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:296
+msgid "All"
+msgstr "Tous"
+
+#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:147
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:300
+msgid "Only common media hosts"
+msgstr "Seulement les hébergeurs de média connus"
+
+#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:151
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:304
+msgid "None"
+msgstr "Aucune"
+
+#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:158
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:281
+msgid "You need to enable the extension <code>php-gd</code> to use thumbnails."
+msgstr ""
+"Vous devez activer l'extension <code>php-gd</code> pour utiliser les "
+"miniatures."
+
+#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:162
+#, fuzzy
+#| msgid "Enable thumbnails"
+msgid "Synchonize thumbnails"
+msgstr "Activer les miniatures"
+
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
 msgid "title"
 msgstr "titre"
@@ -678,22 +682,18 @@ msgid "Theme"
 msgstr "Thème"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:78
 msgid "Language"
 msgstr "Langue"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
 msgid "Timezone"
 msgstr "Fuseau horaire"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
 msgid "Continent"
 msgstr "Continent"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
 msgid "City"
 msgstr "Ville"
 
@@ -734,25 +734,21 @@ msgid "Do not show any links if the user is not logged in"
 msgstr "N'afficher aucun lien sans Ãªtre connecté"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:231
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
 msgid "Check updates"
 msgstr "Vérifier les mises Ã  jour"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:232
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
 msgid "Notify me when a new release is ready"
 msgstr "Me notifier lorsqu'une nouvelle version est disponible"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:247
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
 msgid "Enable REST API"
 msgstr "Activer l'API REST"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:248
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
 msgid "Allow third party software to use Shaarli such as mobile application"
 msgstr ""
-"Permets aux applications tierces d'utiliser Shaarli, par exemple les "
+"Permet aux applications tierces d'utiliser Shaarli, par exemple les "
 "applications mobiles"
 
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:263
@@ -763,30 +759,11 @@ msgstr "Clé d'API secrète"
 msgid "Enable thumbnails"
 msgstr "Activer les miniatures"
 
-#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:281
-msgid "You need to enable the extension <code>php-gd</code> to use thumbnails."
-msgstr ""
-"Vous devez activer l'extension <code>php-gd</code> pour utiliser les "
-"miniatures."
-
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:285
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56
 msgid "Synchronize thumbnails"
 msgstr "Synchroniser les miniatures"
 
-#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:296
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
-msgid "All"
-msgstr "Tous"
-
-#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:300
-msgid "Only common media hosts"
-msgstr "Seulement les hébergeurs de média connus"
-
-#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:304
-msgid "None"
-msgstr "Aucune"
-
 #: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:312
 #: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
 #: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
@@ -847,107 +824,13 @@ msgid "Tags"
 msgstr "Tags"
 
 #: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:167
 msgid "Private"
 msgstr "Privé"
 
 #: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
 msgid "Apply Changes"
-msgstr "Appliquer"
-
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
-msgid "Export Database"
-msgstr "Exporter les données"
-
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
-msgid "Selection"
-msgstr "Choisir"
-
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
-msgid "Public"
-msgstr "Publics"
-
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
-msgid "Prepend note permalinks with this Shaarli instance's URL"
-msgstr "Préfixer les liens de notes avec l'URL de l'instance de Shaarli"
-
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
-msgid "Useful to import bookmarks in a web browser"
-msgstr "Utile pour importer les marques-pages dans un navigateur"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
-msgid "Import Database"
-msgstr "Importer des données"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
-msgid "Maximum size allowed:"
-msgstr "Taille maximum autorisée :"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
-msgid "Visibility"
-msgstr "Visibilité"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
-msgid "Use values from the imported file, default to public"
-msgstr ""
-"Utiliser les valeurs présentes dans le fichier d'import, public par défaut"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
-msgid "Import all bookmarks as private"
-msgstr "Importer tous les liens comme privés"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
-msgid "Import all bookmarks as public"
-msgstr "Importer tous les liens comme publics"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
-msgid "Overwrite existing bookmarks"
-msgstr "Remplacer les liens existants"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
-msgid "Duplicates based on URL"
-msgstr "Les doublons s'appuient sur les URL"
-
-#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
-msgid "Add default tags"
-msgstr "Ajouter des tags par défaut"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
-msgid "Install Shaarli"
-msgstr "Installation de Shaarli"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
-msgid "It looks like it's the first time you run Shaarli. Please configure it."
-msgstr ""
-"Il semblerait que Ã§a soit la première fois que vous lancez Shaarli. Merci de "
-"le configurer."
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
-#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:151
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151
-msgid "Username"
-msgstr "Nom d'utilisateur"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
-#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
-#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:152
-msgid "Password"
-msgstr "Mot de passe"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
-msgid "Shaarli title"
-msgstr "Titre du Shaarli"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
-msgid "My links"
-msgstr "Mes liens"
-
-#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
-msgid "Install"
-msgstr "Installer"
+msgstr "Appliquer les changements"
 
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79
@@ -964,13 +847,11 @@ msgstr[0] "lien privé"
 msgstr[1] "liens privés"
 
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:121
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:121
 msgid "Search text"
 msgstr "Recherche texte"
 
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:128
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:128
 #: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
 #: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:64
@@ -1011,7 +892,6 @@ msgid "without any tag"
 msgstr "sans tag"
 
 #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:42
 msgid "Fold"
 msgstr "Replier"
@@ -1028,36 +908,36 @@ msgstr "permalien"
 msgid "Add tag"
 msgstr "Ajouter un tag"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:183
+msgid "Toggle sticky"
+msgstr "Changer statut Ã©pinglé"
+
+#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:185
+msgid "Sticky"
+msgstr "Épinglé"
+
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7
 msgid "Filters"
 msgstr "Filtres"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12
 msgid "Only display private links"
 msgstr "Afficher uniquement les liens privés"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:15
 msgid "Only display public links"
 msgstr "Afficher uniquement les liens publics"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:20
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:20
 msgid "Filter untagged links"
 msgstr "Filtrer par liens privés"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:24
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:76
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:43
 msgid "Fold all"
 msgstr "Replier tout"
 
-#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
 #: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:69
 msgid "Links per page"
 msgstr "Liens par page"
@@ -1066,62 +946,59 @@ msgstr "Liens par page"
 msgid ""
 "You have been banned after too many failed login attempts. Try again later."
 msgstr ""
-"Vous avez Ã©té banni après trop d'échec d'authentification. Merci de "
+"Vous avez Ã©té banni après trop d'échecs d'authentification. Merci de "
 "réessayer plus tard."
 
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
+#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:152
+msgid "Password"
+msgstr "Mot de passe"
+
 #: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:155
 msgid "Remember me"
 msgstr "Rester connecté"
 
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
 msgid "by the Shaarli community"
 msgstr "par la communauté Shaarli"
 
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15
 msgid "Documentation"
 msgstr "Documentation"
 
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:44
 msgid "Expand"
 msgstr "Déplier"
 
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:45
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:45
 msgid "Expand all"
 msgstr "Déplier tout"
 
-#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:46
 msgid "Are you sure you want to delete this link?"
 msgstr "Êtes-vous sûr de vouloir supprimer ce lien ?"
 
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:90
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:65
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:90
 msgid "RSS Feed"
 msgstr "Flux RSS"
 
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:70
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:70
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:106
 msgid "Logout"
 msgstr "Déconnexion"
 
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:173
 msgid "is available"
 msgstr "est disponible"
 
-#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:180
 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:180
 msgid "Error"
 msgstr "Erreur"
@@ -1221,22 +1098,18 @@ msgstr "tags"
 msgid "List all links with those tags"
 msgstr "Lister tous les liens avec ces tags"
 
-#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:3
 #: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3
 msgid "Sort by:"
 msgstr "Trier par :"
 
-#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:5
 #: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5
 msgid "Cloud"
 msgstr "Nuage"
 
-#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:6
 #: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6
 msgid "Most used"
 msgstr "Plus utilisés"
 
-#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
 #: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7
 msgid "Alphabetical"
 msgstr "Alphabétique"
@@ -1251,7 +1124,7 @@ msgstr "Changer les paramètres de Shaarli : titre, fuseau horaire, etc."
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
 msgid "Configure your Shaarli"
-msgstr "Conguration de Shaarli"
+msgstr "Configurer Shaarli"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
 msgid "Enable, disable and configure plugins"
@@ -1259,31 +1132,39 @@ msgstr "Activer, désactiver et configurer les extensions"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
 msgid "Change your password"
-msgstr "Modification du mot de passe"
+msgstr "Modifier le mot de passe"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
 msgid "Rename or delete a tag in all links"
-msgstr "Rename or delete a tag in all links"
+msgstr "Renommer ou supprimer un tag dans tous les liens"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
+#, fuzzy
+#| msgid ""
+#| "Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
+#| "delicious…)"
 msgid ""
 "Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
 "delicious...)"
 msgstr ""
 "Importer des marques pages au format Netscape HTML (comme exportés depuis "
-"Firefox, Chrome, Opera, delicious...)"
+"Firefox, Chrome, Opera, delicious…)"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
 msgid "Import links"
 msgstr "Importer des liens"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
+#, fuzzy
+#| msgid ""
+#| "Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
+#| "Opera, delicious…)"
 msgid ""
 "Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
 "Opera, delicious...)"
 msgstr ""
 "Exporter les marques pages au format Netscape HTML (comme exportés depuis "
-"Firefox, Chrome, Opera, delicious...)"
+"Firefox, Chrome, Opera, delicious…)"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
 msgid "Export database"
@@ -1298,13 +1179,13 @@ msgid ""
 "Drag one of these button to your bookmarks toolbar or right-click it and "
 "\"Bookmark This Link\""
 msgstr ""
-"Glisser un de ces bouttons dans votre barre de favoris ou cliquer droit "
+"Glisser un de ces boutons dans votre barre de favoris ou cliquer droit "
 "dessus et Â« Ajouter aux favoris Â»"
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
 msgid "then click on the bookmarklet in any page you want to share."
 msgstr ""
-"puis cliquer sur le marque page depuis un site que vous souhaitez partager."
+"puis cliquer sur le marque-page depuis un site que vous souhaitez partager."
 
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
@@ -1339,33 +1220,16 @@ msgstr ""
 msgid "Add Note"
 msgstr "Ajouter une Note"
 
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
-msgid ""
-"You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
-"functionality."
-msgstr ""
-"Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
-"fonctionalité."
-
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144
-msgid "Add to"
-msgstr "Ajouter Ã "
-
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
 msgid "3rd party"
 msgstr "Applications tierces"
 
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:157
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:163
-msgid "Plugin"
-msgstr "Extension"
-
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:158
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:164
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144
 msgid "plugin"
 msgstr "extension"
 
-#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:191
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
 msgid ""
 "Drag this link to your bookmarks toolbar, or right-click it and choose "
 "Bookmark This Link"
@@ -1373,10 +1237,91 @@ msgstr ""
 "Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et Â« "
 "Ajouter aux favoris Â»"
 
-#, fuzzy
-#~| msgid "Enable thumbnails"
-#~ msgid "Synchonize thumbnails"
-#~ msgstr "Activer les miniatures"
+#~ msgid "Sorry, nothing to see here."
+#~ msgstr "Désolé, il y a rien Ã  voir ici."
+
+#~ msgid "Current password"
+#~ msgstr "Mot de passe actuel"
+
+#~ msgid "New password"
+#~ msgstr "Nouveau mot de passe"
+
+#~ msgid "Change"
+#~ msgstr "Changer"
+
+#~ msgid "Export Database"
+#~ msgstr "Exporter les données"
+
+#~ msgid "Selection"
+#~ msgstr "Choisir"
+
+#~ msgid "Public"
+#~ msgstr "Publics"
+
+#~ msgid "Prepend note permalinks with this Shaarli instance's URL"
+#~ msgstr "Préfixer les liens de note avec l'URL de l'instance de Shaarli"
+
+#~ msgid "Useful to import bookmarks in a web browser"
+#~ msgstr "Utile pour importer les marques-pages dans un navigateur"
+
+#~ msgid "Import Database"
+#~ msgstr "Importer des données"
+
+#~ msgid "Maximum size allowed:"
+#~ msgstr "Taille maximum autorisée :"
+
+#~ msgid "Visibility"
+#~ msgstr "Visibilité"
+
+#~ msgid "Use values from the imported file, default to public"
+#~ msgstr ""
+#~ "Utiliser les valeurs présentes dans le fichier d'import, public par défaut"
+
+#~ msgid "Import all bookmarks as private"
+#~ msgstr "Importer tous les liens comme privés"
+
+#~ msgid "Import all bookmarks as public"
+#~ msgstr "Importer tous les liens comme publics"
+
+#~ msgid "Overwrite existing bookmarks"
+#~ msgstr "Remplacer les liens existants"
+
+#~ msgid "Duplicates based on URL"
+#~ msgstr "Les doublons s'appuient sur les URL"
+
+#~ msgid "Add default tags"
+#~ msgstr "Ajouter des tags par défaut"
+
+#~ msgid "Install Shaarli"
+#~ msgstr "Installation de Shaarli"
+
+#~ msgid ""
+#~ "It looks like it's the first time you run Shaarli. Please configure it."
+#~ msgstr ""
+#~ "Il semblerait que Ã§a soit la première fois que vous lancez Shaarli. Merci "
+#~ "de le configurer."
+
+#~ msgid "Shaarli title"
+#~ msgstr "Titre du Shaarli"
+
+#~ msgid "My links"
+#~ msgstr "Mes liens"
+
+#~ msgid "Install"
+#~ msgstr "Installer"
+
+#~ msgid ""
+#~ "You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
+#~ "functionality."
+#~ msgstr ""
+#~ "Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
+#~ "fonctionalité."
+
+#~ msgid "Add to"
+#~ msgstr "Ajouter Ã "
+
+#~ msgid "Plugin"
+#~ msgstr "Extension"
 
 #~ msgid "Warning: "
 #~ msgstr "Attention : "
@@ -1450,7 +1395,8 @@ msgstr ""
 #~ "\n"
 
 #~ msgid "Sessions do not seem to work correctly on your server."
-#~ msgstr "Les sessions ne semblent "
+#~ msgstr ""
+#~ "Les sessions ne semblent pas fonctionner correctement sur votre serveur."
 
 #~ msgid "Tag was renamed in "
 #~ msgstr "Le tag a Ã©té renommé dans "
index 4b86a3e2c17ece7338ad04646cedee6150bbbd47..acfcc660bad686182b9f4fe1294b39a51152de87 100644 (file)
--- a/index.php
+++ b/index.php
@@ -28,7 +28,7 @@ if (date_default_timezone_get() == '') {
 define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0)));
 
 // High execution time in case of problematic imports/exports.
-ini_set('max_input_time','60');
+ini_set('max_input_time', '60');
 
 // Try to set max upload file size and read
 ini_set('memory_limit', '128M');
@@ -85,7 +85,7 @@ use \Shaarli\Thumbnailer;
 // Ensure the PHP version is supported
 try {
     ApplicationUtils::checkPHPVersion('5.5', PHP_VERSION);
-} catch(Exception $exc) {
+} catch (Exception $exc) {
     header('Content-Type: text/plain; charset=utf-8');
     echo $exc->getMessage();
     exit;
@@ -111,7 +111,7 @@ ini_set('session.use_trans_sid', false);
 
 session_name('shaarli');
 // Start session if needed (Some server auto-start sessions).
-if (session_id() == '') {
+if (session_status() == PHP_SESSION_NONE) {
     session_start();
 }
 
@@ -223,7 +223,6 @@ if (isset($_POST['login'])) {
                 $expirationTime,
                 WEB_PATH
             );
-
         } else {
             // Standard session expiration (=when browser closes)
             $expirationTime = 0;
@@ -257,7 +256,8 @@ if (isset($_POST['login'])) {
                 exit;
             }
         }
-        header('Location: ?'); exit;
+        header('Location: ?');
+        exit;
     } else {
         $loginManager->handleFailedLogin($_SERVER);
         $redir = '&username='. urlencode($_POST['login']);
@@ -278,7 +278,9 @@ if (isset($_POST['login'])) {
 // ------------------------------------------------------------------------------------------
 // Token management for XSRF protection
 // Token should be used in any form which acts on data (create,update,delete,import...).
-if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array();  // Token are attached to the session.
+if (!isset($_SESSION['tokens'])) {
+    $_SESSION['tokens']=array();  // Token are attached to the session.
+}
 
 /**
  * Daily RSS feed: 1 RSS entry per day giving all the links on that day.
@@ -288,13 +290,14 @@ if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array();  // Token are atta
  * @param ConfigManager $conf         Configuration Manager instance
  * @param LoginManager  $loginManager LoginManager instance
  */
-function showDailyRSS($conf, $loginManager) {
+function showDailyRSS($conf, $loginManager)
+{
     // Cache system
     $query = $_SERVER['QUERY_STRING'];
     $cache = new CachedPage(
         $conf->get('config.PAGE_CACHE'),
         page_url($_SERVER),
-        startsWith($query,'do=dailyrss') && !$loginManager->isLoggedIn()
+        startsWith($query, 'do=dailyrss') && !$loginManager->isLoggedIn()
     );
     $cached = $cache->cachedVersion();
     if (!empty($cached)) {
@@ -395,7 +398,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
 {
     $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
     if (isset($_GET['day'])) {
-      $day = $_GET['day'];
+        $day = $_GET['day'];
     }
 
     $days = $LINKSDB->days();
@@ -413,7 +416,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
              $previousday=$days[$i - 1];
         }
         if ($i < count($days) - 1) {
-          $nextday = $days[$i + 1];
+            $nextday = $days[$i + 1];
         }
     }
     try {
@@ -424,8 +427,8 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
     }
 
     // We pre-format some fields for proper output.
-    foreach($linksToDisplay as $key => $link) {
-        $taglist = explode(' ',$link['tags']);
+    foreach ($linksToDisplay as $key => $link) {
+        $taglist = explode(' ', $link['tags']);
         uasort($taglist, 'strcasecmp');
         $linksToDisplay[$key]['taglist']=$taglist;
         $linksToDisplay[$key]['formatedDescription'] = format_description(
@@ -457,14 +460,14 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
     */
     $columns = array(array(), array(), array()); // Entries to display, for each column.
     $fill = array(0, 0, 0);  // Rough estimate of columns fill.
-    foreach($data['linksToDisplay'] as $key => $link) {
+    foreach ($data['linksToDisplay'] as $key => $link) {
         // Roughly estimate length of entry (by counting characters)
         // Title: 30 chars = 1 line. 1 line is 30 pixels height.
         // Description: 836 characters gives roughly 342 pixel height.
         // This is not perfect, but it's usually OK.
         $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836;
         if ($link['thumbnail']) {
-          $length += 100; // 1 thumbnails roughly takes 100 pixels height.
+            $length += 100; // 1 thumbnails roughly takes 100 pixels height.
         }
         // Then put in column which is the less filled:
         $smallest = min($fill); // find smallest value in array.
@@ -492,8 +495,9 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
  * @param ConfigManager $conf    Configuration Manager instance.
  * @param PluginManager $pluginManager Plugin Manager instance.
  */
-function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) {
-    buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager, $loginManager);
+function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
+{
+    buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
     $PAGE->renderPage('linklist');
 }
 
@@ -524,8 +528,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
                 $updater->getDoneUpdates()
             );
         }
-    }
-    catch(Exception $e) {
+    } catch (Exception $e) {
         die($e->getMessage());
     }
 
@@ -538,8 +541,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
     $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn());
 
-    if (
-        // if the user isn't logged in
+    if (// if the user isn't logged in
         !$loginManager->isLoggedIn() &&
         // and Shaarli doesn't have public content...
         $conf->get('privacy.hide_public_links') &&
@@ -563,9 +565,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         'footer',
     );
 
-    foreach($common_hooks as $name) {
+    foreach ($common_hooks as $name) {
         $plugin_data = array();
-        $pluginManager->executeHooks('render_' . $name, $plugin_data,
+        $pluginManager->executeHooks(
+            'render_' . $name,
+            $plugin_data,
             array(
                 'target' => $targetPage,
                 'loggedin' => $loginManager->isLoggedIn()
@@ -575,13 +579,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- Display login form.
-    if ($targetPage == Router::$PAGE_LOGIN)
-    {
-        if ($conf->get('security.open_shaarli')) { header('Location: ?'); exit; }  // No need to login for open Shaarli
+    if ($targetPage == Router::$PAGE_LOGIN) {
+        if ($conf->get('security.open_shaarli')) {
+            header('Location: ?');
+            exit;
+        }  // No need to login for open Shaarli
         if (isset($_GET['username'])) {
             $PAGE->assign('username', escape($_GET['username']));
         }
-        $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
+        $PAGE->assign('returnurl', (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
         // add default state of the 'remember me' checkbox
         $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default'));
         $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER));
@@ -590,8 +596,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         exit;
     }
     // -------- User wants to logout.
-    if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout'))
-    {
+    if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) {
         invalidateCaches($conf->get('resource.page_cache'));
         $sessionManager->logout();
         setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, WEB_PATH);
@@ -600,8 +605,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- Picture wall
-    if ($targetPage == Router::$PAGE_PICWALL)
-    {
+    if ($targetPage == Router::$PAGE_PICWALL) {
         $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli'));
         if (! $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) {
             $PAGE->assign('linksToDisplay', []);
@@ -615,8 +619,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
 
         // Get only links which have a thumbnail.
         // Note: we do not retrieve thumbnails here, the request is too heavy.
-        foreach($links as $key => $link)
-        {
+        foreach ($links as $key => $link) {
             if (isset($link['thumbnail']) && $link['thumbnail'] !== false) {
                 $linksToDisplay[] = $link; // Add to array.
             }
@@ -637,8 +640,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- Tag cloud
-    if ($targetPage == Router::$PAGE_TAGCLOUD)
-    {
+    if ($targetPage == Router::$PAGE_TAGCLOUD) {
         $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
         $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
         $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility);
@@ -653,7 +655,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         alphabetical_sort($tags, false, true);
 
         $tagList = array();
-        foreach($tags as $key => $value) {
+        foreach ($tags as $key => $value) {
             if (in_array($key, $filteringTags)) {
                 continue;
             }
@@ -685,8 +687,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- Tag list
-    if ($targetPage == Router::$PAGE_TAGLIST)
-    {
+    if ($targetPage == Router::$PAGE_TAGLIST) {
         $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
         $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
         $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility);
@@ -732,7 +733,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         $cache = new CachedPage(
             $conf->get('resource.page_cache'),
             page_url($_SERVER),
-            startsWith($query,'do='. $targetPage) && !$loginManager->isLoggedIn()
+            startsWith($query, 'do='. $targetPage) && !$loginManager->isLoggedIn()
         );
         $cached = $cache->cachedVersion();
         if (!empty($cached)) {
@@ -770,11 +771,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
-    if (isset($_GET['addtag']))
-    {
+    if (isset($_GET['addtag'])) {
         // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
-        if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?searchtags='.urlencode($_GET['addtag'])); exit; } // In case browser does not send HTTP_REFERER
-        parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params);
+        if (empty($_SERVER['HTTP_REFERER'])) {
+            // In case browser does not send HTTP_REFERER
+            header('Location: ?searchtags='.urlencode($_GET['addtag']));
+            exit;
+        }
+        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
 
         // Prevent redirection loop
         if (isset($params['addtag'])) {
@@ -798,12 +802,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         // Append the tag if necessary
         if (empty($params['searchtags'])) {
             $params['searchtags'] = trim($_GET['addtag']);
-        }
-        elseif ($addtag) {
+        } elseif ($addtag) {
             $params['searchtags'] = trim($params['searchtags']).' '.trim($_GET['addtag']);
         }
 
-        unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different)
+        // We also remove page (keeping the same page has no sense, since the
+        // results are different)
+        unset($params['page']);
+
         header('Location: ?'.http_build_query($params));
         exit;
     }
@@ -828,13 +834,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             $tags = explode(' ', $params['searchtags']);
             // Remove value from array $tags.
             $tags = array_diff($tags, array($_GET['removetag']));
-            $params['searchtags'] = implode(' ',$tags);
+            $params['searchtags'] = implode(' ', $tags);
 
             if (empty($params['searchtags'])) {
                 unset($params['searchtags']);
             }
 
-            unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different)
+            // We also remove page (keeping the same page has no sense, since
+            // the results are different)
+            unset($params['page']);
         }
         header('Location: ?'.http_build_query($params));
         exit;
@@ -897,12 +905,10 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- Handle other actions allowed for non-logged in users:
-    if (!$loginManager->isLoggedIn())
-    {
+    if (!$loginManager->isLoggedIn()) {
         // User tries to post new link but is not logged in:
         // Show login screen, then redirect to ?post=...
-        if (isset($_GET['post']))
-        {
+        if (isset($_GET['post'])) {
             header( // Redirect to login page, then back to post link.
                 'Location: ?do=login&post='.urlencode($_GET['post']).
                 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').
@@ -925,8 +931,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     // -------- All other functions are reserved for the registered user:
 
     // -------- Display the Tools menu if requested (import/export/bookmarklet...)
-    if ($targetPage == Router::$PAGE_TOOLS)
-    {
+    if ($targetPage == Router::$PAGE_TOOLS) {
         $data = [
             'pageabsaddr' => index_url($_SERVER),
             'sslenabled' => is_https($_SERVER),
@@ -943,30 +948,40 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User wants to change his/her password.
-    if ($targetPage == Router::$PAGE_CHANGEPASSWORD)
-    {
+    if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
         if ($conf->get('security.open_shaarli')) {
             die(t('You are not supposed to change a password on an Open Shaarli.'));
         }
 
-        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
-        {
-            if (!$sessionManager->checkToken($_POST['token'])) die(t('Wrong token.')); // Go away!
+        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
+            if (!$sessionManager->checkToken($_POST['token'])) {
+                die(t('Wrong token.')); // Go away!
+            }
 
             // Make sure old password is correct.
-            $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt'));
-            if ($oldhash!= $conf->get('credentials.hash')) {
-                echo '<script>alert("'. t('The old password is not correct.') .'");document.location=\'?do=changepasswd\';</script>';
+            $oldhash = sha1(
+                $_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')
+            );
+            if ($oldhash != $conf->get('credentials.hash')) {
+                echo '<script>alert("'
+                    . t('The old password is not correct.')
+                    .'");document.location=\'?do=changepasswd\';</script>';
                 exit;
             }
             // Save new password
             // Salt renders rainbow-tables attacks useless.
             $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
-            $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt')));
+            $conf->set(
+                'credentials.hash',
+                sha1(
+                    $_POST['setpassword']
+                    . $conf->get('credentials.login')
+                    . $conf->get('credentials.salt')
+                )
+            );
             try {
                 $conf->write($loginManager->isLoggedIn());
-            }
-            catch(Exception $e) {
+            } catch (Exception $e) {
                 error_log(
                     'ERROR while writing config file after changing password.' . PHP_EOL .
                     $e->getMessage()
@@ -978,9 +993,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             }
             echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>';
             exit;
-        }
-        else // show the change password form.
-        {
+        } else {
+            // show the change password form.
             $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli'));
             $PAGE->renderPage('changepassword');
             exit;
@@ -988,10 +1002,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User wants to change configuration
-    if ($targetPage == Router::$PAGE_CONFIGURE)
-    {
-        if (!empty($_POST['title']) )
-        {
+    if ($targetPage == Router::$PAGE_CONFIGURE) {
+        if (!empty($_POST['title'])) {
             if (!$sessionManager->checkToken($_POST['token'])) {
                 die(t('Wrong token.')); // Go away!
             }
@@ -1019,7 +1031,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
                 && $thumbnailsMode !== $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)
             ) {
                 $_SESSION['warnings'][] = t(
-                    'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.'
+                    'You have enabled or changed thumbnails mode. '
+                    .'<a href="?do=thumbs_update">Please synchronize them</a>.'
                 );
             }
             $conf->set('thumbnails.mode', $thumbnailsMode);
@@ -1028,8 +1041,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
                 $conf->write($loginManager->isLoggedIn());
                 $history->updateSettings();
                 invalidateCaches($conf->get('resource.page_cache'));
-            }
-            catch(Exception $e) {
+            } catch (Exception $e) {
                 error_log(
                     'ERROR while writing config file after configuration update.' . PHP_EOL .
                     $e->getMessage()
@@ -1041,9 +1053,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             }
             echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>';
             exit;
-        }
-        else // Show the configuration form.
-        {
+        } else {
+            // Show the configuration form.
             $PAGE->assign('title', $conf->get('general.title'));
             $PAGE->assign('theme', $conf->get('resource.theme'));
             $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl')));
@@ -1071,8 +1082,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User wants to rename a tag or delete it
-    if ($targetPage == Router::$PAGE_CHANGETAG)
-    {
+    if ($targetPage == Router::$PAGE_CHANGETAG) {
         if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) {
             $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : '');
             $PAGE->assign('pagetitle', t('Manage tags') .' - '. $conf->get('general.title', 'Shaarli'));
@@ -1084,7 +1094,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             die(t('Wrong token.'));
         }
 
-        $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag']));
+        $toTag = isset($_POST['totag']) ? escape($_POST['totag']) : null;
+        $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), $toTag);
         $LINKSDB->save($conf->get('resource.page_cache'));
         foreach ($alteredLinks as $link) {
             $history->updateLink($link);
@@ -1100,16 +1111,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User wants to add a link without using the bookmarklet: Show form.
-    if ($targetPage == Router::$PAGE_ADDLINK)
-    {
+    if ($targetPage == Router::$PAGE_ADDLINK) {
         $PAGE->assign('pagetitle', t('Shaare a new link') .' - '. $conf->get('general.title', 'Shaarli'));
         $PAGE->renderPage('addlink');
         exit;
     }
 
     // -------- User clicked the "Save" button when editing a link: Save link to database.
-    if (isset($_POST['save_edit']))
-    {
+    if (isset($_POST['save_edit'])) {
         // Go away!
         if (! $sessionManager->checkToken($_POST['token'])) {
             die(t('Wrong token.'));
@@ -1196,14 +1205,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User clicked the "Cancel" button when editing a link.
-    if (isset($_POST['cancel_edit']))
-    {
+    if (isset($_POST['cancel_edit'])) {
         $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false;
         if (! isset($LINKSDB[$id])) {
             header('Location: ?');
         }
         // If we are called from the bookmarklet, we must close the popup:
-        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
+        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
+            echo '<script>self.close();</script>';
+            exit;
+        }
         $link = $LINKSDB[$id];
         $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
         // Scroll to the link which has been edited.
@@ -1214,8 +1225,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User clicked the "Delete" button when editing a link: Delete link from database.
-    if ($targetPage == Router::$PAGE_DELETELINK)
-    {
+    if ($targetPage == Router::$PAGE_DELETELINK) {
         if (! $sessionManager->checkToken($_GET['token'])) {
             die(t('Wrong token.'));
         }
@@ -1229,28 +1239,31 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             $ids = [$ids];
         }
         // assert at least one id is given
-        if(!count($ids)){
+        if (!count($ids)) {
             die('no id provided');
         }
         foreach ($ids as $id) {
             $id = (int) escape($id);
             $link = $LINKSDB[$id];
             $pluginManager->executeHooks('delete_link', $link);
+            $history->deleteLink($link);
             unset($LINKSDB[$id]);
         }
         $LINKSDB->save($conf->get('resource.page_cache')); // save to disk
-        $history->deleteLink($link);
 
         // If we are called from the bookmarklet, we must close the popup:
-        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
+        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
+            echo '<script>self.close();</script>';
+            exit;
+        }
 
         $location = '?';
         if (isset($_SERVER['HTTP_REFERER'])) {
             // Don't redirect to where we were previously if it was a permalink or an edit_link, because it would 404.
             $location = generateLocation(
-                    $_SERVER['HTTP_REFERER'],
-                    $_SERVER['HTTP_HOST'],
-                    ['delete_link', 'edit_link', $link['shorturl']]
+                $_SERVER['HTTP_REFERER'],
+                $_SERVER['HTTP_HOST'],
+                ['delete_link', 'edit_link', $link['shorturl']]
             );
         }
 
@@ -1259,11 +1272,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
     }
 
     // -------- User clicked the "EDIT" button on a link: Display link edit form.
-    if (isset($_GET['edit_link']))
-    {
+    if (isset($_GET['edit_link'])) {
         $id = (int) escape($_GET['edit_link']);
         $link = $LINKSDB[$id];  // Read database
-        if (!$link) { header('Location: ?'); exit; } // Link not found in database.
+        if (!$link) {
+            header('Location: ?');
+            exit;
+        } // Link not found in database.
         $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT);
         $data = array(
             'link' => $link,
@@ -1289,8 +1304,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         $link_is_new = false;
         // Check if URL is not already in database (in this case, we will edit the existing link)
         $link = $LINKSDB->getLinkFromUrl($url);
-        if (! $link)
-        {
+        if (! $link) {
             $link_is_new = true;
             $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT));
             // Get title if it was provided in URL (by the bookmarklet).
@@ -1299,7 +1313,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
             $description = empty($_GET['description']) ? '' : escape($_GET['description']);
             $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']);
             $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0;
-            // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.)
+
+            // If this is an HTTP(S) link, we try go get the page to extract
+            // the title (otherwise we will to straight to the edit form.)
             if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) {
                 // Short timeout to keep the application responsive
                 // The callback will fill $charset and $title with data from the downloaded page.
@@ -1352,6 +1368,25 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         exit;
     }
 
+    if ($targetPage == Router::$PAGE_PINLINK) {
+        if (! isset($_GET['id']) || empty($LINKSDB[$_GET['id']])) {
+            // FIXME! Use a proper error system.
+            $msg = t('Invalid link ID provided');
+            echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>';
+            exit;
+        }
+        if (! $sessionManager->checkToken($_GET['token'])) {
+            die('Wrong token.');
+        }
+
+        $link = $LINKSDB[$_GET['id']];
+        $link['sticky'] = ! $link['sticky'];
+        $LINKSDB[(int) $_GET['id']] = $link;
+        $LINKSDB->save($conf->get('resource.page_cache'));
+        header('Location: '.index_url($_SERVER));
+        exit;
+    }
+
     if ($targetPage == Router::$PAGE_EXPORT) {
         // Export links as a Netscape Bookmarks file
 
@@ -1388,7 +1423,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         header('Content-Type: text/html; charset=utf-8');
         header(
             'Content-disposition: attachment; filename=bookmarks_'
-           .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html'
+            .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html'
         );
         $PAGE->assign('date', $now->format(DateTime::RFC822));
         $PAGE->assign('eol', PHP_EOL);
@@ -1456,14 +1491,20 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
         $pluginMeta = $pluginManager->getPluginsMeta();
 
         // Split plugins into 2 arrays: ordered enabled plugins and disabled.
-        $enabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] !== false; });
+        $enabledPlugins = array_filter($pluginMeta, function ($v) {
+            return $v['order'] !== false;
+        });
         // Load parameters.
         $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $conf->get('plugins', array()));
         uasort(
             $enabledPlugins,
-            function($a, $b) { return $a['order'] - $b['order']; }
+            function ($a, $b) {
+                return $a['order'] - $b['order'];
+            }
         );
-        $disabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] === false; });
+        $disabledPlugins = array_filter($pluginMeta, function ($v) {
+            return $v['order'] === false;
+        });
 
         $PAGE->assign('enabledPlugins', $enabledPlugins);
         $PAGE->assign('disabledPlugins', $disabledPlugins);
@@ -1480,21 +1521,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
                 foreach ($_POST as $param => $value) {
                     $conf->set('plugins.'. $param, escape($value));
                 }
-            }
-            else {
+            } else {
                 $conf->set('general.enabled_plugins', save_plugin_config($_POST));
             }
             $conf->write($loginManager->isLoggedIn());
             $history->updateSettings();
-        }
-        catch (Exception $e) {
+        } catch (Exception $e) {
             error_log(
                 'ERROR while saving plugin configuration:.' . PHP_EOL .
                 $e->getMessage()
             );
 
             // TODO: do not handle exceptions/errors in JS.
-            echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do='. Router::$PAGE_PLUGINSADMIN .'\';</script>';
+            echo '<script>alert("'
+                . $e->getMessage()
+                .'");document.location=\'?do='
+                . Router::$PAGE_PLUGINSADMIN
+                .'\';</script>';
             exit;
         }
         header('Location: ?do='. Router::$PAGE_PLUGINSADMIN);
@@ -1615,8 +1658,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
     }
 
     $linkDisp = array();
-    while ($i<$end && $i<count($keys))
-    {
+    while ($i<$end && $i<count($keys)) {
         $link = $linksToDisplay[$keys[$i]];
         $link['description'] = format_description(
             $link['description'],
@@ -1719,16 +1761,19 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
  * @param SessionManager $sessionManager SessionManager instance
  * @param LoginManager   $loginManager   LoginManager instance
  */
-function install($conf, $sessionManager, $loginManager) {
+function install($conf, $sessionManager, $loginManager)
+{
     // On free.fr host, make sure the /sessions directory exists, otherwise login will not work.
-    if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705);
+    if (endsWith($_SERVER['HTTP_HOST'], '.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) {
+        mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions', 0705);
+    }
 
 
     // This part makes sure sessions works correctly.
     // (Because on some hosts, session.save_path may not be set correctly,
     // or we may not have write access to it.)
-    if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working'))
-    {
+    if (isset($_GET['test_session'])
+        && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) {
         // Step 2: Check if data in session is correct.
         $msg = t(
             '<pre>Sessions do not seem to work correctly on your server.<br>'.
@@ -1744,19 +1789,18 @@ function install($conf, $sessionManager, $loginManager) {
         echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>';
         die;
     }
-    if (!isset($_SESSION['session_tested']))
-    {   // Step 1 : Try to store data in session and reload page.
+    if (!isset($_SESSION['session_tested'])) {
+        // Step 1 : Try to store data in session and reload page.
         $_SESSION['session_tested'] = 'Working';  // Try to set a variable in session.
         header('Location: '.index_url($_SERVER).'?test_session');  // Redirect to check stored data.
     }
-    if (isset($_GET['test_session']))
-    {   // Step 3: Sessions are OK. Remove test parameter from URL.
+    if (isset($_GET['test_session'])) {
+        // Step 3: Sessions are OK. Remove test parameter from URL.
         header('Location: '.index_url($_SERVER));
     }
 
 
-    if (!empty($_POST['setlogin']) && !empty($_POST['setpassword']))
-    {
+    if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) {
         $tz = 'UTC';
         if (!empty($_POST['continent']) && !empty($_POST['city'])
             && isTimeZoneValid($_POST['continent'], $_POST['city'])
@@ -1787,18 +1831,20 @@ function install($conf, $sessionManager, $loginManager) {
         try {
             // Everything is ok, let's create config file.
             $conf->write($loginManager->isLoggedIn());
-        }
-        catch(Exception $e) {
+        } catch (Exception $e) {
             error_log(
-                    'ERROR while writing config file after installation.' . PHP_EOL .
+                'ERROR while writing config file after installation.' . PHP_EOL .
                     $e->getMessage()
-                );
+            );
 
             // TODO: do not handle exceptions/errors in JS.
             echo '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>';
             exit;
         }
-        echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>';
+        echo '<script>alert('
+            .'"Shaarli is now configured. '
+            .'Please enter your login/password and start shaaring your links!"'
+            .');document.location=\'?do=login\';</script>';
         exit;
     }
 
@@ -1822,7 +1868,7 @@ if (!isset($_SESSION['LINKS_PER_PAGE'])) {
 
 try {
     $history = new History($conf->get('resource.history'));
-} catch(Exception $e) {
+} catch (Exception $e) {
     die($e->getMessage());
 }
 
@@ -1841,7 +1887,7 @@ $container['history'] = $history;
 $app = new \Slim\App($container);
 
 // REST API routes
-$app->group('/api/v1', function() {
+$app->group('/api/v1', function () {
     $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo');
     $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks');
     $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink');
@@ -1858,6 +1904,7 @@ $app->group('/api/v1', function() {
 })->add('\Shaarli\Api\ApiMiddleware');
 
 $response = $app->run(true);
+
 // Hack to make Slim and Shaarli router work together:
 // If a Slim route isn't found and NOT API call, we call renderPage().
 if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) {
@@ -1865,5 +1912,12 @@ if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v
     header('Content-Type: text/html; charset=utf-8');
     renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);
 } else {
+    $response = $response
+        ->withHeader('Access-Control-Allow-Origin', '*')
+        ->withHeader(
+            'Access-Control-Allow-Headers',
+            'X-Requested-With, Content-Type, Accept, Origin, Authorization'
+        )
+        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
     $app->respond($response);
 }
index 941fce3aad1ad0f6a779d2789ce666419a4029c6..248fdbfe3923dafab6cca711a4f4278eaf6a583b 100644 (file)
@@ -2,7 +2,9 @@ site_name: Shaarli Documentation
 repo_url: https://github.com/shaarli/Shaarli
 edit_uri: edit/master/doc/md
 site_description: The personal, minimalist, super-fast, database free, bookmarking service
-theme: readthedocs
+theme:
+  name: readthedocs
+  custom_dir: doc/custom_theme/
 docs_dir: doc/md
 site_dir: doc/html
 # Disable strict mode until ReadTheDocs provides up-to-date MkDocs settings:
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644 (file)
index 0000000..29b95d5
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<ruleset name="Shaarli">
+  <description>The Shaarli coding standards</description>
+
+  <file>index.php</file>
+  <file>application</file>
+  <file>plugins</file>
+  <file>tests</file>
+
+  <exclude-pattern>*/*.css</exclude-pattern>
+  <exclude-pattern>*/*.js</exclude-pattern>
+
+  <arg name="colors"/>
+
+  <rule ref="PSR1"/>
+  <rule ref="PSR2"/>
+</ruleset>
index cda35751041933c6caffd061c59121da5833c24f..5dcea5a647930b43d6226fc8033f9e51408db67c 100644 (file)
@@ -17,7 +17,7 @@ function hook_archiveorg_render_linklist($data)
     $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html');
 
     foreach ($data['links'] as &$value) {
-        if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
+        if ($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
             continue;
         }
         $archive = sprintf($archive_html, $value['url'], t('View on archive.org'));
index f3a63b6aeb26a9a003271f981e16ac32761623d6..ca520d15147ce937302eb99d0a15c38c1721e2fb 100644 (file)
@@ -73,7 +73,6 @@ function hook_demo_plugin_render_header($data)
 {
     // Only execute when linklist is rendered.
     if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
-
         // If loggedin
         if ($data['_LOGGEDIN_'] === true) {
             /*
@@ -109,10 +108,10 @@ function hook_demo_plugin_render_header($data)
          *      ],
          *  ]
          * This example renders as:
-         *      <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value">
-         *          <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value">
-         *          <input input-2-attribute-1="input 2 attribute 1 value">
-         *      </form>
+         * <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value">
+         *   <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value">
+         *   <input input-2-attribute-1="input 2 attribute 1 value">
+         * </form>
          */
         $form = array(
             'attr' => array(
@@ -448,8 +447,7 @@ function hook_demo_plugin_render_feed($data)
     foreach ($data['links'] as &$link) {
         if ($data['_PAGE_'] == Router::$PAGE_FEED_ATOM) {
             $link['description'] .= ' - ATOM Feed' ;
-        }
-        elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) {
+        } elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) {
             $link['description'] .= ' - RSS Feed';
         }
     }
diff --git a/plugins/isso/comment.png b/plugins/isso/comment.png
new file mode 100644 (file)
index 0000000..0158c03
Binary files /dev/null and b/plugins/isso/comment.png differ
index 5bc1cce26e79d1be3cc64fa3bda9109a12c2ea3f..378c11af3bfb526b57e22e76c88aa06f4408c2bd 100644 (file)
@@ -46,9 +46,36 @@ function hook_isso_render_linklist($data, $conf)
 
         $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']);
         $data['plugin_end_zone'][] = $isso;
+    } else {
+        $button = '<span><a href="?%s#isso-thread">';
+        // For the default theme we use a FontAwesome icon which is better than an image
+        if ($conf->get('resource.theme') === 'default') {
+            $button .= '<i class="linklist-plugin-icon fa fa-comment"></i>';
+        } else {
+            $button .= '<img class="linklist-plugin-icon" src="plugins/isso/comment.png" ';
+            $button .= 'title="Comment on this shaare" alt="Comments" />';
+        }
+        $button .= '</a></span>';
+        foreach ($data['links'] as &$value) {
+            $commentLink = sprintf($button, $value['shorturl']);
+            $value['link_plugin'][] = $commentLink;
+        }
+    }
 
-        // Hackish way to include this CSS file only when necessary.
-        $data['plugins_includes']['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
+    return $data;
+}
+
+/**
+ * When linklist is displayed, include isso CSS file.
+ *
+ * @param array $data - header data.
+ *
+ * @return mixed - header data with isso CSS file added.
+ */
+function hook_isso_render_includes($data)
+{
+    if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
+        $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
     }
 
     return $data;
diff --git a/plugins/isso/isso_button.html b/plugins/isso/isso_button.html
new file mode 100644 (file)
index 0000000..3f82848
--- /dev/null
@@ -0,0 +1,5 @@
+<span>
+  <a href="?%s#isso-thread">
+    <img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
+  </a>
+</span>
index 821bb12511bf15b98aba0dab6b757c0fc15079a5..8823af91c77d816a015136227eb8859b4c0cef5b 100644 (file)
@@ -28,6 +28,7 @@ function hook_markdown_render_linklist($data, $conf)
             $value = stripNoMarkdownTag($value);
             continue;
         }
+        $value['description_src'] = $value['description'];
         $value['description'] = process_markdown(
             $value['description'],
             $conf->get('security.markdown_escape', true),
@@ -138,7 +139,6 @@ function hook_markdown_render_includes($data)
         || $data['_PAGE_'] == Router::$PAGE_DAILY
         || $data['_PAGE_'] == Router::$PAGE_EDITLINK
     ) {
-
         $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/markdown/markdown.css';
     }
 
@@ -194,8 +194,7 @@ function reverse_text2clickable($description)
         // Detect and toggle block of code
         if (!$codeBlockOn) {
             $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0;
-        }
-        elseif (preg_match('/^```/', $descriptionLine) > 0) {
+        } elseif (preg_match('/^```/', $descriptionLine) > 0) {
             $codeBlockOn = false;
         }
 
@@ -215,6 +214,15 @@ function reverse_text2clickable($description)
             $descriptionLine
         );
 
+        // Make hashtag links markdown ready, otherwise the links will be ignored with escape set to true
+        if (!$codeBlockOn && !$codeLineOn) {
+            $descriptionLine = preg_replace(
+                '#<a href="([^ ]*)"'. $hashtagTitle .'>([^<]+)</a>#m',
+                '[$2]($1)',
+                $descriptionLine
+            );
+        }
+
         $descriptionOut .= $descriptionLine;
         if ($lineCount++ < count($descriptionLines) - 1) {
             $descriptionOut .= PHP_EOL;
@@ -292,13 +300,17 @@ function sanitize_html($description)
     foreach ($escapeTags as $tag) {
         $description = preg_replace_callback(
             '#<\s*'. $tag .'[^>]*>(.*</\s*'. $tag .'[^>]*>)?#is',
-            function ($match) { return escape($match[0]); },
-            $description);
+            function ($match) {
+                return escape($match[0]);
+            },
+            $description
+        );
     }
     $description = preg_replace(
         '#(<[^>]+\s)on[a-z]*="?[^ "]*"?#is',
         '$1',
-        $description);
+        $description
+    );
     return $description;
 }
 
@@ -331,7 +343,7 @@ function process_markdown($description, $escape = true, $allowedProtocols = [])
         ->text($processedDescription);
     $processedDescription = sanitize_html($processedDescription);
 
-    if(!empty($processedDescription)){
+    if (!empty($processedDescription)) {
         $processedDescription = '<div class="markdown">'. $processedDescription . '</div>';
     }
 
index 184b588b7f44d62b89df0fc2f0e07094da0e8b41..9f0342a37fa376b20cdab8dac2a655c0c178ea5b 100644 (file)
@@ -6,7 +6,7 @@
  * PubSub is a protocol which fasten up RSS fetching:
  *   - Every time a new link is posted, Shaarli notify the hub.
  *   - The hub notify all feed subscribers that a new link has been posted.
- *   - Subscribers retrieve the new link. 
+ *   - Subscribers retrieve the new link.
  */
 
 use pubsubhubbub\publisher\Publisher;
@@ -82,7 +82,8 @@ function hook_pubsubhubbub_save_link($data, $conf)
  *
  * @throws Exception An error occurred.
  */
-function nocurl_http_post($url, $postString) {
+function nocurl_http_post($url, $postString)
+{
     $params = array('http' => array(
         'method' => 'POST',
         'content' => $postString,
index 0f96a106921e856eff1160cc0b881c1b68b2e75d..4b59caa03e61db70ebeef68a4626c751ae034bb8 100644 (file)
@@ -17,7 +17,8 @@ function hook_qrcode_render_linklist($data)
     $qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html');
 
     foreach ($data['links'] as &$value) {
-        $qrcode = sprintf($qrcode_html,
+        $qrcode = sprintf(
+            $qrcode_html,
             urlencode($value['url']),
             $value['url'],
             PluginManager::$PLUGINS_PATH
index 9dfd079eb21da9d027dc71a3aacfb9fe5d0608a0..a6476c71c893b0b9f7b2fadef9e8610a86b807be 100644 (file)
@@ -69,4 +69,3 @@ function wallabag_dummy_translation()
     t('Wallabag API URL');
     t('Wallabag API version (1 or 2)');
 }
-
index ff4c9e17944be6de5fa9e53f9574eb1baac5c665..fe5f84ce674f8dadcf195793b0ec61c0d807891b 100644 (file)
@@ -17,7 +17,7 @@ class FakeApplicationUtils extends ApplicationUtils
     /**
      * Toggle HTTP requests, allow overriding the version code
      */
-    public static function getVersion($url, $timeout=0)
+    public static function getVersion($url, $timeout = 0)
     {
         return self::$VERSION_CODE;
     }
@@ -67,7 +67,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
             '0.5.4',
             ApplicationUtils::getVersion(
                 'https://raw.githubusercontent.com/shaarli/Shaarli/'
-               .'v0.5.4/shaarli_version.php',
+                .'v0.5.4/shaarli_version.php',
                 $testTimeout
             )
         );
@@ -75,7 +75,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
             self::$versionPattern,
             ApplicationUtils::getVersion(
                 'https://raw.githubusercontent.com/shaarli/Shaarli/'
-               .'latest/shaarli_version.php',
+                .'latest/shaarli_version.php',
                 $testTimeout
             )
         );
index 992e26a5502cea6afc15cc9756f85c7e507089d0..f60fad91099048ba5d1c8c2d3d34289175880a4f 100644 (file)
@@ -84,7 +84,7 @@ class CacheTest extends PHPUnit_Framework_TestCase
         invalidateCaches(self::$testCacheDir);
         foreach (self::$pages as $page) {
             $this->assertFileNotExists(self::$testCacheDir.'/'.$page.'.cache');
-        }        
+        }
 
         $this->assertArrayNotHasKey('tags', $_SESSION);
     }
index a590306db54524e8fc4a98a6d5a2d8e023c3d4ee..4ca58e5a3b514bf20f21a4597c3d49fb927c1378 100644 (file)
@@ -82,8 +82,8 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
         $this->assertFalse($data['usepermalinks']);
         $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
 
-        // Test first link (note link)
-        $link = reset($data['links']);
+        // Test first not pinned link (note link)
+        $link = $data['links'][array_keys($data['links'])[2]];
         $this->assertEquals(41, $link['id']);
         $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
         $this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
@@ -119,7 +119,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
         $data = $feedBuilder->buildData();
         $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
         $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
-        $link = reset($data['links']);
+        $link = $data['links'][array_keys($data['links'])[2]];
         $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
         $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
     }
@@ -148,13 +148,13 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
     public function testBuildDataCount()
     {
         $criteria = array(
-            'nb' => '1',
+            'nb' => '3',
         );
         $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false);
         $feedBuilder->setLocale(self::$LOCALE);
         $data = $feedBuilder->buildData();
-        $this->assertEquals(1, count($data['links']));
-        $link = array_shift($data['links']);
+        $this->assertEquals(3, count($data['links']));
+        $link = $data['links'][array_keys($data['links'])[2]];
         $this->assertEquals(41, $link['id']);
         $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
     }
@@ -171,7 +171,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
         $this->assertTrue($data['usepermalinks']);
         // First link is a permalink
-        $link = array_shift($data['links']);
+        $link = $data['links'][array_keys($data['links'])[2]];
         $this->assertEquals(41, $link['id']);
         $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
         $this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
@@ -179,7 +179,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
         $this->assertContains('Direct link', $link['description']);
         $this->assertContains('http://host.tld/?WDWyig', $link['description']);
         // Second link is a direct link
-        $link = array_shift($data['links']);
+        $link = $data['links'][array_keys($data['links'])[3]];
         $this->assertEquals(8, $link['id']);
         $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
         $this->assertEquals('http://host.tld/?RttfEw', $link['guid']);
@@ -237,7 +237,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
         );
 
         // Test first link (note link)
-        $link = array_shift($data['links']);
+        $link = $data['links'][array_keys($data['links'])[2]];
         $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']);
         $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']);
         $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']);
index 6a74a45aff696f66623d64b3072c0c0af62a2272..7af5bd9db058c356e9b0b13a52cc2ffc430b2337 100644 (file)
@@ -5,7 +5,8 @@ require_once 'application/HttpUtils.php';
 /**
  * Unitary tests for getIpAddressFromProxy()
  */
-class GetIpAdressFromProxyTest extends PHPUnit_Framework_TestCase {
+class GetIpAdressFromProxyTest extends PHPUnit_Framework_TestCase
+{
 
     /**
      * Test without proxy
index 3b9808782fba646c9ec1440b844d46310b05cbcd..c763c0cbd5f34ce7797658b7bcae2e72829f3290 100644 (file)
@@ -239,12 +239,12 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
     public function testDays()
     {
         $this->assertEquals(
-            array('20100310', '20121206', '20130614', '20150310'),
+            array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'),
             self::$publicLinkDB->days()
         );
 
         $this->assertEquals(
-            array('20100310', '20121206', '20130614', '20141125', '20150310'),
+            array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'),
             self::$privateLinkDB->days()
         );
     }
@@ -362,7 +362,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
     public function testLinkRealUrlWithoutRedirector()
     {
         $db = new LinkDB(self::$testDatastore, false, false);
-        foreach($db as $link) {
+        foreach ($db as $link) {
             $this->assertEquals($link['url'], $link['real_url']);
         }
     }
@@ -374,13 +374,13 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
     {
         $redirector = 'http://redirector.to?';
         $db = new LinkDB(self::$testDatastore, false, false, $redirector);
-        foreach($db as $link) {
+        foreach ($db as $link) {
             $this->assertStringStartsWith($redirector, $link['real_url']);
             $this->assertNotFalse(strpos($link['real_url'], urlencode('://')));
         }
 
         $db = new LinkDB(self::$testDatastore, false, false, $redirector, false);
-        foreach($db as $link) {
+        foreach ($db as $link) {
             $this->assertStringStartsWith($redirector, $link['real_url']);
             $this->assertFalse(strpos($link['real_url'], urlencode('://')));
         }
@@ -475,13 +475,15 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
     public function testReorderLinksDesc()
     {
         self::$privateLinkDB->reorder('ASC');
-        $linkIds = array(42, 4, 9, 1, 0, 7, 6, 8, 41);
+        $stickyIds = [11, 10];
+        $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
+        $linkIds = array_merge($stickyIds, $standardIds);
         $cpt = 0;
         foreach (self::$privateLinkDB as $key => $value) {
             $this->assertEquals($linkIds[$cpt++], $key);
         }
         self::$privateLinkDB->reorder('DESC');
-        $linkIds = array_reverse($linkIds);
+        $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
         $cpt = 0;
         foreach (self::$privateLinkDB as $key => $value) {
             $this->assertEquals($linkIds[$cpt++], $key);
index 9cd6dbd443b2a52b492cd165d12155c06aa14f49..eb54c3591af12fb6111389d34d55b5979149bc10 100644 (file)
@@ -76,7 +76,15 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
 
         $this->assertEquals(
             self::$refDB->countUntaggedLinks(),
-            count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, /*$request=*/'', /*$casesensitive=*/false, /*$visibility=*/'all', /*$untaggedonly=*/true))
+            count(
+                self::$linkFilter->filter(
+                    LinkFilter::$FILTER_TAG,
+                    /*$request=*/'',
+                    /*$casesensitive=*/false,
+                    /*$visibility=*/'all',
+                    /*$untaggedonly=*/true
+                )
+            )
         );
 
         $this->assertEquals(
@@ -246,7 +254,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
             2,
             count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
         );
-        
+
         $this->assertEquals(
             2,
             count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars org'))
@@ -288,16 +296,16 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
             1,
             count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'publishing media'))
         );
-        
+
         $this->assertEquals(
             1,
             count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'mercurial w3c'))
         );
-        
+
         $this->assertEquals(
             3,
             count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '"free software"'))
-        );        
+        );
     }
 
     /**
index 7fbd59b0b80489d7b9e3521664e8aeb7d204b19d..5407159a04c37ed9908aa7fcb063c576b4fd8475 100644 (file)
@@ -83,7 +83,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
             'Date: Sat, 28 Oct 2017 12:01:33 GMT',
             'Content-Type: text/html; charset=utf-8',
             'Status: 200 OK',
-            'end' => 'th=device-width"><title>Refactoring Â· GitHub</title><link rel="search" type="application/opensea',
+            'end' => 'th=device-width">'
+            .'<title>Refactoring Â· GitHub</title>'
+            .'<link rel="search" type="application/opensea',
             '<title>ignored</title>',
         ];
         foreach ($data as $key => $line) {
@@ -106,7 +108,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
         $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset');
         $data = [
             'HTTP/1.1 200 OK',
-            'end' => 'th=device-width"><title>Refactoring Â· GitHub</title><link rel="search" type="application/opensea',
+            'end' => 'th=device-width">'
+            .'<title>Refactoring Â· GitHub</title>'
+            .'<link rel="search" type="application/opensea',
             '<title>ignored</title>',
         ];
         foreach ($data as $key => $line) {
@@ -126,7 +130,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
         $data = [
             'HTTP/1.1 200 OK',
             '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
-            'end' => 'th=device-width"><title>Refactoring Â· GitHub</title><link rel="search" type="application/opensea',
+            'end' => 'th=device-width">'
+            .'<title>Refactoring Â· GitHub</title>'
+            .'<link rel="search" type="application/opensea',
             '<title>ignored</title>',
         ];
         foreach ($data as $key => $line) {
@@ -211,23 +217,26 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
     public function testText2clickableWithoutRedirector()
     {
         $text = 'stuff http://hello.there/is=someone#here otherstuff';
-        $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff';
+        $expectedText = 'stuff <a href="http://hello.there/is=someone#here">'
+            .'http://hello.there/is=someone#here</a> otherstuff';
         $processedText = text2clickable($text, '');
         $this->assertEquals($expectedText, $processedText);
 
         $text = 'stuff http://hello.there/is=someone#here(please) otherstuff';
-        $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">http://hello.there/is=someone#here(please)</a> otherstuff';
+        $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">'
+            .'http://hello.there/is=someone#here(please)</a> otherstuff';
         $processedText = text2clickable($text, '');
         $this->assertEquals($expectedText, $processedText);
 
         $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
-        $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">http://hello.there/is=someone#here(please)&no</a> otherstuff';
+        $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">'
+            .'http://hello.there/is=someone#here(please)&no</a> otherstuff';
         $processedText = text2clickable($text, '');
         $this->assertEquals($expectedText, $processedText);
     }
 
     /**
-     * Test text2clickable a redirector set.
+     * Test text2clickable with a redirector set.
      */
     public function testText2clickableWithRedirector()
     {
@@ -410,4 +419,3 @@ function ut_curl_getinfo_rs_ct_ko($ch, $type)
             return 'text/plain';
     }
 }
-
index 6a47bbb97acac26996766c7cf9c30f8738fc069c..77fbd5f38795ee6bf8d1761e1a00da77922be9ae 100644 (file)
@@ -110,7 +110,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
         $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
         $this->assertEquals(
             '?WDWyig',
-            $links[0]['url']
+            $links[2]['url']
         );
     }
 
@@ -128,7 +128,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
         );
         $this->assertEquals(
             $indexUrl . '?WDWyig',
-            $links[0]['url']
+            $links[2]['url']
         );
     }
 }
index 544bcf9c8890d6613f569795df1456107cee8b99..abf1bd5f5067caf74451439273c2e5a494fe4963 100644 (file)
@@ -218,7 +218,6 @@ class RouterTest extends PHPUnit_Framework_TestCase
             Router::$PAGE_CHANGEPASSWORD,
             Router::findPage('do=changepasswd&stuff', array(), true)
         );
-
     }
 
     /**
index 08311545ef3b597f19d124aa7e6dc7286b69ae1d..c01849f7f089e45e1ca17af6dcb0ee0a3c2e5daf 100644 (file)
@@ -98,15 +98,17 @@ class ThumbnailerTest extends TestCase
         ini_set('error_log', $oldlog);
     }
 
-    protected function rrmdirContent($dir) {
+    protected function rrmdirContent($dir)
+    {
         if (is_dir($dir)) {
             $objects = scandir($dir);
             foreach ($objects as $object) {
                 if ($object != "." && $object != "..") {
-                    if (is_dir($dir."/".$object))
+                    if (is_dir($dir."/".$object)) {
                         $this->rrmdirContent($dir."/".$object);
-                    else
+                    } else {
                         unlink($dir."/".$object);
+                    }
                 }
             }
         }
index a0be4413e1e2e8f39c8e55eec78e6bddd49f3aa1..a805ab5e900181755191e46b2dbe82b2f3c9e2dd 100644 (file)
@@ -31,7 +31,7 @@ class DummyUpdater extends Updater
      *
      * @return bool true.
      */
-    private final function updateMethodDummy1()
+    final private function updateMethodDummy1()
     {
         return true;
     }
@@ -41,7 +41,7 @@ class DummyUpdater extends Updater
      *
      * @return bool true.
      */
-    private final function updateMethodDummy2()
+    final private function updateMethodDummy2()
     {
         return true;
     }
@@ -51,7 +51,7 @@ class DummyUpdater extends Updater
      *
      * @return bool true.
      */
-    private final function updateMethodDummy3()
+    final private function updateMethodDummy3()
     {
         return true;
     }
@@ -61,7 +61,7 @@ class DummyUpdater extends Updater
      *
      * @throws Exception error.
      */
-    private final function updateMethodException()
+    final private function updateMethodException()
     {
         throw new Exception('whatever');
     }
index cacee2d2da25b247e8f5084a1192fe09640693f4..c4a6e7ef4fee86c9634e9aaff4cfdc7f6f8d03d7 100644 (file)
@@ -393,20 +393,32 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
         $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
         $this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
         $this->assertTrue($linkDB[0]['private']);
-        $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), $linkDB[0]['created']);
+        $this->assertEquals(
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'),
+            $linkDB[0]['created']
+        );
 
         $this->assertTrue(isset($linkDB[1]));
         $this->assertFalse(isset($linkDB[1]['linkdate']));
         $this->assertEquals(1, $linkDB[1]['id']);
         $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
-        $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), $linkDB[1]['created']);
+        $this->assertEquals(
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'),
+            $linkDB[1]['created']
+        );
 
         $this->assertTrue(isset($linkDB[2]));
         $this->assertFalse(isset($linkDB[2]['linkdate']));
         $this->assertEquals(2, $linkDB[2]['id']);
         $this->assertEquals('Geek and Poke', $linkDB[2]['title']);
-        $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), $linkDB[2]['created']);
-        $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'), $linkDB[2]['updated']);
+        $this->assertEquals(
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'),
+            $linkDB[2]['created']
+        );
+        $this->assertEquals(
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'),
+            $linkDB[2]['updated']
+        );
     }
 
     /**
@@ -688,6 +700,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
     }
 
     /**
+<<<<<<< HEAD
      * Test updateMethodWebThumbnailer with thumbnails enabled.
      */
     public function testUpdateMethodWebThumbnailerEnabled()
@@ -732,4 +745,64 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
         $this->assertEquals(53, $this->conf->get('thumbnails.height'));
         $this->assertTrue(empty($_SESSION['warnings']));
     }
+
+    /**
+     * Test updateMethodSetSticky().
+     */
+    public function testUpdateStickyValid()
+    {
+        $blank = [
+            'id' => 1,
+            'url' => 'z',
+            'title' => '',
+            'description' => '',
+            'tags' => '',
+            'created' => new DateTime(),
+        ];
+        $links = [
+            1 => ['id' => 1] + $blank,
+            2 => ['id' => 2] + $blank,
+        ];
+        $refDB = new ReferenceLinkDB();
+        $refDB->setLinks($links);
+        $refDB->write(self::$testDatastore);
+        $linkDB = new LinkDB(self::$testDatastore, true, false);
+
+        $updater = new Updater(array(), $linkDB, $this->conf, true);
+        $this->assertTrue($updater->updateMethodSetSticky());
+
+        $linkDB = new LinkDB(self::$testDatastore, true, false);
+        foreach ($linkDB as $link) {
+            $this->assertFalse($link['sticky']);
+        }
+    }
+
+    /**
+     * Test updateMethodSetSticky().
+     */
+    public function testUpdateStickyNothingToDo()
+    {
+        $blank = [
+            'id' => 1,
+            'url' => 'z',
+            'title' => '',
+            'description' => '',
+            'tags' => '',
+            'created' => new DateTime(),
+        ];
+        $links = [
+            1 => ['id' => 1, 'sticky' => true] + $blank,
+            2 => ['id' => 2] + $blank,
+        ];
+        $refDB = new ReferenceLinkDB();
+        $refDB->setLinks($links);
+        $refDB->write(self::$testDatastore);
+        $linkDB = new LinkDB(self::$testDatastore, true, false);
+
+        $updater = new Updater(array(), $linkDB, $this->conf, true);
+        $this->assertTrue($updater->updateMethodSetSticky());
+
+        $linkDB = new LinkDB(self::$testDatastore, true, false);
+        $this->assertTrue($linkDB[1]['sticky']);
+    }
 }
index 1407d7d2581689bddaba990c5435ec9506781db3..24791948337a94be0692a6e97b4c20faa94d2de2 100644 (file)
@@ -107,4 +107,3 @@ class CleanupUrlTest extends PHPUnit_Framework_TestCase
         );
     }
 }
-
index 72d80b30825ee452a1028e4b962ee0c477229160..18b932d6f3a350c9f7191a5faf7517ffbdd02b76 100644 (file)
@@ -28,4 +28,3 @@ class GetUrlSchemeTest extends PHPUnit_Framework_TestCase
         $this->assertEquals('git', get_url_scheme('git://domain.tld/push?pull=clone#checkout'));
     }
 }
-
index edde73e408580fe0f7e40884645b2bdf302d9b15..e314b48468b789688fec09dda194074c670de0e3 100644 (file)
@@ -28,4 +28,3 @@ class UnparseUrlTest extends PHPUnit_Framework_TestCase
         $this->assertEquals($ref, unparse_url(parse_url($ref)));
     }
 }
-
index aa2f2234529b4878cdb3e926918a13d004f7d9bc..db229ce0d87c544ff912fd5240cc6a15dccaa950 100644 (file)
@@ -16,7 +16,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
     /**
      * Helper method
      */
-    private function assertUrlIsCleaned($query='', $fragment='')
+    private function assertUrlIsCleaned($query = '', $fragment = '')
     {
         $url = new Url(self::$baseUrl.$query.$fragment);
         $url->cleanup();
@@ -135,13 +135,13 @@ class UrlTest extends PHPUnit_Framework_TestCase
             'about://reader?url=' . urlencode(self::$baseUrl .'?my=stuff&is=kept')
         );
         $this->assertEquals(self::$baseUrl.'?my=stuff&is=kept', $url->cleanup());
-
     }
 
     /**
      * Test default http scheme.
      */
-    public function testDefaultScheme() {
+    public function testDefaultScheme()
+    {
         $url = new Url(self::$baseUrl);
         $this->assertEquals('http', $url->getScheme());
         $url = new Url('domain.tld');
index 6cd37a7afa27836249af244559fe23c93748e2bd..d0abd9968cfdc4afa152db87682b00ae4d5d73e5 100644 (file)
@@ -187,7 +187,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
     /**
      * Test generate location with valid data.
      */
-    public function testGenerateLocation() {
+    public function testGenerateLocation()
+    {
         $ref = 'http://localhost/?test';
         $this->assertEquals($ref, generateLocation($ref, 'localhost'));
         $ref = 'http://localhost:8080/?test';
@@ -199,7 +200,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
     /**
      * Test generate location - anti loop.
      */
-    public function testGenerateLocationLoop() {
+    public function testGenerateLocationLoop()
+    {
         $ref = 'http://localhost/?test';
         $this->assertEquals('?', generateLocation($ref, 'localhost', array('test')));
     }
@@ -207,7 +209,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
     /**
      * Test generate location - from other domain.
      */
-    public function testGenerateLocationOut() {
+    public function testGenerateLocationOut()
+    {
         $ref = 'http://somewebsite.com/?test';
         $this->assertEquals('?', generateLocation($ref, 'localhost'));
     }
index 62baf4c52f606d9dd8918ac821cec6dbbc73a989..df4e189a9f786b91718cde89b6152c90bac395c0 100644 (file)
@@ -4,7 +4,6 @@ namespace Shaarli\Api;
 
 use Shaarli\Base64Url;
 
-
 /**
  * Class ApiUtilsTest
  */
@@ -34,7 +33,7 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase
         $payload = Base64Url::encode('{
             "iat": '. time() .'
         }');
-        $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload , $secret, true));
+        $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload, $secret, true));
         return $header .'.'. $payload .'.'. $signature;
     }
 
index 61046d9732e78439c10aa028c7ddb3720583d9aa..ff34e16dce598be8e849fba6fac30887908cc875 100644 (file)
@@ -3,7 +3,6 @@
 
 namespace Shaarli\Api\Controllers;
 
-
 use Shaarli\Config\ConfigManager;
 use Slim\Container;
 use Slim\Http\Environment;
index f7e63bfaf2623e498ce0f9605c76aad7e517975b..e437082a1af9517b9b658498a07bed89ff9459c8 100644 (file)
@@ -10,9 +10,9 @@ use Slim\Http\Response;
 
 /**
  * Class InfoTest
- * 
+ *
  * Test REST API controller Info.
- * 
+ *
  * @package Api\Controllers
  */
 class InfoTest extends \PHPUnit_Framework_TestCase
index d22ed3bfe97fe8d98054f1eff9e9e3597573e5a9..64f027743787d22fd7ad9a700145c4e6563ba6e7 100644 (file)
@@ -95,7 +95,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($this->refDB->countLinks(), count($data));
 
         // Check order
-        $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
+        $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
         $cpt = 0;
         foreach ($data as $link) {
             $this->assertEquals(self::NB_FIELDS_LINK, count($link));
@@ -103,7 +103,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         }
 
         // Check first element fields
-        $first = $data[0];
+        $first = $data[2];
         $this->assertEquals('http://domain.tld/?WDWyig', $first['url']);
         $this->assertEquals('WDWyig', $first['shorturl']);
         $this->assertEquals('Link title: @website', $first['title']);
@@ -120,7 +120,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $this->assertEmpty($first['updated']);
 
         // Multi tags
-        $link = $data[1];
+        $link = $data[3];
         $this->assertEquals(7, count($link['tags']));
 
         // Update date
@@ -138,7 +138,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
     {
         $env = Environment::mock([
             'REQUEST_METHOD' => 'GET',
-            'QUERY_STRING' => 'offset=1&limit=1'
+            'QUERY_STRING' => 'offset=3&limit=1'
         ]);
         $request = Request::createFromEnvironment($env);
         $response = $this->controller->getLinks($request, new Response());
@@ -164,7 +164,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $data = json_decode((string) $response->getBody(), true);
         $this->assertEquals($this->refDB->countLinks(), count($data));
         // Check order
-        $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
+        $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
         $cpt = 0;
         foreach ($data as $link) {
             $this->assertEquals(self::NB_FIELDS_LINK, count($link));
@@ -205,7 +205,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(200, $response->getStatusCode());
         $data = json_decode((string)$response->getBody(), true);
         $this->assertEquals($this->refDB->countLinks(), count($data));
-        $this->assertEquals(41, $data[0]['id']);
+        $this->assertEquals(10, $data[0]['id']);
+        $this->assertEquals(41, $data[2]['id']);
         $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
     }
 
@@ -243,7 +244,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(200, $response->getStatusCode());
         $data = json_decode((string)$response->getBody(), true);
         $this->assertEquals($this->refDB->countPublicLinks(), count($data));
-        $this->assertEquals(41, $data[0]['id']);
+        $this->assertEquals(10, $data[0]['id']);
+        $this->assertEquals(41, $data[2]['id']);
         $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
     }
 
@@ -413,8 +415,9 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
         $response = $this->controller->getLinks($request, new Response());
         $this->assertEquals(200, $response->getStatusCode());
         $data = json_decode((string) $response->getBody(), true);
-        $this->assertEquals(9, count($data));
-        $this->assertEquals(41, $data[0]['id']);
+        $this->assertEquals(\ReferenceLinkDB::$NB_LINKS_TOTAL, count($data));
+        $this->assertEquals(10, $data[0]['id']);
+        $this->assertEquals(41, $data[2]['id']);
 
         // wildcard: optional ('*' does not need to expand)
         $env = Environment::mock([
index 100a91704f866e383430cf2e690522302ba20957..5c2b56232c4691529c0aa8ac96904a1c4ec9ec54 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace Shaarli\Api\Controllers;
 
-
 use PHPUnit\Framework\TestCase;
 use Shaarli\Config\ConfigManager;
 use Slim\Container;
@@ -128,7 +127,9 @@ class PostLinkTest extends TestCase
         $this->assertEquals('', $data['description']);
         $this->assertEquals([], $data['tags']);
         $this->assertEquals(false, $data['private']);
-        $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']));
+        $this->assertTrue(
+            new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
+        );
         $this->assertEquals('', $data['updated']);
 
         $historyEntry = $this->history->getHistory()[0];
@@ -171,7 +172,9 @@ class PostLinkTest extends TestCase
         $this->assertEquals($link['description'], $data['description']);
         $this->assertEquals($link['tags'], $data['tags']);
         $this->assertEquals(true, $data['private']);
-        $this->assertTrue(new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']));
+        $this->assertTrue(
+            new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
+        );
         $this->assertEquals('', $data['updated']);
     }
 
index 8a5625710b5e924cda9d3c5146112b3d3851f353..f276b4c179df6edcec3c7896c4621bc7bdc93bf1 100644 (file)
@@ -3,7 +3,6 @@
 
 namespace Shaarli\Api\Controllers;
 
-
 use Shaarli\Config\ConfigManager;
 use Slim\Container;
 use Slim\Http\Environment;
@@ -115,7 +114,9 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
             \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
             \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
         );
-        $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']));
+        $this->assertTrue(
+            new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
+        );
 
         $historyEntry = $this->history->getHistory()[0];
         $this->assertEquals(\History::UPDATED, $historyEntry['event']);
@@ -160,7 +161,9 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
             \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
             \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
         );
-        $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']));
+        $this->assertTrue(
+            new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
+        );
     }
 
     /**
index 6f7dec224071c7c764506a52e4b1e2680ad33cef..3801724304d9b011c88325ff60d77fc8e0cf826f 100644 (file)
@@ -3,7 +3,6 @@
 
 namespace Shaarli\Api\Controllers;
 
-
 use Shaarli\Api\Exceptions\ApiBadParametersException;
 use Shaarli\Config\ConfigManager;
 use Slim\Container;
index 4569c923b3171609d8a755cd32aa4cdf06c755a2..588c9fd6e4749e59517bbbedd248c04698331616 100644 (file)
@@ -20,7 +20,7 @@ class UtilsDeTest extends UtilsTest
     public function testDateFormatNoTime()
     {
         $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
-        $this->assertRegExp('/1\. Januar 2017/', format_date($date, false,true));
+        $this->assertRegExp('/1\. Januar 2017/', format_date($date, false, true));
     }
 
     /**
index 0cf74891b7ab937403c2b6f6184975edeb5a2671..38347de19cce5e543e8774d60b5991a19c6bc8ed 100644 (file)
@@ -3,7 +3,6 @@
 
 namespace Shaarli;
 
-
 use Shaarli\Config\ConfigManager;
 
 /**
index 0ae73183808666ec74bf7ef7ea64e50f63b73c94..2c9efbcde4292a221392a725d73d488dd26c2c9e 100644 (file)
@@ -21,7 +21,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
     /**
      * Test Isso init without errors.
      */
-    public function testWallabagInitNoError()
+    public function testIssoInitNoError()
     {
         $conf = new ConfigManager('');
         $conf->set('plugins.ISSO_SERVER', 'value');
@@ -32,7 +32,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
     /**
      * Test Isso init with errors.
      */
-    public function testWallabagInitError()
+    public function testIssoInitError()
     {
         $conf = new ConfigManager('');
         $errors = isso_init($conf);
@@ -96,19 +96,22 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
                 array(
                     'id' => 12,
                     'url' => $str,
+                    'shorturl' => $short1 = 'abcd',
                     'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1),
                 ),
                 array(
                     'id' => 13,
                     'url' => $str . '2',
+                    'shorturl' => $short2 = 'efgh',
                     'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2),
                 ),
             )
         );
 
         $processed = hook_isso_render_linklist($data, $conf);
-        // data shouldn't be altered
-        $this->assertEquals($data, $processed);
+        // link_plugin should be added for the icon
+        $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
+        $this->assertContains('<a href="?'. $short2 .'#isso-thread">', $processed['links'][1]['link_plugin'][0]);
     }
 
     /**
@@ -127,6 +130,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
                 array(
                     'id' => 12,
                     'url' => $str,
+                    'shorturl' => $short1 = 'abcd',
                     'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date),
                 )
             ),
@@ -135,8 +139,8 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
 
         $processed = hook_isso_render_linklist($data, $conf);
 
-        // data shouldn't be altered
-        $this->assertEquals($data, $processed);
+        // link_plugin should be added for the icon
+        $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
     }
 
     /**
index b31e817fe6093c85e7a5dbb57813a1d6f2ec0ad0..44364b05b5f4a14036e432a51fdc539668ac39dd 100644 (file)
@@ -47,6 +47,8 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
         $data = hook_markdown_render_linklist($data, $this->conf);
         $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>'));
         $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>'));
+
+        $this->assertEquals($markdown, $data['links'][0]['description_src']);
     }
 
     /**
@@ -106,6 +108,18 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
         $this->assertEquals($text, $reversedText);
     }
 
+    /**
+     * Test reverse_text2clickable().
+     */
+    public function testReverseText2clickableHashtags()
+    {
+        $text = file_get_contents('tests/plugins/resources/hashtags.raw');
+        $md = file_get_contents('tests/plugins/resources/hashtags.md');
+        $clickableText = hashtag_autolink($text);
+        $reversedText = reverse_text2clickable($clickableText);
+        $this->assertEquals($md, $reversedText);
+    }
+
     /**
      * Test reverse_nl2br().
      */
@@ -246,7 +260,7 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
             $this->conf->get('security.markdown_escape', true),
             $this->conf->get('security.allowed_protocols')
         );
-        $this->assertEquals($html, $data);
+        $this->assertEquals($html, $data . PHP_EOL);
     }
 
     /**
index ebfadddf39b0bd6241b13273d8734d633c74cebe..dd632eee4594592a5ebcadf47b6c386caffe7a49 100644 (file)
@@ -15,7 +15,8 @@ class PluginQrcodeTest extends PHPUnit_Framework_TestCase
     /**
      * Reset plugin path
      */
-    public function setUp() {
+    public function setUp()
+    {
         PluginManager::$PLUGINS_PATH = 'plugins';
     }
 
diff --git a/tests/plugins/resources/hashtags.md b/tests/plugins/resources/hashtags.md
new file mode 100644 (file)
index 0000000..46326de
--- /dev/null
@@ -0,0 +1,10 @@
+[#lol](?addtag=lol)
+
+    #test
+
+`#test2`
+
+```
+bla #bli blo
+#bla
+```
diff --git a/tests/plugins/resources/hashtags.raw b/tests/plugins/resources/hashtags.raw
new file mode 100644 (file)
index 0000000..9d2dc98
--- /dev/null
@@ -0,0 +1,10 @@
+#lol
+
+    #test
+
+`#test2`
+
+```
+bla #bli blo
+#bla
+```
index f1df4e7ee96fa1aba001e0b6ee067b8958d1c61e..c3460bf7880c58c4184bc862e115029c7364b574 100644 (file)
 <li><a href="http://link.tld">two</a></li>
 <li><a href="http://link.tld">three</a></li>
 <li><a href="http://link.tld">four</a></li>
-<li>foo &lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt;</li>
+<li>foo <a href="?addtag=foobar">#foobar</a></li>
 </ol></li>
 </ol>
-<p>&lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt; foo <code>lol #foo</code> &lt;a href=&quot;?addtag=bar&quot; title=&quot;Hashtag bar&quot;&gt;#bar&lt;/a&gt;</p>
-<p>fsdfs <a href="http://link.tld">http://link.tld</a> &lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt; <code>http://link.tld</code></p>
+<p><a href="?addtag=foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar">#bar</a></p>
+<p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar">#foobar</a> <code>http://link.tld</code></p>
 <pre><code>http://link.tld #foobar
 next #foo</code></pre>
 <p>Block:</p>
@@ -30,4 +30,4 @@ next #foo</code></pre>
 <a href="ftp://test.tld/path/?query=value#hash">link</a><br />
 <a href="magnet:test.tld/path/?query=value#hash">link</a><br />
 <a href="http://alert(&#039;xss&#039;)">link</a><br />
-<a href="http://test.tld/path/?query=value#hash">link</a></p></div>
\ No newline at end of file
+<a href="http://test.tld/path/?query=value#hash">link</a></p></div>
index b8ebd9340870e338a8a125d1522f8fe96970cc15..9350a8c7dd9f4c7011d85f64dc6abba92eca3401 100644 (file)
@@ -31,4 +31,4 @@ lorem ipsum #foobar http://link.tld
 [link](ftp://test.tld/path/?query=value#hash)
 [link](magnet:test.tld/path/?query=value#hash)
 [link](javascript:alert('xss'))
-[link](other://test.tld/path/?query=value#hash)
\ No newline at end of file
+[link](other://test.tld/path/?query=value#hash)
index 9bd868f809687273bf680f8a551ac41d876d8684..7961e7710dc3581a2d7f63659fc6f2aa500c25b2 100644 (file)
@@ -8,7 +8,6 @@ ReferenceSessionIdHashes::genAllHashes();
 use \Shaarli\Security\SessionManager;
 use \PHPUnit\Framework\TestCase;
 
-
 /**
  * Test coverage for SessionManager
  */
index e887aa78c2251747c46cf37fab4359c782b32b36..59679e380f8d59b8e959d5773b941ec9b68b3a65 100644 (file)
@@ -4,7 +4,7 @@
  */
 class ReferenceLinkDB
 {
-    public static $NB_LINKS_TOTAL = 9;
+    public static $NB_LINKS_TOTAL = 11;
 
     private $_links = array();
     private $_publicCount = 0;
@@ -15,6 +15,32 @@ class ReferenceLinkDB
      */
     public function __construct()
     {
+        $this->addLink(
+            11,
+            'Pined older',
+            '?PCRizQ',
+            'This is an older pinned link',
+            0,
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100309_101010'),
+            '',
+            null,
+            'PCRizQ',
+            true
+        );
+
+        $this->addLink(
+            10,
+            'Pined',
+            '?0gCTjQ',
+            'This is a pinned link',
+            0,
+            DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121207_152312'),
+            '',
+            null,
+            '0gCTjQ',
+            true
+        );
+
         $this->addLink(
             41,
             'Link title: @website',
@@ -114,8 +140,18 @@ class ReferenceLinkDB
     /**
      * Adds a new link
      */
-    protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '')
-    {
+    protected function addLink(
+        $id,
+        $title,
+        $url,
+        $description,
+        $private,
+        $date,
+        $tags,
+        $updated = '',
+        $shorturl = '',
+        $pinned = false
+    ) {
         $link = array(
             'id' => $id,
             'title' => $title,
@@ -126,6 +162,7 @@ class ReferenceLinkDB
             'created' => $date,
             'updated' => $updated,
             'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id),
+            'sticky' => $pinned
         );
         $this->_links[$id] = $link;
 
@@ -164,7 +201,11 @@ class ReferenceLinkDB
 
         $order = $order === 'ASC' ? -1 : 1;
         // Reorder array by dates.
-        usort($this->_links, function($a, $b) use ($order) {
+        usort($this->_links, function ($a, $b) use ($order) {
+            if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
+                return $a['sticky'] ? -1 : 1;
+            }
+
             return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
         });
     }
index 0e034175757d7ccc1f076606d70b181bc05b8f88..34b11fcddf1a76ed9a1ef3f926e999ce9ab70cd8 100644 (file)
@@ -1,4 +1,4 @@
-<?php 
+<?php
 $GLOBALS['login'] = 'root';
 $GLOBALS['hash'] = 'hash';
 $GLOBALS['salt'] = 'salt';
index 2f956e061bc30534a1f15cbaa2087e2a51da9c3d..55864a023947c201ad09fd32754452f45bb02c6d 100644 (file)
@@ -11,7 +11,8 @@
     <h2 class="window-title">{"Shaare a new link"|t}</h2>
     <form method="GET" action="#" name="addform" class="addform">
       <div>
-        <input type="text" name="post" placeholder="{'URL or leave empty to post a note'|t}" class="autofocus">
+        <label for="shaare">{'URL or leave empty to post a note'|t}</label>
+        <input type="text" name="post" id="shaare" class="autofocus">
       </div>
       <div>
         <input type="submit" value="{'Add link'|t}">
index 816e5d0a8bd342cdb200fdb34525a6dbce5865d9..2c409478bed424edd51700855ed2f352c70dd9df 100644 (file)
@@ -72,7 +72,7 @@
                   {if="$thumbnails_enabled && !empty($link.thumbnail)"}
                     <div class="daily-entry-thumbnail">
                       <img data-src="{$link.thumbnail}#" class="b-lazy"
-                           src="#"
+                           src=""
                            alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
                     </div>
                   {/if}
index 5ccacaafb82d1dada9c27a1ea7fac317e267e2b4..0427e2243a37bc1dc0e2085ce6996772d8a682a9 100644 (file)
   <link type="text/css" rel="stylesheet" href="data/user.css#" />
 {/if}
 <link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle}"/>
+{if="! empty($links) && count($links) === 1"}
+  {$link=reset($links)}
+  <meta property="og:title" content="{$link.title}" />
+  <meta property="og:type" content="article" />
+  <meta property="og:url" content="{$index_url}?{$link.shorturl}" />
+  {$ogDescription=isset($link.description_src) ? $link.description_src : $link.description}
+  <meta property="og:description" content="{function="substr($ogDescription, 0, 300)"}" />
+  {if="$link.thumbnail"}
+    <meta property="og:image" content="{$index_url}{$link.thumbnail}" />
+  {/if}
+  {if="!$hide_timestamps || $is_logged_in"}
+    <meta property="article:published_time" content="{$link.created->format(DateTime::ATOM)}" />
+    {if="$link.updated"}
+      <meta property="article:modified_time" content="{$link.updated->format(DateTime::ATOM)}" />
+    {/if}
+  {/if}
+  {loop="link.taglist"}
+    <meta property="article:tag" content="{$value}" />
+  {/loop}
+{/if}
index 8ea2ce66194ef728581cd75f8c5a7dfa5cf157e8..ed78f40a7502f625e60659a92738ef91fc669226 100644 (file)
       {$strPermalink=t('Permalink')}
       {$strPermalinkLc=t('permalink')}
       {$strAddTag=t('Add tag')}
+      {$strToggleSticky=t('Toggle sticky')}
+      {$strSticky=t('Sticky')}
       {ignore}End of translations{/ignore}
       {loop="links"}
         <div class="anchor" id="{$value.shorturl}"></div>
                   <a href="{$value.real_url}">
                   {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
                   <img data-src="{$value.thumbnail}#" class="b-lazy"
-                    src="#"
+                    src=""
                     alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
                   </a>
                 </div>
                 {if="$is_logged_in"}
                   <div class="linklist-item-infos-controls-group pure-u-0 pure-u-lg-visible">
                     <span class="linklist-item-infos-controls-item ctrl-checkbox">
-                      <input type="checkbox" class="delete-checkbox" value="{$value.id}">
+                      <input type="checkbox" class="link-checkbox" value="{$value.id}">
                     </span>
                     <span class="linklist-item-infos-controls-item ctrl-edit">
                       <a href="?edit_link={$value.id}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link"></i></a>
                         <i class="fa fa-trash"></i>
                       </a>
                     </span>
+                    <span class="linklist-item-infos-controls-item ctrl-pin">
+                      <a href="?do=pin&amp;id={$value.id}&amp;token={$token}"
+                         title="{$strToggleSticky}" class="pin-link {if="$value.sticky"}pinned-link{/if} pure-u-0 pure-u-lg-visible">
+                        <i class="fa fa-thumb-tack"></i>
+                      </a>
+                    </span>
                   </div>
+                {else}
+                  {if="$value.sticky"}
+                    <div class="linklist-item-infos-controls-group pure-u-0 pure-u-lg-visible">
+                      <span class="linklist-item-infos-controls-item ctrl-pin">
+                        <span title="{$strSticky}" class="pin-link pinned-link pure-u-0 pure-u-lg-visible">
+                          <i class="fa fa-thumb-tack"></i>
+                        </span>
+                      </span>
+                    </div>
+                  {/if}
                 {/if}
                 <a href="?{$value.shorturl}" title="{$strPermalink}">
                   {if="!$hide_timestamps || $is_logged_in"}
index 5309e348a8c38ba236be2ee13be916279c84fa67..fe665a840293fa2396ff4b0ea9470ce6a091cc4a 100644 (file)
@@ -16,6 +16,9 @@
         <a href="?untaggedonly" title="{'Filter untagged links'|t}"
            class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
         ><i class="fa fa-tag"></i></a>
+        <a href="#" title="{'Select all'|t}"
+           class="filter-off select-all-button"
+        ><i class="fa fa-check-square-o"></i></a>
         <a href="#" class="filter-off fold-all pure-u-lg-0" title="{'Fold all'|t}">
           <i class="fa fa-chevron-up"></i>
         </a>
index 3b43a6115b3151cccc3486a33f59c2389fa0c339..4f6dd4d8f87afa536db31854f99a3f9083c6080f 100644 (file)
   <div id="actions" class="subheader-form">
     <div class="pure-g">
       <div class="pure-u-1">
-        <a href="" id="actions-delete" class="button">Delete</a>
+        <a href="" id="actions-delete" class="button">{'Delete'|t}</a>
       </div>
     </div>
   </div>
index 9a0b10dc6084e2e3ac7e29d0fa562a6728c56426..4c325487ac0dbfe10328f79205a4f4e9a4d1f242 100644 (file)
@@ -37,7 +37,7 @@
           <div class="picwall-pictureframe">
             {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
             <img data-src="{$value.thumbnail}#" class="b-lazy"
-                 src="#"
+                 src=""
                  alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
             <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
             {loop="$value.picwall_plugin"}
index 00148a585b88ebcd1a16db3f35aebf83246dfc29..71d84475df2a2b1f42a20eb43159070438750495 100644 (file)
@@ -71,7 +71,7 @@
                         {if="$thumbnails_enabled && !empty($link.thumbnail)"}
                             <div class="dailyEntryThumbnail">
                               <img data-src="{$link.thumbnail}#" class="b-lazy"
-                                   src="#"
+                                   src=""
                                    alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
                             </div>
                         {/if}
index 410b466b1b5d8f1954c90b4bdfdbec5f1fd0115b..df093495572ba72888c023f4af1b01d26163b1f0 100644 (file)
 {/loop}
 {if="is_file('data/user.css')"}<link type="text/css" rel="stylesheet" href="data/user.css#" />{/if}
 <link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle|htmlspecialchars}"/>
+{if="! empty($links) && count($links) === 1"}
+  {$link=reset($links)}
+  <meta property="og:title" content="{$link.title}" />
+  <meta property="og:type" content="article" />
+  <meta property="og:url" content="{$index_url}?{$link.shorturl}" />
+  {$ogDescription=isset($link.description_src) ? $link.description_src : $link.description}
+  <meta property="og:description" content="{function="mb_substr($ogDescription, 0, 300)"}" />
+  {if="$link.thumbnail"}
+    <meta property="og:image" content="{$index_url}{$link.thumbnail}" />
+  {/if}
+  {if="!$hide_timestamps || $is_logged_in"}
+    <meta property="article:published_time" content="{$link.created->format(DateTime::ATOM)}" />
+    {if="$link.updated"}
+      <meta property="article:modified_time" content="{$link.updated->format(DateTime::ATOM)}" />
+    {/if}
+  {/if}
+  {loop="link.taglist"}
+    <meta property="article:tag" content="{$value}" />
+  {/loop}
+{/if}
index 3f202849db3313b91f26b32ef2d5deec3b019656..dcb14e908042f6a6088ca118f820f19551cac47a 100644 (file)
@@ -85,7 +85,7 @@
                     <a href="{$value.real_url}">
                         {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
                         <img data-src="{$value.thumbnail}#" class="b-lazy"
-                             src="#"
+                             src=""
                              alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
                     </a>
                 </div>
index 5f1d266ee1f15ddc7bc4f00403e968511d033b7d..b3a16791b679a06dc8cadea773d299f3cf0e701f 100644 (file)
@@ -17,7 +17,7 @@
             <div class="picwall_pictureframe">
                 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
                 <img data-src="{$value.thumbnail}#" class="b-lazy"
-                     src="#"
+                     src=""
                      alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
                 <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
                 {loop="$value.picwall_plugin"}