Merge remote-tracking branch 'github/latest' into myShaarli_commu

This commit is contained in:
Knah Tsaeb 2018-02-09 16:10:09 +01:00
commit d923d1db2f
28 changed files with 730 additions and 290 deletions

2
.github/mailmap vendored
View file

@ -1,6 +1,8 @@
ArthurHoaro <arthur@hoa.ro>
Florian Eula <eula.florian@gmail.com> feula
Florian Eula <eula.florian@gmail.com> <mr.pikzen@gmail.com>
Immánuel Fodor <immanuelfactor+github@gmail.com>
kalvn <kalvnthereal@gmail.com> <kalvn@users.noreply.github.com>
Nicolas Danelon <hi@nicolasmd.com.ar> nicolasm
Nicolas Danelon <hi@nicolasmd.com.ar> <nda@3818.com.ar>
Nicolas Danelon <hi@nicolasmd.com.ar> <nicolasdanelon@gmail.com>

13
AUTHORS
View file

@ -1,6 +1,6 @@
537 ArthurHoaro <arthur@hoa.ro>
252 VirtualTam <virtualtam@flibidi.net>
148 nodiscc <nodiscc@gmail.com>
588 ArthurHoaro <arthur@hoa.ro>
283 VirtualTam <virtualtam@flibidi.net>
179 nodiscc <nodiscc@gmail.com>
56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
15 Florian Eula <eula.florian@gmail.com>
13 Emilien Klein <emilien@klein.st>
@ -11,8 +11,9 @@
5 Lucas Cimon <lucas.cimon@gmail.com>
4 Alexandre Alapetite <alexandre@alapetite.fr>
4 David Sferruzza <david.sferruzza@gmail.com>
4 Immánuel Fodor <immanuelfactor+github@gmail.com>
4 kalvn <kalvnthereal@gmail.com>
3 Teromene <teromene@teromene.fr>
3 kalvn <kalvnthereal@gmail.com>
2 Chris Kuethe <chris.kuethe@gmail.com>
2 Knah Tsaeb <Knah-Tsaeb@knah-tsaeb.org>
2 Mathieu Chabanon <git@matchab.fr>
@ -27,11 +28,13 @@
1 BoboTiG <bobotig@gmail.com>
1 Bronco <bronco@warriordudimanche.net>
1 D Low <daniellowtw@gmail.com>
1 Daniel Jakots <vigdis@chown.me>
1 Dimtion <zizou.xena@gmail.com>
1 Fanch <fanch-github@qth.fr>
1 Felix Bartels <felix@host-consultants.de>
1 Felix Kästner <github.com-fpunktk@fpunktk.de>
1 Florian Voigt <flvoigt@me.com>
1 Franck Kerbiriou <FranckKe@users.noreply.github.com>
1 Gary Marigliano <gmarigliano93@gmail.com>
1 Guillaume Virlet <github@virlet.org>
1 Jonathan Druart <jonathan.druart@gmail.com>
@ -41,6 +44,8 @@
1 Lionel Martin <renarddesmers@gmail.com>
1 Mark Gerarts <mark.gerarts@gmail.com>
1 Marsup <marsup@gmail.com>
1 Neros <contact@neros.fr>
1 Sbgodin <Sbgodin@users.noreply.github.com>
1 TsT <tst2005@gmail.com>
1 dimtion <zizou.xena@gmail.com>
1 durcheinandr <jochen@durcheinandr.de>

View file

@ -4,6 +4,49 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [v0.10.0](https://github.com/shaarli/Shaarli/releases/tag/v0.10.0) - UNPUBLISHED
## [v0.9.5](https://github.com/shaarli/Shaarli/releases/tag/v0.9.5) - 2018-02-02
### Fixed
- Fix a warning happening when `php-intl` is not installed on the system
- Fix warnings happening when updating from legacy SebSauvage version
## [v0.9.4](https://github.com/shaarli/Shaarli/releases/tag/v0.9.4) - 2018-01-30
### Added
- Enable translations: Shaarli is now also available in French. Other language translations are welcome!
- Add EditorConfig configuration
- Add favicons for mobile devices
- Add Alpine Linux arm32v7 Dockerfiles (master, latest)
### Changed
- Do not write bookmark edition history during file imports (performance)
- Migrate Docker images (master, latest) to Alpine Linux
- Improve unitary tests and code coverage
- Improve thumbnail display
- Improve theme ergonomics
- Improve messages if there is no plugin or parameter available in the admin page
- Increase buffer size for cURL download
- Force HTTPS if the original port is 443 behind a reverse proxy (workaround)
- Improve page title retrieval performances
### Removed
- Remove redirector setting from Configure page
### Fixed
- Fix broken links in the documentation
- Enable access to `data/user.css` (Apache 2.2 & 2.4)
- Don't URL encode description links if parameter `redirector.encode_url` is set to false
- Fix an issue preventing the Save button to appear for plugin parameters
## [v0.9.3](https://github.com/shaarli/Shaarli/releases/tag/v0.9.3) - 2018-01-04
**XSS vulnerability fixed. Please update.**
## Security
- Fix an XSS (cross-site-scripting) vulnerability in `index.php` -
[CVE-2018-5249](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5249)
## [v0.9.2](https://github.com/shaarli/Shaarli/releases/tag/v0.9.2) - 2017-10-07
**Major security issue fixed. Please update.**
@ -42,6 +85,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed reflected XSS vulnerability introduced in v0.9.1, discovered by @chb9 ([CVE-2017-15215](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15215)).
## [v0.9.1](https://github.com/shaarli/Shaarli/releases/tag/v0.9.1) - 2017-08-23
The documentation has been migrated to ReadTheDocs:
@ -187,6 +231,13 @@ Theming:
- Editing a link created before the new ID system would change its permalink.
## [v0.8.5](https://github.com/shaarli/Shaarli/releases/tag/v0.8.5) - 2018-01-04
**XSS vulnerability fixed. Please update.**
## Security
- Fix an XSS (cross-site-scripting) vulnerability in `index.php` -
[CVE-2018-5249](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5249)
## [v0.8.4](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4) - 2017-03-04
### Security
- Markdown plugin: escape HTML entities by default

View file

@ -6,13 +6,13 @@ _Do you want to share the links you discover?_
_Shaarli is a minimalist link sharing service that you can install on your own server._
_It is designed to be personal (single-user), fast and handy._
[![](https://img.shields.io/badge/stable-v0.8.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4)
[![](https://img.shields.io/badge/stable-v0.8.5-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.5)
[![](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.9.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.2)
[![](https://img.shields.io/badge/latest-v0.9.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.4)
[![](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.9.x-blue.svg)](https://github.com/shaarli/Shaarli)
[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli)
[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli)
[![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli)

View file

@ -6,6 +6,8 @@
* @param string $url URL to get (http://...)
* @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.).
*
* @return array HTTP response headers, downloaded content
*
@ -29,7 +31,7 @@
* @see http://stackoverflow.com/q/9183178
* @see http://stackoverflow.com/q/1462720
*/
function get_http_response($url, $timeout = 30, $maxBytes = 4194304)
function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteFunction = null)
{
$urlObj = new Url($url);
$cleanUrl = $urlObj->idnToAscii();
@ -75,6 +77,10 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304)
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);

View file

@ -69,6 +69,8 @@ public function __construct($language, $conf)
{
$this->conf = $conf;
$confLanguage = $this->conf->get('translation.language', 'auto');
// Auto mode or invalid parameter, use the detected language.
// If the detected language is invalid, it doesn't matter, it will use English.
if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) {
$this->language = substr($language, 0, 5);
} else {

View file

@ -1,5 +1,54 @@
<?php
/**
* Get cURL callback function for CURLOPT_WRITEFUNCTION
*
* @param string $charset to extract from the downloaded page (reference)
* @param string $title to extract from the downloaded page (reference)
* @param string $curlGetInfo Optionnaly overrides curl_getinfo function
*
* @return Closure
*/
function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_getinfo')
{
/**
* cURL callback function for CURLOPT_WRITEFUNCTION (called during the download).
*
* While downloading the remote page, we check that the HTTP code is 200 and content type is 'html/text'
* Then we extract the title and the charset and stop the download when it's done.
*
* @param resource $ch cURL resource
* @param string $data chunk of data being downloaded
*
* @return int|bool length of $data or false if we need to stop the download
*/
return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title) {
$responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE);
if (!empty($responseCode) && $responseCode != 200) {
return false;
}
$contentType = $curlGetInfo($ch, CURLINFO_CONTENT_TYPE);
if (!empty($contentType) && strpos($contentType, 'text/html') === false) {
return false;
}
if (empty($charset)) {
$charset = header_extract_charset($contentType);
}
if (empty($charset)) {
$charset = html_extract_charset($data);
}
if (empty($title)) {
$title = html_extract_title($data);
}
// We got everything we want, stop the download.
if (!empty($responseCode) && !empty($contentType) && !empty($charset) && !empty($title)) {
return false;
}
return strlen($data);
};
}
/**
* Extract title from an HTML document.
*
@ -16,46 +65,18 @@ function html_extract_title($html)
}
/**
* Determine charset from downloaded page.
* Priority:
* 1. HTTP headers (Content type).
* 2. HTML content page (tag <meta charset>).
* 3. Use a default charset (default: UTF-8).
* Extract charset from HTTP header if it's defined.
*
* @param array $headers HTTP headers array.
* @param string $htmlContent HTML content where to look for charset.
* @param string $defaultCharset Default charset to apply if other methods failed.
*
* @return string Determined charset.
*/
function get_charset($headers, $htmlContent, $defaultCharset = 'utf-8')
{
if ($charset = headers_extract_charset($headers)) {
return $charset;
}
if ($charset = html_extract_charset($htmlContent)) {
return $charset;
}
return $defaultCharset;
}
/**
* Extract charset from HTTP headers if it's defined.
*
* @param array $headers HTTP headers array.
* @param string $header HTTP header Content-Type line.
*
* @return bool|string Charset string if found (lowercase), false otherwise.
*/
function headers_extract_charset($headers)
function header_extract_charset($header)
{
if (! empty($headers['Content-Type']) && strpos($headers['Content-Type'], 'charset=') !== false) {
preg_match('/charset="?([^; ]+)/i', $headers['Content-Type'], $match);
preg_match('/charset="?([^; ]+)/i', $header, $match);
if (! empty($match[1])) {
return strtolower(trim($match[1]));
}
}
return false;
}

View file

@ -83,10 +83,10 @@ public function read($filepath)
$out = array();
foreach (self::$ROOT_KEYS as $key) {
$out[$key] = $GLOBALS[$key];
$out[$key] = isset($GLOBALS[$key]) ? $GLOBALS[$key] : '';
}
$out['config'] = $GLOBALS['config'];
$out['plugins'] = !empty($GLOBALS['plugins']) ? $GLOBALS['plugins'] : array();
$out['config'] = isset($GLOBALS['config']) ? $GLOBALS['config'] : [];
$out['plugins'] = isset($GLOBALS['plugins']) ? $GLOBALS['plugins'] : [];
return $out;
}

178
composer.lock generated
View file

@ -294,16 +294,16 @@
},
{
"name": "pimple/pimple",
"version": "v3.2.2",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a"
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/4d45fb62d96418396ec58ba76e6f065bca16e10a",
"reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
@ -340,7 +340,7 @@
"container",
"dependency injection"
],
"time": "2017-07-23T07:32:15+00:00"
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
@ -533,16 +533,16 @@
},
{
"name": "shaarli/netscape-bookmark-parser",
"version": "v2.0.4",
"version": "v2.0.5",
"source": {
"type": "git",
"url": "https://github.com/shaarli/netscape-bookmark-parser.git",
"reference": "81023979c981514f5dda5582e9c0be2ed6688a6b"
"reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/81023979c981514f5dda5582e9c0be2ed6688a6b",
"reference": "81023979c981514f5dda5582e9c0be2ed6688a6b",
"url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/ea6911a0ea3dd372fa7002593c5aef9c15a49315",
"reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315",
"shasum": ""
},
"require": {
@ -584,20 +584,20 @@
"netscape",
"parse"
],
"time": "2017-07-30T21:08:03+00:00"
"time": "2018-01-30T17:34:48+00:00"
},
{
"name": "slim/slim",
"version": "3.8.1",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "5385302707530b2bccee1769613ad769859b826d"
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/5385302707530b2bccee1769613ad769859b826d",
"reference": "5385302707530b2bccee1769613ad769859b826d",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
"shasum": ""
},
"require": {
@ -655,7 +655,7 @@
"micro",
"router"
],
"time": "2017-03-19T17:55:20+00:00"
"time": "2017-11-26T19:13:09+00:00"
}
],
"packages-dev": [
@ -715,26 +715,26 @@
},
{
"name": "pdepend/pdepend",
"version": "2.5.0",
"version": "2.5.2",
"source": {
"type": "git",
"url": "https://github.com/pdepend/pdepend.git",
"reference": "0c50874333149c0dad5a2877801aed148f2767ff"
"reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/0c50874333149c0dad5a2877801aed148f2767ff",
"reference": "0c50874333149c0dad5a2877801aed148f2767ff",
"url": "https://api.github.com/repos/pdepend/pdepend/zipball/9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
"reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
"shasum": ""
},
"require": {
"php": ">=5.3.7",
"symfony/config": "^2.3.0|^3",
"symfony/dependency-injection": "^2.3.0|^3",
"symfony/filesystem": "^2.3.0|^3"
"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.4.0,<4.8",
"phpunit/phpunit": "^4.8|^5.7",
"squizlabs/php_codesniffer": "^2.0.0"
},
"bin": [
@ -751,7 +751,7 @@
"BSD-3-Clause"
],
"description": "Official version of pdepend to be handled with Composer",
"time": "2017-01-19T14:23:36+00:00"
"time": "2017-12-13T13:21:38+00:00"
},
{
"name": "phpdocumentor/reflection-common",
@ -967,16 +967,16 @@
},
{
"name": "phpspec/prophecy",
"version": "v1.7.2",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6"
"reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
"reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
"reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
"shasum": ""
},
"require": {
@ -988,7 +988,7 @@
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
"phpunit/phpunit": "^4.8 || ^5.6.5"
"phpunit/phpunit": "^4.8.35 || ^5.7"
},
"type": "library",
"extra": {
@ -1026,7 +1026,7 @@
"spy",
"stub"
],
"time": "2017-09-04T11:05:03+00:00"
"time": "2017-11-24T13:59:53+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1092,16 +1092,16 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.2",
"version": "1.4.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
"reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
"shasum": ""
},
"require": {
@ -1135,7 +1135,7 @@
"filesystem",
"iterator"
],
"time": "2016-10-03T07:40:28+00:00"
"time": "2017-11-27T13:52:08+00:00"
},
{
"name": "phpunit/php-text-template",
@ -1229,16 +1229,16 @@
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.11",
"version": "1.4.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
"reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
"reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16",
"reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16",
"shasum": ""
},
"require": {
@ -1274,7 +1274,7 @@
"keywords": [
"tokenizer"
],
"time": "2017-02-27T10:12:30+00:00"
"time": "2017-12-04T08:55:13+00:00"
},
{
"name": "phpunit/phpcov",
@ -1691,20 +1691,20 @@
},
{
"name": "sebastian/finder-facade",
"version": "1.2.1",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/finder-facade.git",
"reference": "2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9"
"reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9",
"reference": "2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9",
"url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f",
"reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f",
"shasum": ""
},
"require": {
"symfony/finder": "~2.3|~3.0",
"symfony/finder": "~2.3|~3.0|~4.0",
"theseer/fdomdocument": "~1.3"
},
"type": "library",
@ -1726,7 +1726,7 @@
],
"description": "FinderFacade is a convenience wrapper for Symfony's Finder component.",
"homepage": "https://github.com/sebastianbergmann/finder-facade",
"time": "2016-02-17T07:02:23+00:00"
"time": "2017-11-18T17:31:49+00:00"
},
{
"name": "sebastian/global-state",
@ -1998,30 +1998,30 @@
},
{
"name": "symfony/config",
"version": "v3.3.10",
"version": "v3.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd"
"reference": "72689b934d6c6ecf73eca874e98933bf055313c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/4ab62407bff9cd97c410a7feaef04c375aaa5cfd",
"reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd",
"url": "https://api.github.com/repos/symfony/config/zipball/72689b934d6c6ecf73eca874e98933bf055313c9",
"reference": "72689b934d6c6ecf73eca874e98933bf055313c9",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/filesystem": "~2.8|~3.0"
"symfony/filesystem": "~2.8|~3.0|~4.0"
},
"conflict": {
"symfony/dependency-injection": "<3.3",
"symfony/finder": "<3.3"
},
"require-dev": {
"symfony/dependency-injection": "~3.3",
"symfony/finder": "~3.3",
"symfony/yaml": "~3.0"
"symfony/dependency-injection": "~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"
@ -2029,7 +2029,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
"dev-master": "3.4-dev"
}
},
"autoload": {
@ -2056,20 +2056,20 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2017-10-04T18:56:58+00:00"
"time": "2018-01-21T19:05:02+00:00"
},
{
"name": "symfony/console",
"version": "v2.8.28",
"version": "v2.8.34",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853"
"reference": "162ca7d0ea597599967aa63b23418e747da0896b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/f81549d2c5fdee8d711c9ab3c7e7362353ea5853",
"reference": "f81549d2c5fdee8d711c9ab3c7e7362353ea5853",
"url": "https://api.github.com/repos/symfony/console/zipball/162ca7d0ea597599967aa63b23418e747da0896b",
"reference": "162ca7d0ea597599967aa63b23418e747da0896b",
"shasum": ""
},
"require": {
@ -2117,7 +2117,7 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2017-10-01T21:00:16+00:00"
"time": "2018-01-29T08:54:45+00:00"
},
{
"name": "symfony/debug",
@ -2178,16 +2178,16 @@
},
{
"name": "symfony/dependency-injection",
"version": "v3.3.10",
"version": "v3.3.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1"
"reference": "54243abc4e1a1a15e274e391bd6f7090b44711f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8ebad929aee3ca185b05f55d9cc5521670821ad1",
"reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/54243abc4e1a1a15e274e391bd6f7090b44711f1",
"reference": "54243abc4e1a1a15e274e391bd6f7090b44711f1",
"shasum": ""
},
"require": {
@ -2195,7 +2195,7 @@
"psr/container": "^1.0"
},
"conflict": {
"symfony/config": "<3.3.1",
"symfony/config": "<3.3.7",
"symfony/finder": "<3.3",
"symfony/yaml": "<3.3"
},
@ -2244,20 +2244,20 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
"time": "2017-10-04T17:15:30+00:00"
"time": "2018-01-29T09:02:23+00:00"
},
{
"name": "symfony/filesystem",
"version": "v3.3.10",
"version": "v3.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1"
"reference": "e078773ad6354af38169faf31c21df0f18ace03d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/90bc45abf02ae6b7deb43895c1052cb0038506f1",
"reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e078773ad6354af38169faf31c21df0f18ace03d",
"reference": "e078773ad6354af38169faf31c21df0f18ace03d",
"shasum": ""
},
"require": {
@ -2266,7 +2266,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
"dev-master": "3.4-dev"
}
},
"autoload": {
@ -2293,20 +2293,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2017-10-03T13:33:10+00:00"
"time": "2018-01-03T07:37:34+00:00"
},
{
"name": "symfony/finder",
"version": "v3.3.10",
"version": "v3.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "773e19a491d97926f236942484cb541560ce862d"
"reference": "613e26310776f49a1773b6737c6bd554b8bc8c6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/773e19a491d97926f236942484cb541560ce862d",
"reference": "773e19a491d97926f236942484cb541560ce862d",
"url": "https://api.github.com/repos/symfony/finder/zipball/613e26310776f49a1773b6737c6bd554b8bc8c6f",
"reference": "613e26310776f49a1773b6737c6bd554b8bc8c6f",
"shasum": ""
},
"require": {
@ -2315,7 +2315,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
"dev-master": "3.4-dev"
}
},
"autoload": {
@ -2342,7 +2342,7 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2017-10-02T06:42:24+00:00"
"time": "2018-01-03T07:37:34+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@ -2405,16 +2405,16 @@
},
{
"name": "symfony/yaml",
"version": "v3.3.10",
"version": "v3.3.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46"
"reference": "af615970e265543a26ee712c958404eb9b7ac93d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
"reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
"url": "https://api.github.com/repos/symfony/yaml/zipball/af615970e265543a26ee712c958404eb9b7ac93d",
"reference": "af615970e265543a26ee712c958404eb9b7ac93d",
"shasum": ""
},
"require": {
@ -2456,7 +2456,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2017-10-05T14:43:42+00:00"
"time": "2018-01-20T15:04:53+00:00"
},
{
"name": "theseer/fdomdocument",
@ -2500,16 +2500,16 @@
},
{
"name": "webmozart/assert",
"version": "1.2.0",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f"
"reference": "0df1908962e7a3071564e857d86874dad1ef204a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f",
"reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f",
"url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
"reference": "0df1908962e7a3071564e857d86874dad1ef204a",
"shasum": ""
},
"require": {
@ -2546,7 +2546,7 @@
"check",
"validate"
],
"time": "2016-11-23T20:04:58+00:00"
"time": "2018-01-29T19:49:41+00:00"
}
],
"aliases": [],

View file

@ -45,6 +45,10 @@ Shaarli cannot import data directly from [Scuttle](https://github.com/scronide/s
However, you can use the third-party [scuttle-to-shaarli](https://github.com/q2apro/scuttle-to-shaarli)
tool to export the Scuttle database to the Netscape HTML format compatible with the Shaarli importer.
### Refind
You can use the third-party tool [Derefind](https://github.com/ShawnPConroy/Derefind) to convert refind.com bookmark exports to a format that can be imported into Shaarli.
## Import Shaarli links to Firefox
- Export your Shaarli links as described above.

View file

@ -21,7 +21,7 @@ _This bookmarklet button is compatible with Firefox, Opera, Chrome and Safari. U
Websites which enforce Content Security Policy (CSP), such as github.com, disallow usage of bookmarklets. Unfortunatly, there is nothing Shaarli can do about it.
See [#196](https://github.com/shaarli/Shaarli#196).
See [#196](https://github.com/shaarli/Shaarli/issues/196).
There is an open bug for both Firefox and Chromium:

View file

@ -14,10 +14,24 @@ Use the `Filter by tags` field to restrict displayed links to entries tagged wit
**Hidden tags:** Tags starting with a dot `.` (example `.secret`) are private. They can only be seen and searched when logged in.
Alternatively you can use the `Tag cloud` to discover all tags and click on any of them to display related links.
### Tag cloud
To search for links that are not tagged, enter `""` in the tag search field.
The `Tag cloud` page diplays a "cloud" view of all tags in your Shaarli.
* The most frequently used tags are displayed with a bigger font size.
* When sorting by `Most used` or `Alphabetical`, tags are displayed as a _list_, along with counters and edit/delete buttons for each tag.
* Clicking on any tag will display a list of all Shaares matching this tag.
* Clicking on the counter next to a tag `example`, will filter the tag cloud to only display tags found in Shaares tagged `example`. Repeat this any number of times to further filter the tag cloud. Click `List all links with those tags` to display Shaares matching your current tag filter.
## Filtering RSS feeds/Picture wall
RSS feeds can also be restricted to only return items matching a text/tag search: see [RSS feeds](RSS-feeds).
## Filter buttons
Filter buttons can be found at the top left of the link list. They allow you to apply different filters to the list:
* **Private links:** When this toggle button is enabled, only shaares set to `private` will be shown.
* **Untagged links:** When the this toggle button is enabled (top left of the link list), only shaares _without any tags_ will be shown in the link list.
Filter buttons are only available when logged in.

View file

@ -1,6 +1,59 @@
_Unofficial but related work on Shaarli. If you maintain one of these,
please get in touch with us to help us find a way to adapt your work to our fork._
## Related software
### REST API clients
See [REST API](REST-API) for a list of official and community clients.
### Third party plugins
- [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.
- [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter.
- [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli.
- [emojione](https://github.com/NerosTie/emojione) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli.
- [twemoji](https://github.com/NerosTie/twemoji) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli (Twemoji version)
- [google analytics](https://github.com/ericjuden/Shaarli-Google-Analytics-Plugin) by [@ericjuden](http://github.com/ericjuden): Adds Google Analytics tracking support
- [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.
- [markdown-toolbar](https://github.com/immanuelfodor/shaarli-markdown-toolbar) by [@immanuelfodor](https://github.com/immanuelfodor) - Easily insert markdown syntax into the Description field when editing a link.
- [related](https://github.com/ilesinge/shaarli-related) by [@ilesinge](https://github.com/ilesinge) - Show related links based on the number of identical tags.
- [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks.
- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli
- [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline.
- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a link.
### Third-party themes
See [Theming](Theming) for a list of community-contributed themes, and an installation guide.
### Integration with other platforms
- [tt-rss-shaarli](https://github.com/jcsaaddupuy/tt-rss-shaarli) - [Tiny-Tiny RSS](http://tt-rss.org/) plugin that adds support for sharing articles with Shaarli
- [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli links on the sidebar
- [Scuttle to Shaarli](https://github.com/q2apro/scuttle-to-shaarli) - Import bookmarks from Scuttle
### Mobile Apps
- [ShaarliOS](https://github.com/mro/ShaarliOS) - Apple iOS share extension.
- [Shaarli for Android](http://sebsauvage.net/links/?ZAyDzg) - Android application that adds Shaarli as a sharing provider
- [Shaarlier for Android](https://github.com/dimtion/Shaarlier) - Android application to simply add links directly into your Shaarli
### Browser addons
* [Shaarli Web Extension](https://github.com/ikipatang/shaarli-web-extension) - toolbar button to share your current tab with Shaarli.
### Server apps
- [shaarchiver](https://github.com/nodiscc/shaarchiver) - Archive your Shaarli bookmarks and their content
- [shaarli-river](https://github.com/mknexen/shaarli-river) - An aggregator for shaarlis with many features
- [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).
- [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
See [awesome-selfhosted: bookmarks & link sharing](https://github.com/Kickball/awesome-selfhosted/#bookmarks--link-sharing).
## Community
- [Liens en vrac de sebsauvage](http://sebsauvage.net/links/) - the original Shaarli
- [A large list of Shaarlis](http://porneia.free.fr/pub/links/ou-est-shaarli.html)
@ -12,57 +65,8 @@ please get in touch with us to help us find a way to adapt your work to our fork
- [Original revisions history](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history)
- [Shaarli.fr/my](https://www.shaarli.fr/my.php) - Unofficial, unsupported (old fork) hosted Shaarlis provider, courtesy of [DMeloni](https://github.com/DMeloni)
### Articles and social media discussions
- 2016-09-22 - Hacker News - https://news.ycombinator.com/item?id=12552176
- 2015-08-15 - Reddit - [Question about migrating from WordPress to Shaarli.](https://www.reddit.com/r/selfhosted/comments/3h3zwh/question_about_migrating_from_wordpress_to_shaarli/)
- 2015-06-22 - Hacker News - https://news.ycombinator.com/item?id=9755366
- 2015-05-12 - Reddit - [shaarli - Self hosted Bookmarking / Delicious (PHP, MySQL)](https://www.reddit.com/r/selfhosted/comments/35pkkc/shaarli_self_hosted_bookmarking_delicious_php/)
### REST API clients
See [REST API](REST-API) for a list of official and community clients.
### Third party plugins
- [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.
- [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter.
- [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli.
- [emojione](https://github.com/NerosTie/emojione) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli.
- [google analytics](https://github.com/ericjuden/Shaarli-Google-Analytics-Plugin) by [@ericjuden](http://github.com/ericjuden): Adds Google Analytics tracking support
- [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.
- [related](https://github.com/ilesinge/shaarli-related) by [@ilesinge](https://github.com/ilesinge) - Show related links based on the number of identical tags.
- [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks.
- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli
- [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline.
### Third-party themes
See [Theming](Theming) for a list of community-contributed themes, and an installation guide.
## Integration with other platforms
- [tt-rss-shaarli](https://github.com/jcsaaddupuy/tt-rss-shaarli) - [Tiny-Tiny RSS](http://tt-rss.org/) plugin that adds support for sharing articles with Shaarli
- [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli links on the sidebar
- [Scuttle to Shaarli](https://github.com/q2apro/scuttle-to-shaarli) - Import bookmarks from Scuttle
### Mobile Apps
- [ShaarliOS](https://github.com/mro/ShaarliOS) iOS share extension - see [#308](https://github.com/shaarli/Shaarli/issues/308#issuecomment-184592070) for some promo codes,
- [Shaarli for Android](http://sebsauvage.net/links/?ZAyDzg) - Android application that adds Shaarli as a sharing provider
- [Shaarlier for Android](https://github.com/dimtion/Shaarlier) - Android application to simply add links directly into your Shaarli
### Server apps
- [shaarchiver](https://github.com/nodiscc/shaarchiver) - Archive your Shaarli bookmarks and their content
- [shaarli-river](https://github.com/mknexen/shaarli-river) - An aggregator for shaarlis with many features
- [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).
- [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
See the [bookmarks & link sharing](https://github.com/Kickball/awesome-selfhosted/#bookmarks--link-sharing)
section on [awesome-selfhosted](https://github.com/Kickball/awesome-selfhosted/).

View file

@ -15,7 +15,7 @@ Using one of the following methods:
- by downloading full release archives including all dependencies
- by downloading Github archives
- by cloning the Git repository
- using Docker: [see the documentation](docker/shaarli-images)
- using Docker: [see the documentation](docker/shaarli-images.md)
--------------------------------------------------------------------------------
@ -25,11 +25,11 @@ Using one of the following methods:
In most cases, you should download the latest Shaarli release from the [releases](https://github.com/shaarli/Shaarli/releases) page. **Download our *shaarli-full* archive** to include dependencies.
The current latest released version is `v0.9.1`
The current latest released version is `v0.9.3`
```bash
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.9.1/shaarli-v0.9.1-full.zip
$ unzip shaarli-v0.9.1-full.zip
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.9.3/shaarli-v0.9.3-full.zip
$ unzip shaarli-v0.9.3-full.zip
$ mv Shaarli /path/to/shaarli/
```

View file

@ -1,3 +1,6 @@
| Note | Firefox Share is no longer available for Firefox 57 and later versions. |
|---------|---------|
### Add Shaarli as a sharing service to Firefox
- Open your Shaarli and `Login`

View file

@ -35,7 +35,7 @@ Library | Required? | Usage
Extension | Required? | Usage
---|:---:|---
[`openssl`](http://php.net/manual/en/book.openssl.php) | All | OpenSSL, HTTPS
[`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows | multibyte (Unicode) string support
[`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows, some hosting providers | multibyte (Unicode) string support
[`php-gd`](http://php.net/manual/en/book.image.php) | optional | thumbnail resizing
[`php-intl`](http://php.net/manual/en/book.intl.php) | optional | localized text sorting (e.g. `e->è->f`)
[`php-curl`](http://php.net/manual/en/book.curl.php) | optional | using cURL for fetching webpages and thumbnails in a more robust way

View file

@ -1,3 +1,6 @@
A brief guide on getting starting using docker is given in [Docker 101](docker-101.md).
To learn more about user data and how to keep it across versions, please see [Upgrade and Migration](../Upgrade-and-migration.md).
## Get and run a Shaarli image
### DockerHub repository
@ -21,6 +24,7 @@ The `stable` image relies on:
- [PHP5-FPM](http://php-fpm.org/)
- [Nginx](http://nginx.org/)
Additional [Dockerfiles](https://github.com/shaarli/Shaarli/tree/master/docker) are provided for the `arm32v7` platform, relying on [Linuxserver.io Alpine armhf images](https://hub.docker.com/r/lsiobase/alpine.armhf/). These images must be built using [`docker build`](https://docs.docker.com/engine/reference/commandline/build/) on an `arm32v7` machine or using an emulator such as [qemu](https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/).
### Download from DockerHub
```bash
@ -78,3 +82,14 @@ backstabbing_galileo
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
```
### Automatic builds
Docker users can start a personal instance from an [autobuild image](https://hub.docker.com/r/shaarli/shaarli/). For example to start a temporary Shaarli at ``localhost:8000``, and keep session data (config, storage):
```
MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P)
docker run -ti --rm \
-p 8000:80 \
-v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \
shaarli/shaarli
```

View file

@ -22,20 +22,25 @@ It runs the latest development version of Shaarli and is updated/reset daily.
Login: `demo`; Password: `demo`
Docker users can start a personal instance from an [autobuild image](https://hub.docker.com/r/shaarli/shaarli/). For example to start a temporary Shaarli at ``localhost:8000``, and keep session data (config, storage):
```
MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P)
docker run -ti --rm \
-p 8000:80 \
-v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \
shaarli/shaarli
```
A brief guide on getting starting using docker is given in [Docker 101](docker/docker-101).
To learn more about user data and how to keep it across versions, please see [Upgrade and Migration](Upgrade-and-migration) documentation.
## 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 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 ...).
### Interface
- minimalist design (simple is beautiful)
- FAST
@ -89,14 +94,12 @@ Easily extensible by any client using the REST API exposed by Shaarli.
See the [API documentation](http://shaarli.github.io/api-documentation/).
### Other usages
Though Shaarli is primarily a bookmarking application, it can serve other purposes
(see [Features](Features)):
- micro-blogging
- pastebin
- online notepad
- snippet archive
### Using Shaarli as a blog, notepad, pastebin...
- Go to your Shaarli setup and log in
- Click the `Add Link` button
- To share text only, do not enter any URL in the corresponding input field and click `Add Link`
- Pick a title and enter your article, or note, in the description field; add a few tags; optionally check `Private` then click `Save`
- Voilà! Your article is now published (privately if you selected that option) and accessible using its permalink.
## About
### Shaarli community fork

View file

@ -0,0 +1,47 @@
FROM lsiobase/alpine.armhf:3.6
MAINTAINER Shaarli Community
RUN apk --update --no-cache add \
ca-certificates \
curl \
nginx \
php7 \
php7-ctype \
php7-curl \
php7-fpm \
php7-gd \
php7-iconv \
php7-intl \
php7-json \
php7-mbstring \
php7-openssl \
php7-phar \
php7-session \
php7-xml \
php7-zlib \
s6
COPY nginx.conf /etc/nginx/nginx.conf
COPY php-fpm.conf /etc/php7/php-fpm.conf
COPY services.d /etc/services.d
RUN curl -sS https://getcomposer.org/installer | php7 -- --install-dir=/usr/local/bin --filename=composer \
&& rm -rf /etc/php7/php-fpm.d/www.conf \
&& sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php7/php.ini \
&& sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php7/php.ini
WORKDIR /var/www
RUN curl -L https://github.com/shaarli/Shaarli/archive/latest.tar.gz | tar xzf - \
&& mv Shaarli-latest shaarli \
&& cd shaarli \
&& composer --prefer-dist --no-dev install \
&& rm -rf ~/.composer \
&& chown -R nginx:nginx .
VOLUME /var/www/shaarli/data
EXPOSE 80
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
CMD []

View file

@ -0,0 +1,47 @@
FROM lsiobase/alpine.armhf:3.6
MAINTAINER Shaarli Community
RUN apk --update --no-cache add \
ca-certificates \
curl \
nginx \
php7 \
php7-ctype \
php7-curl \
php7-fpm \
php7-gd \
php7-iconv \
php7-intl \
php7-json \
php7-mbstring \
php7-openssl \
php7-phar \
php7-session \
php7-xml \
php7-zlib \
s6
COPY nginx.conf /etc/nginx/nginx.conf
COPY php-fpm.conf /etc/php7/php-fpm.conf
COPY services.d /etc/services.d
RUN curl -sS https://getcomposer.org/installer | php7 -- --install-dir=/usr/local/bin --filename=composer \
&& rm -rf /etc/php7/php-fpm.d/www.conf \
&& sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php7/php.ini \
&& sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php7/php.ini
WORKDIR /var/www
RUN curl -L https://github.com/shaarli/Shaarli/archive/master.tar.gz | tar xzf - \
&& mv Shaarli-master shaarli \
&& cd shaarli \
&& composer --prefer-dist --no-dev install \
&& rm -rf ~/.composer \
&& chown -R nginx:nginx .
VOLUME /var/www/shaarli/data
EXPOSE 80
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
CMD []

View file

@ -124,6 +124,11 @@
$conf = new ConfigManager();
$sessionManager = new SessionManager($_SESSION, $conf);
// LC_MESSAGES isn't defined without php-intl, in this case use LC_COLLATE locale instead.
if (! defined('LC_MESSAGES')) {
define('LC_MESSAGES', LC_COLLATE);
}
// Sniff browser language and set date format accordingly.
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
autoLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
@ -436,7 +441,7 @@ function ban_canLogin($conf)
else
{
ban_loginFailed($conf);
$redir = '&username='. $_POST['login'];
$redir = '&username='. urlencode($_POST['login']);
if (isset($_GET['post'])) {
$redir .= '&post=' . urlencode($_GET['post']);
foreach (array('description', 'source', 'title', 'tags') as $param) {
@ -1443,18 +1448,12 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager)
// 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
list($headers, $content) = get_http_response($url, 4);
if (strpos($headers[0], '200 OK') !== false) {
// Retrieve charset.
$charset = get_charset($headers, $content);
// Extract title.
$title = html_extract_title($content);
// Re-encode title in utf-8 if necessary.
// The callback will fill $charset and $title with data from the downloaded page.
get_http_response($url, 25, 4194304, get_curl_download_callback($charset, $title));
if (! empty($title) && strtolower($charset) != 'utf-8') {
$title = mb_convert_encoding($title, 'utf-8', $charset);
}
}
}
if ($url == '') {
$url = '?' . smallHash($linkdate . $LINKSDB->getNextId());

View file

@ -22,16 +22,15 @@ pages:
- Reverse proxy configuration: docker/reverse-proxy-configuration.md
- Docker resources: docker/resources.md
- Usage:
- Features: Features.md
- Bookmarklet: Bookmarklet.md
- Browsing and searching: Browsing-and-searching.md
- Firefox share: Firefox-share.md
- RSS feeds: RSS-feeds.md
- REST API: REST-API.md
- Community & Related software: Community-&-Related-software.md
- How To:
- Backup, restore, import and export: Backup,-restore,-import-and-export.md
- Various hacks: Various-hacks.md
- Troubleshooting: Troubleshooting.md
- Development:
- Development guidelines: Development-guidelines.md
- Continuous integration tools: Continuous-integration-tools.md
@ -47,6 +46,5 @@ pages:
- Theming: Theming.md
- Unit tests: Unit-tests.md
- Unit tests inside Docker: Unit-tests-Docker.md
- About:
- FAQ: FAQ.md
- Community & Related software: Community-&-Related-software.md
- FAQ: FAQ.md
- Troubleshooting: Troubleshooting.md

View file

@ -1 +1 @@
<?php /* 0.9.2 */ ?>
<?php /* 0.9.5 */ ?>

View file

@ -28,28 +28,14 @@ public function testHtmlExtractNonExistentTitle()
$this->assertFalse(html_extract_title($html));
}
/**
* Test get_charset() with all priorities.
*/
public function testGetCharset()
{
$headers = array('Content-Type' => 'text/html; charset=Headers');
$html = '<html><meta>stuff</meta><meta charset="Html"/></html>';
$default = 'default';
$this->assertEquals('headers', get_charset($headers, $html, $default));
$this->assertEquals('html', get_charset(array(), $html, $default));
$this->assertEquals($default, get_charset(array(), '', $default));
$this->assertEquals('utf-8', get_charset(array(), ''));
}
/**
* Test headers_extract_charset() when the charset is found.
*/
public function testHeadersExtractExistentCharset()
{
$charset = 'x-MacCroatian';
$headers = array('Content-Type' => 'text/html; charset='. $charset);
$this->assertEquals(strtolower($charset), headers_extract_charset($headers));
$headers = 'text/html; charset='. $charset;
$this->assertEquals(strtolower($charset), header_extract_charset($headers));
}
/**
@ -57,11 +43,11 @@ public function testHeadersExtractExistentCharset()
*/
public function testHeadersExtractNonExistentCharset()
{
$headers = array();
$this->assertFalse(headers_extract_charset($headers));
$headers = '';
$this->assertFalse(header_extract_charset($headers));
$headers = array('Content-Type' => 'text/html');
$this->assertFalse(headers_extract_charset($headers));
$headers = 'text/html';
$this->assertFalse(header_extract_charset($headers));
}
/**
@ -85,6 +71,131 @@ public function testHtmlExtractNonExistentCharset()
$this->assertFalse(html_extract_charset($html));
}
/**
* Test the download callback with valid value
*/
public function testCurlDownloadCallbackOk()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ok');
$data = [
'HTTP/1.1 200 OK',
'Server: GitHub.com',
'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',
'<title>ignored</title>',
];
foreach ($data as $key => $line) {
$ignore = null;
$expected = $key !== 'end' ? strlen($line) : false;
$this->assertEquals($expected, $callback($ignore, $line));
if ($expected === false) {
break;
}
}
$this->assertEquals('utf-8', $charset);
$this->assertEquals('Refactoring · GitHub', $title);
}
/**
* Test the download callback with valid values and no charset
*/
public function testCurlDownloadCallbackOkNoCharset()
{
$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',
'<title>ignored</title>',
];
foreach ($data as $key => $line) {
$ignore = null;
$this->assertEquals(strlen($line), $callback($ignore, $line));
}
$this->assertEmpty($charset);
$this->assertEquals('Refactoring · GitHub', $title);
}
/**
* Test the download callback with valid values and no charset
*/
public function testCurlDownloadCallbackOkHtmlCharset()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset');
$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',
'<title>ignored</title>',
];
foreach ($data as $key => $line) {
$ignore = null;
$expected = $key !== 'end' ? strlen($line) : false;
$this->assertEquals($expected, $callback($ignore, $line));
if ($expected === false) {
break;
}
}
$this->assertEquals('utf-8', $charset);
$this->assertEquals('Refactoring · GitHub', $title);
}
/**
* Test the download callback with valid values and no title
*/
public function testCurlDownloadCallbackOkNoTitle()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ok');
$data = [
'HTTP/1.1 200 OK',
'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea',
'ignored',
];
foreach ($data as $key => $line) {
$ignore = null;
$this->assertEquals(strlen($line), $callback($ignore, $line));
}
$this->assertEquals('utf-8', $charset);
$this->assertEmpty($title);
}
/**
* Test the download callback with an invalid content type.
*/
public function testCurlDownloadCallbackInvalidContentType()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ct_ko');
$ignore = null;
$this->assertFalse($callback($ignore, ''));
$this->assertEmpty($charset);
$this->assertEmpty($title);
}
/**
* Test the download callback with an invalid response code.
*/
public function testCurlDownloadCallbackInvalidResponseCode()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_rc_ko');
$ignore = null;
$this->assertFalse($callback($ignore, ''));
$this->assertEmpty($charset);
$this->assertEmpty($title);
}
/**
* Test the download callback with an invalid content type and response code.
*/
public function testCurlDownloadCallbackInvalidContentTypeAndResponseCode()
{
$callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_rs_ct_ko');
$ignore = null;
$this->assertFalse($callback($ignore, ''));
$this->assertEmpty($charset);
$this->assertEmpty($title);
}
/**
* Test count_private.
*/
@ -207,3 +318,96 @@ private function getHashtagLink($hashtag, $index = '')
return str_replace('$1', $hashtag, $hashtagLink);
}
}
// old style mock: PHPUnit doesn't allow function mock
/**
* Returns code 200 or html content type.
*
* @param resource $ch cURL resource
* @param int $type cURL info type
*
* @return int|string 200 or 'text/html'
*/
function ut_curl_getinfo_ok($ch, $type)
{
switch ($type) {
case CURLINFO_RESPONSE_CODE:
return 200;
case CURLINFO_CONTENT_TYPE:
return 'text/html; charset=utf-8';
}
}
/**
* Returns code 200 or html content type without charset.
*
* @param resource $ch cURL resource
* @param int $type cURL info type
*
* @return int|string 200 or 'text/html'
*/
function ut_curl_getinfo_no_charset($ch, $type)
{
switch ($type) {
case CURLINFO_RESPONSE_CODE:
return 200;
case CURLINFO_CONTENT_TYPE:
return 'text/html';
}
}
/**
* Invalid response code.
*
* @param resource $ch cURL resource
* @param int $type cURL info type
*
* @return int|string 404 or 'text/html'
*/
function ut_curl_getinfo_rc_ko($ch, $type)
{
switch ($type) {
case CURLINFO_RESPONSE_CODE:
return 404;
case CURLINFO_CONTENT_TYPE:
return 'text/html; charset=utf-8';
}
}
/**
* Invalid content type.
*
* @param resource $ch cURL resource
* @param int $type cURL info type
*
* @return int|string 200 or 'text/plain'
*/
function ut_curl_getinfo_ct_ko($ch, $type)
{
switch ($type) {
case CURLINFO_RESPONSE_CODE:
return 200;
case CURLINFO_CONTENT_TYPE:
return 'text/plain';
}
}
/**
* Invalid response code and content type.
*
* @param resource $ch cURL resource
* @param int $type cURL info type
*
* @return int|string 404 or 'text/plain'
*/
function ut_curl_getinfo_rs_ct_ko($ch, $type)
{
switch ($type) {
case CURLINFO_RESPONSE_CODE:
return 404;
case CURLINFO_CONTENT_TYPE:
return 'text/plain';
}
}

View file

@ -36,6 +36,20 @@ public function testReadNonExistent()
$this->assertEquals(array(), $this->configIO->read('nope'));
}
/**
* Read an empty existent config file -> array with blank default values.
*/
public function testReadEmpty()
{
$dataFile = 'tests/utils/config/emptyConfigPhp.php';
$conf = $this->configIO->read($dataFile);
$this->assertEmpty($conf['login']);
$this->assertEmpty($conf['title']);
$this->assertEmpty($conf['titleLink']);
$this->assertEmpty($conf['config']);
$this->assertEmpty($conf['plugins']);
}
/**
* Write a new config file.
*/

View file

@ -0,0 +1 @@
<?php

View file

@ -1,7 +1,7 @@
<div class="shaarli-menu pure-g" id="shaarli-menu">
<div class="pure-u-lg-0 pure-u-1">
<div class="pure-menu">
<a href="{$titleLink}" class="pure-menu-link">
<a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-mobile">
<img src="img/icon.png" width="16" height="16" class="head-logo" alt="logo" />
{$shaarlititle}
</a>
@ -12,32 +12,32 @@
<div class="pure-menu menu-transform pure-menu-horizontal pure-g">
<ul class="pure-menu-list pure-u-lg-5-6 pure-u-1">
<li class="pure-menu-item pure-u-0 pure-u-lg-visible">
<a href="{$titleLink}" class="pure-menu-link">
<a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-desktop">
<img src="img/icon.png" width="16" height="16" class="head-logo" alt="logo" />
{$shaarlititle}
</a>
</li>
{if="isLoggedIn() || $openshaarli"}
<li class="pure-menu-item">
<a href="?do=addlink" class="pure-menu-link">
<a href="?do=addlink" class="pure-menu-link" id="shaarli-menu-shaare">
<i class="fa fa-plus" ></i> {'Shaare'|t}
</a>
</li>
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-tools">
<a href="?do=tools" class="pure-menu-link">{'Tools'|t}</a>
</li>
{/if}
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-tags">
<a href="?do=tagcloud" class="pure-menu-link">{'Tag cloud'|t}</a>
</li>
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-picwall">
<a href="?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a>
</li>
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-daily">
<a href="?do=daily" class="pure-menu-link">{'Daily'|t}</a>
</li>
{loop="$plugins_header.buttons_toolbar"}
<li class="pure-menu-item">
<li class="pure-menu-item shaarli-menu-plugin">
<a
{$value.attr.class=isset($value.class) ? $value.attr.class . ' pure-menu-link' : 'pure-menu-link'}
{loop="$value.attr"}
@ -47,35 +47,35 @@
</a>
</li>
{/loop}
<li class="pure-menu-item pure-u-lg-0">
<li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-rss">
<a href="?do={$feed_type}{$searchcrits}" class="pure-menu-link">{'RSS Feed'|t}</a>
</li>
{if="isLoggedIn()"}
<li class="pure-menu-item pure-u-lg-0">
<li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-logout">
<a href="?do=logout" class="pure-menu-link">{'Logout'|t}</a>
</li>
{else}
<li class="pure-menu-item pure-u-lg-0">
<li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-login">
<a href="?do=login" class="pure-menu-link">{'Login'|t}</a>
</li>
{/if}
</ul>
<div class="header-buttons pure-u-lg-1-6 pure-u-0 pure-u-lg-visible">
<ul class="pure-menu-list">
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-desktop-search">
<a href="#" class="pure-menu-link subheader-opener"
data-open-id="search"
id="search-button" title="{'Search'|t}">
<i class="fa fa-search"></i>
</a>
</li>
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-desktop-rss">
<a href="?do={$feed_type}{$searchcrits}" class="pure-menu-link" title="{'RSS Feed'|t}">
<i class="fa fa-rss"></i>
</a>
</li>
{if="!isLoggedIn()"}
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-desktop-login">
<a href="?do=login" class="pure-menu-link"
data-open-id="header-login-form"
id="login-button" title="{'Login'|t}">
@ -83,7 +83,7 @@
</a>
</li>
{else}
<li class="pure-menu-item">
<li class="pure-menu-item" id="shaarli-menu-desktop-logout">
<a href="?do=logout" class="pure-menu-link" title="{'Logout'|t}">
<i class="fa fa-sign-out"></i>
</a>
@ -156,7 +156,7 @@
{/if}
{if="!empty($plugin_errors) && isLoggedIn()"}
<div class="pure-g new-version-message pure-alert pure-alert-error pure-alert-closable">
<div class="pure-g new-version-message pure-alert pure-alert-error pure-alert-closable" id="shaarli-errors-alert">
<div class="pure-u-2-24"></div>
<div class="pure-u-20-24">
{loop="plugin_errors"}

View file

@ -137,9 +137,9 @@ <h2 class="window-title">{'Plugin configuration'|t}</h2>
{if="count($enabledPlugins)==0"}
<p class="center">{'No plugin enabled.'|t}</p>
{else}
{$counter=0}
{$nbParameters=0}
{loop="$enabledPlugins"}
{$counter=$counter+count($value.parameters)}
{$nbParameters=$nbParameters+count($value.parameters)}
{if="count($value.parameters) > 0"}
<div class="plugin_parameters">
<h3 class="window-subtitle">{function="str_replace('_', ' ', $key)"}</h3>
@ -161,7 +161,7 @@ <h3 class="window-subtitle">{function="str_replace('_', ' ', $key)"}</h3>
</div>
{/if}
{/loop}
{if="$counter===0"}
{if="$nbParameters===0"}
<p class="center">{'No parameter available.'|t}</p>
{else}
<div class="center">