diff --git a/application/PageBuilder.php b/application/PageBuilder.php index a448387..b1abe0d 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php @@ -1,6 +1,7 @@ tpl = false; $this->conf = $conf; + $this->session = $session; $this->linkDB = $linkDB; $this->token = $token; $this->isLoggedIn = $isLoggedIn; @@ -105,6 +119,19 @@ class PageBuilder if ($this->linkDB !== null) { $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); } + + $this->tpl->assign( + 'thumbnails_enabled', + $this->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE + ); + $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width')); + $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height')); + + if (! empty($_SESSION['warnings'])) { + $this->tpl->assign('global_warnings', $_SESSION['warnings']); + unset($_SESSION['warnings']); + } + // To be removed with a proper theme configuration. $this->tpl->assign('conf', $this->conf); } diff --git a/application/Router.php b/application/Router.php index 4df0387..bf86b88 100644 --- a/application/Router.php +++ b/application/Router.php @@ -7,6 +7,8 @@ */ class Router { + public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update'; + public static $PAGE_LOGIN = 'login'; public static $PAGE_PICWALL = 'picwall'; @@ -47,6 +49,8 @@ class Router public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin'; + public static $PAGE_THUMBS_UPDATE = 'thumbs_update'; + public static $GET_TOKEN = 'token'; /** @@ -101,6 +105,14 @@ class Router return self::$PAGE_FEED_RSS; } + if (startsWith($query, 'do='. self::$PAGE_THUMBS_UPDATE)) { + return self::$PAGE_THUMBS_UPDATE; + } + + if (startsWith($query, 'do='. self::$AJAX_THUMB_UPDATE)) { + return self::$AJAX_THUMB_UPDATE; + } + // At this point, only loggedin pages. if (!$loggedIn) { return self::$PAGE_LINKLIST; diff --git a/application/Thumbnailer.php b/application/Thumbnailer.php new file mode 100644 index 0000000..7d0d9c3 --- /dev/null +++ b/application/Thumbnailer.php @@ -0,0 +1,127 @@ +conf = $conf; + + if (! $this->checkRequirements()) { + $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.')); + } + + $this->wt = new WebThumbnailer(); + WTConfigManager::addFile('inc/web-thumbnailer.json'); + $this->wt->maxWidth($this->conf->get('thumbnails.width')) + ->maxHeight($this->conf->get('thumbnails.height')) + ->crop(true) + ->debug($this->conf->get('dev.debug', false)); + } + + /** + * Retrieve a thumbnail for given URL + * + * @param string $url where to look for a thumbnail. + * + * @return bool|string The thumbnail relative cache file path, or false if none has been found. + */ + public function get($url) + { + if ($this->conf->get('thumbnails.mode') === self::MODE_COMMON + && ! $this->isCommonMediaOrImage($url) + ) { + return false; + } + + try { + return $this->wt->thumbnail($url); + } catch (WebThumbnailerException $e) { + // Exceptions are only thrown in debug mode. + error_log(get_class($e) . ': ' . $e->getMessage()); + } + return false; + } + + /** + * We check weather the given URL is from a common media domain, + * or if the file extension is an image. + * + * @param string $url to check + * + * @return bool true if it's an image or from a common media domain, false otherwise. + */ + public function isCommonMediaOrImage($url) + { + foreach (self::COMMON_MEDIA_DOMAINS as $domain) { + if (strpos($url, $domain) !== false) { + return true; + } + } + + if (endsWith($url, '.jpg') || endsWith($url, '.png') || endsWith($url, '.jpeg')) { + return true; + } + + return false; + } + + /** + * Make sure that requirements are match to use thumbnails: + * - php-gd is loaded + */ + protected function checkRequirements() + { + return extension_loaded('gd'); + } +} diff --git a/application/Updater.php b/application/Updater.php index dece2c0..c2aa156 100644 --- a/application/Updater.php +++ b/application/Updater.php @@ -2,6 +2,7 @@ use Shaarli\Config\ConfigJson; use Shaarli\Config\ConfigPhp; use Shaarli\Config\ConfigManager; +use Shaarli\Thumbnailer; /** * Class Updater. @@ -30,6 +31,11 @@ class Updater */ protected $isLoggedIn; + /** + * @var array $_SESSION + */ + protected $session; + /** * @var ReflectionMethod[] List of current class methods. */ @@ -42,13 +48,17 @@ class Updater * @param LinkDB $linkDB LinkDB instance. * @param ConfigManager $conf Configuration Manager instance. * @param boolean $isLoggedIn True if the user is logged in. + * @param array $session $_SESSION (by reference) + * + * @throws ReflectionException */ - public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) + public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = []) { $this->doneUpdates = $doneUpdates; $this->linkDB = $linkDB; $this->conf = $conf; $this->isLoggedIn = $isLoggedIn; + $this->session = &$session; // Retrieve all update methods. $class = new ReflectionClass($this); @@ -480,6 +490,30 @@ class Updater } $this->conf->write($this->isLoggedIn); + return true; + } + + /** + * * Move thumbnails management to WebThumbnailer, coming with new settings. + */ + public function updateMethodWebThumbnailer() + { + if ($this->conf->exists('thumbnails.mode')) { + return true; + } + + $thumbnailsEnabled = $this->conf->get('thumbnail.enable_thumbnails', true); + $this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE); + $this->conf->set('thumbnails.width', 125); + $this->conf->set('thumbnails.height', 90); + $this->conf->remove('thumbnail'); + $this->conf->write(true); + + if ($thumbnailsEnabled) { + $this->session['warnings'][] = t( + 'You have enabled or changed thumbnails mode. Please synchronize them.' + ); + } return true; } diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 82f4a36..32aaea4 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -147,6 +147,33 @@ class ConfigManager } } + /** + * Remove a config element from the config file. + * + * @param string $setting Asked setting, keys separated with dots. + * @param bool $write Write the new setting in the config file, default false. + * @param bool $isLoggedIn User login state, default false. + * + * @throws \Exception Invalid + */ + public function remove($setting, $write = false, $isLoggedIn = false) + { + if (empty($setting) || ! is_string($setting)) { + throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting)); + } + + // During the ConfigIO transition, map legacy settings to the new ones. + if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { + $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; + } + + $settings = explode('.', $setting); + self::removeConfig($settings, $this->loadedConfig); + if ($write) { + $this->write($isLoggedIn); + } + } + /** * Check if a settings exists. * @@ -272,7 +299,7 @@ class ConfigManager * * @param array $settings Ordered array which contains keys to find. * @param mixed $value - * @param array $conf Loaded settings, then sub-array. + * @param array $conf Loaded settings, then sub-array. * * @return mixed Found setting or NOT_FOUND flag. */ @@ -289,6 +316,27 @@ class ConfigManager $conf[$setting] = $value; } + /** + * Recursive function which find asked setting in the loaded config and deletes it. + * + * @param array $settings Ordered array which contains keys to find. + * @param array $conf Loaded settings, then sub-array. + * + * @return mixed Found setting or NOT_FOUND flag. + */ + protected static function removeConfig($settings, &$conf) + { + if (!is_array($settings) || count($settings) == 0) { + return self::$NOT_FOUND; + } + + $setting = array_shift($settings); + if (count($settings) > 0) { + return self::removeConfig($settings, $conf[$setting]); + } + unset($conf[$setting]); + } + /** * Set a bunch of default values allowing Shaarli to start without a config file. */ @@ -333,12 +381,12 @@ class ConfigManager // default state of the 'remember me' checkbox of the login form $this->setEmpty('privacy.remember_user_default', true); - $this->setEmpty('thumbnail.enable_thumbnails', true); - $this->setEmpty('thumbnail.enable_localcache', true); - $this->setEmpty('redirector.url', ''); $this->setEmpty('redirector.encode_url', true); + $this->setEmpty('thumbnails.width', '125'); + $this->setEmpty('thumbnails.height', '90'); + $this->setEmpty('translation.language', 'auto'); $this->setEmpty('translation.mode', 'php'); $this->setEmpty('translation.extensions', []); diff --git a/assets/common/js/picwall.js b/assets/common/js/picwall.js deleted file mode 100644 index 87a93fc..0000000 --- a/assets/common/js/picwall.js +++ /dev/null @@ -1,10 +0,0 @@ -import Blazy from 'blazy'; - -(() => { - const picwall = document.getElementById('picwall_container'); - if (picwall != null) { - // Suppress ESLint error because that's how bLazy works - /* eslint-disable no-new */ - new Blazy(); - } -})(); diff --git a/assets/common/js/thumbnails-update.js b/assets/common/js/thumbnails-update.js new file mode 100644 index 0000000..b66ca3a --- /dev/null +++ b/assets/common/js/thumbnails-update.js @@ -0,0 +1,51 @@ +/** + * Script used in the thumbnails update page. + * + * It retrieves the list of link IDs to update, and execute AJAX requests + * to update their thumbnails, while updating the progress bar. + */ + +/** + * Update the thumbnail of the link with the current i index in ids. + * It contains a recursive call to retrieve the thumb of the next link when it succeed. + * It also update the progress bar and other visual feedback elements. + * + * @param {array} ids List of LinkID to update + * @param {int} i Current index in ids + * @param {object} elements List of DOM element to avoid retrieving them at each iteration + */ +function updateThumb(ids, i, elements) { + const xhr = new XMLHttpRequest(); + xhr.open('POST', '?do=ajax_thumb_update'); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status !== 200) { + alert(`An error occurred. Return code: ${xhr.status}`); + } else { + const { response } = xhr; + i += 1; + elements.progressBar.style.width = `${(i * 100) / ids.length}%`; + elements.current.innerHTML = i; + elements.title.innerHTML = response.title; + if (response.thumbnail !== false) { + elements.thumbnail.innerHTML = ``; + } + if (i < ids.length) { + updateThumb(ids, i, elements); + } + } + }; + xhr.send(`id=${ids[i]}`); +} + +(() => { + const ids = document.getElementsByName('ids')[0].value.split(','); + const elements = { + progressBar: document.querySelector('.progressbar > div'), + current: document.querySelector('.progress-current'), + thumbnail: document.querySelector('.thumbnail-placeholder'), + title: document.querySelector('.thumbnail-link-title'), + }; + updateThumb(ids, 0, elements); +})(); diff --git a/assets/common/js/thumbnails.js b/assets/common/js/thumbnails.js new file mode 100644 index 0000000..c28322b --- /dev/null +++ b/assets/common/js/thumbnails.js @@ -0,0 +1,7 @@ +import Blazy from 'blazy'; + +(() => { + // Suppress ESLint error because that's how bLazy works + /* eslint-disable no-new */ + new Blazy(); +})(); diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss index 09d5efb..6b286f1 100644 --- a/assets/default/scss/shaarli.scss +++ b/assets/default/scss/shaarli.scss @@ -146,6 +146,17 @@ body, background-color: $main-green; } +.pure-alert-warning { + a { + color: $warning-text; + font-weight: bold; + } +} + +.page-single-alert { + margin-top: 100px; +} + .anchor { &:target { padding-top: 40px; @@ -625,23 +636,22 @@ body, } .linklist-item { + position: relative; margin: 0 0 10px; box-shadow: 1px 1px 3px $light-grey; background: $almost-white; &.private { - .linklist-item-title { - &::before { - @extend %private-border; - margin-top: 3px; - } - } - - .linklist-item-description { - &::before { - @extend %private-border; - height: 100%; - } + &::before { + display: block; + position: absolute; + top: 0; + left: 0; + z-index: 1; + background: $orange; + width: 2px; + height: 100%; + content: ''; } } } @@ -1543,3 +1553,40 @@ form { .pure-button-shaarli { background-color: $main-green; } + +.progressbar { + border-radius: 6px; + background-color: $main-green; + padding: 1px; + + > div { + border-radius: 10px; + background: repeating-linear-gradient( + -45deg, + $almost-white, + $almost-white 6px, + $background-color 6px, + $background-color 12px + ); + width: 0%; + height: 10px; + } +} + +.thumbnails-page-container { + .progress-counter { + padding: 10px 0 20px; + } + + .thumbnail-placeholder { + margin: 10px auto; + background-color: $light-grey; + } + + .thumbnail-link-title { + padding-bottom: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} diff --git a/assets/vintage/css/shaarli.css b/assets/vintage/css/shaarli.css index c919339..87c440c 100644 --- a/assets/vintage/css/shaarli.css +++ b/assets/vintage/css/shaarli.css @@ -701,8 +701,8 @@ a.bigbutton, #pageheader a.bigbutton { position: relative; display: table-cell; vertical-align: middle; - width: 90px; - height: 90px; + width: 120px; + height: 120px; overflow: hidden; text-align: center; float: left; @@ -739,9 +739,9 @@ a.bigbutton, #pageheader a.bigbutton { position: absolute; top: 0; left: 0; - width: 90px; + width: 120px; font-weight: bold; - font-size: 8pt; + font-size: 9pt; color: #fff; text-align: left; background-color: transparent; @@ -1210,3 +1210,43 @@ ul.errors { width: 13px; height: 13px; } + +.thumbnails-update-container { + padding: 20px 0; + width: 50%; + margin: auto; +} + +.thumbnails-update-container .thumbnail-placeholder { + background: grey; + margin: auto; +} + +.thumbnails-update-container .thumbnail-link-title { + width: 75%; + margin: auto; + + padding-bottom: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.progressbar { + border-radius: 6px; + background-color: #111; + padding: 1px; +} + +.progressbar > div { + border-radius: 10px; + background: repeating-linear-gradient( + -45deg, + #f5f5f5, + #f5f5f5 6px, + #d0d0d0 6px, + #d0d0d0 12px + ); + width: 0%; + height: 10px; +} diff --git a/composer.json b/composer.json index 0d4c623..99ef0b5 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "shaarli/netscape-bookmark-parser": "^2.0", "erusev/parsedown": "^1.6", "slim/slim": "^3.0", + "arthurhoaro/web-thumbnailer": "^1.1", "pubsubhubbub/publisher": "dev-master", "gettext/gettext": "^4.4" }, diff --git a/composer.lock b/composer.lock index ee762c0..08e915c 100644 --- a/composer.lock +++ b/composer.lock @@ -1,11 +1,59 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "308a35eab91602fbb449f2c669c445ed", + "content-hash": "da7a0c081b61d949154c5d2e5370cbab", "packages": [ + { + "name": "arthurhoaro/web-thumbnailer", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/ArthurHoaro/web-thumbnailer.git", + "reference": "a5a52f69e8e8f3c71fab9649e2a927e2d3f418f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ArthurHoaro/web-thumbnailer/zipball/a5a52f69e8e8f3c71fab9649e2a927e2d3f418f1", + "reference": "a5a52f69e8e8f3c71fab9649e2a927e2d3f418f1", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "phpunit/php-text-template": "^1.2" + }, + "conflict": { + "phpunit/php-timer": ">=2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "5.2.*", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "WebThumbnailer\\": [ + "src/", + "tests/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arthur Hoaro", + "homepage": "http://hoa.ro" + } + ], + "description": "PHP library which will retrieve a thumbnail for any given URL", + "time": "2018-07-17T10:21:14+00:00" + }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -85,16 +133,16 @@ }, { "name": "gettext/gettext", - "version": "v4.4.4", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/oscarotero/Gettext.git", - "reference": "ab5e863de2f60806d02e6e6081e21efd45249168" + "reference": "cae84aff39a87e07bd6e5cddb5adb720a0ffa357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/ab5e863de2f60806d02e6e6081e21efd45249168", - "reference": "ab5e863de2f60806d02e6e6081e21efd45249168", + "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/cae84aff39a87e07bd6e5cddb5adb720a0ffa357", + "reference": "cae84aff39a87e07bd6e5cddb5adb720a0ffa357", "shasum": "" }, "require": { @@ -103,7 +151,7 @@ }, "require-dev": { "illuminate/view": "*", - "phpunit/phpunit": "^4.8|^5.7", + "phpunit/phpunit": "^4.8|^5.7|^6.5", "squizlabs/php_codesniffer": "^3.0", "symfony/yaml": "~2", "twig/extensions": "*", @@ -143,20 +191,20 @@ "po", "translation" ], - "time": "2018-02-21T18:49:59+00:00" + "time": "2018-06-26T16:51:09+00:00" }, { "name": "gettext/languages", - "version": "2.3.0", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/mlocati/cldr-to-gettext-plural-rules.git", - "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7" + "reference": "1b74377bd0c4cd87e8d72b948f5d8867e23505a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/49c39e51569963cc917a924b489e7025bfb9d8c7", - "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7", + "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/1b74377bd0c4cd87e8d72b948f5d8867e23505a5", + "reference": "1b74377bd0c4cd87e8d72b948f5d8867e23505a5", "shasum": "" }, "require": { @@ -204,7 +252,7 @@ "translations", "unicode" ], - "time": "2017-03-23T17:02:28+00:00" + "time": "2018-06-21T15:58:36+00:00" }, { "name": "katzgrau/klogger", @@ -302,6 +350,47 @@ ], "time": "2018-02-13T20:26:39+00:00" }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, { "name": "pimple/pimple", "version": "v3.2.3", @@ -504,12 +593,12 @@ "source": { "type": "git", "url": "https://github.com/pubsubhubbub/php-publisher.git", - "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f" + "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/0d224daebd504ab61c22fee4db58f8d1fc18945f", - "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f", + "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/5008fc529b057251b48f4d17a10fdb20047ea8f5", + "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5", "shasum": "" }, "require": { @@ -539,7 +628,7 @@ "publishers", "pubsubhubbub" ], - "time": "2017-10-08T10:59:41+00:00" + "time": "2018-05-22T11:56:26+00:00" }, { "name": "shaarli/netscape-bookmark-parser", @@ -598,16 +687,16 @@ }, { "name": "slim/slim", - "version": "3.9.2", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118" + "reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118", - "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/d8aabeacc3688b25e2f2dd2db91df91ec6fdd748", + "reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748", "shasum": "" }, "require": { @@ -665,7 +754,7 @@ "micro", "router" ], - "time": "2017-11-26T19:13:09+00:00" + "time": "2018-04-19T19:29:08+00:00" } ], "packages-dev": [ @@ -1022,23 +1111,23 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "1.7.6", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { @@ -1081,7 +1170,7 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-04-18T13:57:24+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1193,47 +1282,6 @@ ], "time": "2017-11-27T13:52:08+00:00" }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, { "name": "phpunit/php-timer", "version": "1.0.9", @@ -2207,21 +2255,22 @@ }, { "name": "symfony/config", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "05e10567b529476a006b00746c5f538f1636810e" + "reference": "1fffdeb349ff36a25184e5564c25289b1dbfc402" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/05e10567b529476a006b00746c5f538f1636810e", - "reference": "05e10567b529476a006b00746c5f538f1636810e", + "url": "https://api.github.com/repos/symfony/config/zipball/1fffdeb349ff36a25184e5564c25289b1dbfc402", + "reference": "1fffdeb349ff36a25184e5564c25289b1dbfc402", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0|~4.0" + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/dependency-injection": "<3.3", @@ -2266,20 +2315,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-02-14T10:03:57+00:00" + "time": "2018-06-19T14:02:58+00:00" }, { "name": "symfony/console", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" + "reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", + "url": "https://api.github.com/repos/symfony/console/zipball/1b97071a26d028c9bd4588264e101e14f6e7cd00", + "reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00", "shasum": "" }, "require": { @@ -2300,7 +2349,7 @@ "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log": "For using the console logger", + "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -2335,20 +2384,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-02-26T15:46:28+00:00" + "time": "2018-05-23T05:02:55+00:00" }, { "name": "symfony/debug", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" + "reference": "47e6788c5b151cf0cfdf3329116bf33800632d75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", + "url": "https://api.github.com/repos/symfony/debug/zipball/47e6788c5b151cf0cfdf3329116bf33800632d75", + "reference": "47e6788c5b151cf0cfdf3329116bf33800632d75", "shasum": "" }, "require": { @@ -2391,20 +2440,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-02-28T21:49:22+00:00" + "time": "2018-06-25T11:10:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07" + "reference": "a0be80e3f8c11aca506e250c00bb100c04c35d10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/12e901abc1cb0d637a0e5abe9923471361d96b07", - "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a0be80e3f8c11aca506e250c00bb100c04c35d10", + "reference": "a0be80e3f8c11aca506e250c00bb100c04c35d10", "shasum": "" }, "require": { @@ -2462,24 +2511,25 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-03-04T03:54:53+00:00" + "time": "2018-06-25T08:36:56+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541" + "reference": "8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/253a4490b528597aa14d2bf5aeded6f5e5e4a541", - "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed", + "reference": "8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { @@ -2511,20 +2561,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:48:49+00:00" + "time": "2018-06-21T11:10:19+00:00" }, { "name": "symfony/finder", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625" + "reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625", + "url": "https://api.github.com/repos/symfony/finder/zipball/3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394", + "reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394", "shasum": "" }, "require": { @@ -2560,20 +2610,75 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-03-05T18:28:11+00:00" + "time": "2018-06-19T20:52:10+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "name": "symfony/polyfill-ctype", + "version": "v1.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-04-30T19:57:29+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "3296adf6a6454a050679cde90f95350ad604b171" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -2585,7 +2690,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -2619,24 +2724,25 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.6", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/console": "<3.4" @@ -2677,7 +2783,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-02-16T09:50:28+00:00" + "time": "2018-05-03T23:18:14+00:00" }, { "name": "theseer/fdomdocument", diff --git a/doc/md/Link-structure.md b/doc/md/Link-structure.md new file mode 100644 index 0000000..0a2d0f8 --- /dev/null +++ b/doc/md/Link-structure.md @@ -0,0 +1,18 @@ +## Link structure + +Every link available through the `LinkDB` object is represented as an array +containing the following fields: + + * `id` (integer): Unique identifier. + * `title` (string): Title of the link. + * `url` (string): URL of the link. Used for displayable links (without redirector, url encoding, etc.). + Can be absolute or relative for Notes. + * `real_url` (string): Real destination URL, can be redirected, encoded, etc. + * `shorturl` (string): Permalink small hash. + * `description` (string): Link text description. + * `private` (boolean): whether the link is private or not. + * `tags` (string): all link tags separated by a single space + * `thumbnail` (string|boolean): relative path of the thumbnail cache file, or false if there isn't any. + * `created` (DateTime): link creation date time. + * `updated` (DateTime): last modification date time. + \ No newline at end of file diff --git a/doc/md/Server-configuration.md b/doc/md/Server-configuration.md index ca82b2e..e281dc8 100644 --- a/doc/md/Server-configuration.md +++ b/doc/md/Server-configuration.md @@ -29,7 +29,7 @@ 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, some hosting providers | multibyte (Unicode) string support -[`php-gd`](http://php.net/manual/en/book.image.php) | optional | thumbnail resizing +[`php-gd`](http://php.net/manual/en/book.image.php) | optional | required to use thumbnails [`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 [`php-gettext`](http://php.net/manual/en/book.gettext.php) | optional | Use the translation system in gettext mode (faster) diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po index 2ebeccb..155eb52 100644 --- a/inc/languages/fr/LC_MESSAGES/shaarli.po +++ b/inc/languages/fr/LC_MESSAGES/shaarli.po @@ -1,15 +1,15 @@ msgid "" msgstr "" "Project-Id-Version: Shaarli\n" -"POT-Creation-Date: 2018-01-24 18:43+0100\n" -"PO-Revision-Date: 2018-03-06 18:44+0100\n" +"POT-Creation-Date: 2018-07-17 13:04+0200\n" +"PO-Revision-Date: 2018-07-17 13:07+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.6\n" +"X-Generator: Poedit 2.0.9\n" "X-Poedit-Basepath: ../../../..\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Poedit-SourceCharset: UTF-8\n" @@ -56,7 +56,7 @@ msgstr "Liens directs" #: application/FeedBuilder.php:153 #: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:178 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:177 msgid "Permalink" msgstr "Permalien" @@ -68,18 +68,22 @@ msgstr "Le fichier d'historique n'est pas accessible en lecture ou en écriture" msgid "Could not parse history file" msgstr "Format incorrect pour le fichier d'historique" -#: application/Languages.php:161 +#: application/Languages.php:177 msgid "Automatic" msgstr "Automatique" -#: application/Languages.php:162 +#: application/Languages.php:178 msgid "English" msgstr "Anglais" -#: application/Languages.php:163 +#: application/Languages.php:179 msgid "French" msgstr "Français" +#: application/Languages.php:180 +msgid "German" +msgstr "Allemand" + #: application/LinkDB.php:136 msgid "You are not authorized to add a link." msgstr "Vous n'êtes pas autorisé à ajouter un lien." @@ -163,11 +167,11 @@ msgstr "" "a été importé avec succès en %d secondes : %d liens importés, %d liens " "écrasés, %d liens ignorés." -#: application/PageBuilder.php:168 +#: application/PageBuilder.php:200 msgid "The page you are trying to reach does not exist or has been deleted." msgstr "La page que vous essayez de consulter n'existe pas ou a été supprimée." -#: application/PageBuilder.php:170 +#: application/PageBuilder.php:202 msgid "404 Not Found" msgstr "404 Introuvable" @@ -176,21 +180,37 @@ msgstr "404 Introuvable" msgid "Plugin \"%s\" files not found." msgstr "Les fichiers de l'extension \"%s\" sont introuvables." -#: application/Updater.php:76 +#: application/Thumbnailer.php:61 +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." + +#: 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:506 +#: application/Updater.php:514 index.php:1023 +msgid "" +"You have enabled or changed thumbnails mode. Please synchronize them." +msgstr "" +"Vous avez activé ou changé le mode de miniatures. Merci de les synchroniser." + +#: application/Updater.php:566 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:546 +#: application/Updater.php:606 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:551 +#: application/Updater.php:611 msgid "Unable to write updates in " msgstr "Impossible d'écrire les mises à jour dans " @@ -230,6 +250,7 @@ msgstr "" "Shaarli a les droits d'écriture dans le dossier dans lequel il est installé." #: application/config/ConfigManager.php:135 +#: application/config/ConfigManager.php:162 msgid "Invalid setting key parameter. String expected, got: " msgstr "Clé de paramétrage invalide. Chaîne de caractères obtenue, attendu : " @@ -251,135 +272,133 @@ msgstr "Vous n'êtes pas autorisé à modifier la configuration." msgid "Error accessing" msgstr "Une erreur s'est produite en accédant à" -#: index.php:142 +#: index.php:143 msgid "Shared links on " msgstr "Liens partagés sur " -#: index.php:164 +#: index.php:165 msgid "Insufficient permissions:" msgstr "Permissions insuffisantes :" -#: index.php:303 +#: index.php:201 msgid "I said: NO. You are banned for the moment. Go away." msgstr "NON. Vous êtes banni pour le moment. Revenez plus tard." -#: index.php:368 +#: index.php:273 msgid "Wrong login/password." msgstr "Nom d'utilisateur ou mot de passe incorrects." -#: index.php:576 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:42 +#: index.php:483 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:46 msgid "Daily" msgstr "Quotidien" -#: index.php:681 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 +#: index.php:589 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 #: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:71 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:95 +#: 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:722 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:39 +#: index.php:606 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:41 msgid "Picture wall" msgstr "Mur d'images" -#: index.php:770 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 +#: index.php:683 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36 #: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 msgid "Tag cloud" msgstr "Nuage de tags" -#: index.php:803 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 +#: index.php:716 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 msgid "Tag list" msgstr "Liste des tags" -#: index.php:1028 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 +#: index.php:941 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31 msgid "Tools" msgstr "Outils" -#: index.php:1037 +#: index.php:950 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:1042 index.php:1084 index.php:1162 index.php:1193 index.php:1293 +#: index.php:955 index.php:997 index.php:1085 index.php:1116 index.php:1221 msgid "Wrong token." msgstr "Jeton invalide." -#: index.php:1047 +#: index.php:960 msgid "The old password is not correct." msgstr "L'ancien mot de passe est incorrect." -#: index.php:1067 +#: index.php:980 msgid "Your password has been changed" msgstr "Votre mot de passe a été modifié" -#: index.php:1072 +#: index.php:985 #: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29 msgid "Change password" msgstr "Modification du mot de passe" -#: index.php:1121 +#: index.php:1043 msgid "Configuration was saved." msgstr "La configuration a été sauvegardé." -#: index.php:1145 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24 +#: index.php:1068 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24 msgid "Configure" msgstr "Configurer" -#: index.php:1156 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 +#: index.php:1079 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 msgid "Manage tags" msgstr "Gérer les tags" -#: index.php:1174 +#: index.php:1097 #, php-format msgid "The tag was removed from %d link." msgid_plural "The tag was removed from %d links." msgstr[0] "Le tag a été supprimé de %d lien." msgstr[1] "Le tag a été supprimé de %d liens." -#: index.php:1175 +#: index.php:1098 #, php-format msgid "The tag was renamed in %d link." msgid_plural "The tag was renamed in %d links." msgstr[0] "Le tag a été renommé dans %d lien." msgstr[1] "Le tag a été renommé dans %d liens." -#: index.php:1183 tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 +#: index.php:1106 tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 msgid "Shaare a new link" msgstr "Partager un nouveau lien" -#: index.php:1353 tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170 +#: index.php:1281 tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169 msgid "Edit" msgstr "Modifier" -#: index.php:1353 index.php:1418 -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 +#: index.php:1281 index.php:1351 #: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 #: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26 msgid "Shaare" msgstr "Shaare" -#: index.php:1387 +#: index.php:1320 msgid "Note: " msgstr "Note : " -#: index.php:1427 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65 +#: index.php:1360 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65 msgid "Export" msgstr "Exporter" -#: index.php:1489 tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83 +#: index.php:1422 tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83 msgid "Import" msgstr "Importer" -#: index.php:1499 +#: index.php:1432 #, php-format msgid "" "The file you are trying to upload is probably bigger than what this " @@ -389,16 +408,20 @@ msgstr "" "le serveur web peut accepter (%s). Merci de l'envoyer en parties plus " "légères." -#: index.php:1538 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 +#: index.php:1471 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 #: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22 msgid "Plugin administration" msgstr "Administration des extensions" -#: index.php:1703 +#: index.php:1523 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 +msgid "Thumbnails update" +msgstr "Mise à jour des miniatures" + +#: index.php:1695 msgid "Search: " msgstr "Recherche : " -#: index.php:1930 +#: index.php:1735 #, php-format msgid "" "
Sessions do not seem to work correctly on your server.
Make sure the " @@ -417,7 +440,7 @@ msgstr "" "cookies. Nous vous recommandons d'accéder à votre serveur depuis son adresse " "IP ou un Fully Qualified Domain Name.
" -#: index.php:1940 +#: index.php:1745 msgid "Click to try again." msgstr "Cliquer ici pour réessayer." @@ -467,19 +490,19 @@ msgstr "" msgid "Isso server URL (without 'http://')" msgstr "URL du serveur Isso (sans 'http://')" -#: plugins/markdown/markdown.php:158 +#: plugins/markdown/markdown.php:161 msgid "Description will be rendered with" msgstr "La description sera générée avec" -#: plugins/markdown/markdown.php:159 +#: plugins/markdown/markdown.php:162 msgid "Markdown syntax documentation" msgstr "Documentation sur la syntaxe Markdown" -#: plugins/markdown/markdown.php:160 +#: plugins/markdown/markdown.php:163 msgid "Markdown syntax" msgstr "la syntaxe Markdown" -#: plugins/markdown/markdown.php:339 +#: plugins/markdown/markdown.php:347 msgid "" "Render shaare description with Markdown syntax.
Warning:\n" @@ -577,11 +600,11 @@ msgstr "URL de l'API Wallabag" msgid "Wallabag API version (1 or 2)" msgstr "Version de l'API Wallabag (1 ou 2)" -#: tests/LanguagesTest.php:188 tests/LanguagesTest.php:201 +#: 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:81 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:81 +#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:85 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:85 msgid "Search" msgid_plural "Search" msgstr[0] "Rechercher" @@ -625,8 +648,8 @@ msgid "Rename" msgstr "Renommer" #: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:172 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:171 msgid "Delete" msgstr "Supprimer" @@ -736,8 +759,36 @@ msgstr "" msgid "API secret" msgstr "Clé d'API secrète" -#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:274 -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74 +#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:277 +msgid "Enable thumbnails" +msgstr "Activer les miniatures" + +#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:281 +msgid "You need to enable the extension php-gd to use thumbnails." +msgstr "" +"Vous devez activer l'extension php-gd 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 #: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199 msgid "Save" @@ -763,25 +814,27 @@ msgstr "Tous les liens d'un jour sur une page." msgid "Next day" msgstr "Jour suivant" -#: tpl/editlink.html +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 msgid "Edit Shaare" msgstr "Modifier le Shaare" + +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 msgid "New Shaare" msgstr "Nouveau Shaare" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23 msgid "Created:" msgstr "Création :" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 msgid "URL" msgstr "URL" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32 msgid "Title" msgstr "Titre" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38 #: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 #: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75 #: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99 @@ -789,17 +842,17 @@ msgstr "Titre" msgid "Description" msgstr "Description" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44 msgid "Tags" msgstr "Tags" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:59 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57 #: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:168 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:167 msgid "Private" msgstr "Privé" -#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74 +#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72 msgid "Apply Changes" msgstr "Appliquer" @@ -811,10 +864,6 @@ msgstr "Exporter les données" msgid "Selection" msgstr "Choisir" -#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 -msgid "All" -msgstr "Tous" - #: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 msgid "Public" msgstr "Publics" @@ -876,15 +925,15 @@ msgstr "" #: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33 #: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147 +#: 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:148 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:148 +#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:152 msgid "Password" msgstr "Mot de passe" @@ -901,28 +950,28 @@ msgid "Install" msgstr "Installer" #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79 msgid "shaare" msgid_plural "shaares" msgstr[0] "shaare" msgstr[1] "shaares" #: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18 -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:84 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83 msgid "private link" msgid_plural "private links" msgstr[0] "lien privé" msgstr[1] "liens privés" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:117 +#: 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:38 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:124 +#: 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 #: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 @@ -930,52 +979,52 @@ msgstr "Recherche texte" msgid "Filter by tag" msgstr "Filtrer par tag" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110 msgid "Nothing found." msgstr "Aucun résultat." -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:119 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118 #, php-format msgid "%s result" msgid_plural "%s results" msgstr[0] "%s résultat" msgstr[1] "%s résultats" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122 msgid "for" msgstr "pour" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129 msgid "tagged" msgstr "taggé" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:133 msgid "Remove tag" msgstr "Retirer le tag" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:143 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:142 msgid "with status" msgstr "avec le statut" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:153 msgid "without any tag" msgstr "sans tag" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:174 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173 #: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 #: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:42 msgid "Fold" msgstr "Replier" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:176 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:175 msgid "Edited: " msgstr "Modifié : " -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:180 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:179 msgid "permalink" msgstr "permalien" -#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182 +#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:181 msgid "Add tag" msgstr "Ajouter un tag" @@ -1021,8 +1070,8 @@ msgstr "" "réessayer plus tard." #: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:151 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151 +#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:155 msgid "Remember me" msgstr "Rester connecté" @@ -1053,35 +1102,52 @@ msgstr "Déplier tout" 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:61 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:61 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:86 +#: 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:66 -#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:66 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:102 +#: 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:169 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:169 +#: 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:176 -#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:176 +#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:180 +#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:180 msgid "Error" msgstr "Erreur" -#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 +#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 +msgid "Picture wall unavailable (thumbnails are disabled)." +msgstr "" +"Le mur d'images n'est pas disponible (les miniatures sont désactivées)." + +#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24 +#, fuzzy +#| msgid "" +#| "You don't have any cached thumbnail. Try to synchronize them." +msgid "" +"There is no cached thumbnail. Try to synchronize them." +msgstr "" +"Il n'y a aucune miniature en cache. Essayer de les synchroniser." + +#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 msgid "Picture Wall" msgstr "Mur d'images" -#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 +#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 msgid "pics" msgstr "images" @@ -1223,7 +1289,11 @@ msgstr "" msgid "Export database" msgstr "Exporter les données" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:55 +msgid "Synchronize all link thumbnails" +msgstr "Synchroniser toutes les miniatures" + +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81 msgid "" "Drag one of these button to your bookmarks toolbar or right-click it and " "\"Bookmark This Link\"" @@ -1231,13 +1301,13 @@ msgstr "" "Glisser un de ces bouttons dans votre barre de favoris ou cliquer droit " "dessus et « Ajouter aux favoris »" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72 +#: 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." -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76 -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:100 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110 msgid "" "Drag this link to your bookmarks toolbar or right-click it and Bookmark This " "Link" @@ -1245,31 +1315,31 @@ msgstr "" "Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « " "Ajouter aux favoris »" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87 msgid "then click ✚Shaare link button in any page you want to share" msgstr "puis cliquer sur ✚Shaare depuis un site que vous souhaitez partager" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:108 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118 msgid "The selected text is too long, it will be truncated." msgstr "Le texte sélectionné est trop long, il sera tronqué." -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106 msgid "Shaare link" msgstr "Shaare" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:101 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111 msgid "" "Then click ✚Add Note button anytime to start composing a private Note (text " "post) to your Shaarli" msgstr "" "Puis cliquer sur ✚Add Note pour commencer à rédiger une Note sur Shaarli" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127 msgid "Add Note" msgstr "Ajouter une Note" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139 msgid "" "You need to browse your Shaarli over HTTPS to use this " "functionality." @@ -1277,25 +1347,25 @@ msgstr "" "Vous devez utiliser Shaarli en HTTPS pour utiliser cette " "fonctionalité." -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144 msgid "Add to" msgstr "Ajouter à" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:145 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155 msgid "3rd party" msgstr "Applications tierces" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147 -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:153 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:157 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:163 msgid "Plugin" msgstr "Extension" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:148 -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:158 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:164 msgid "plugin" msgstr "extension" -#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:175 +#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:191 msgid "" "Drag this link to your bookmarks toolbar, or right-click it and choose " "Bookmark This Link" @@ -1303,6 +1373,26 @@ 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 "Warning: " +#~ msgstr "Attention : " + +#~ msgid "" +#~ "It's recommended to visit the picture wall after enabling this feature." +#~ msgstr "" +#~ "Il est recommandé de visiter le Mur d'images après avoir activé cette " +#~ "fonctionnalité." + +#~ msgid "" +#~ "If you have a large database, the first retrieval may take a few minutes." +#~ msgstr "" +#~ "Si vous avez beaucoup de liens, la première récupération peut prendre " +#~ "plusieurs minutes." + #, fuzzy #~| msgid "Change" #~ msgid "range" diff --git a/inc/web-thumbnailer.json b/inc/web-thumbnailer.json new file mode 100644 index 0000000..dcaa149 --- /dev/null +++ b/inc/web-thumbnailer.json @@ -0,0 +1,13 @@ +{ + "settings": { + "default": { + "download_mode": "DOWNLOAD", + "_comment": "infinite cache", + "cache_duration": -1, + "timeout": 10 + }, + "path": { + "cache": "cache/" + } + } +} diff --git a/index.php b/index.php index 5fc880e..1480bbc 100644 --- a/index.php +++ b/index.php @@ -75,11 +75,12 @@ require_once 'application/Utils.php'; require_once 'application/PluginManager.php'; require_once 'application/Router.php'; require_once 'application/Updater.php'; -use \Shaarli\Languages; -use \Shaarli\ThemeUtils; use \Shaarli\Config\ConfigManager; +use \Shaarli\Languages; use \Shaarli\Security\LoginManager; use \Shaarli\Security\SessionManager; +use \Shaarli\ThemeUtils; +use \Shaarli\Thumbnailer; // Ensure the PHP version is supported try { @@ -513,7 +514,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, read_updates_file($conf->get('resource.updates')), $LINKSDB, $conf, - $loginManager->isLoggedIn() + $loginManager->isLoggedIn(), + $_SESSION ); try { $newUpdates = $updater->update(); @@ -528,7 +530,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, die($e->getMessage()); } - $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); + $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); $PAGE->assign('linkcount', count($LINKSDB)); $PAGE->assign('privateLinkcount', count_private($LINKSDB)); $PAGE->assign('plugin_errors', $pluginManager->getErrors()); @@ -601,19 +603,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, // -------- Picture wall 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', []); + $PAGE->renderPage('picwall'); + exit; + } + // Optionally filter the results: $links = $LINKSDB->filterSearch($_GET); $linksToDisplay = array(); // Get only links which have a thumbnail. - foreach($links as $link) + // Note: we do not retrieve thumbnails here, the request is too heavy. + foreach($links as $key => $link) { - $permalink='?'.$link['shorturl']; - $thumb=lazyThumbnail($conf, $link['url'],$permalink); - if ($thumb!='') // Only output links which have a thumbnail. - { - $link['thumbnail']=$thumb; // Thumbnail HTML code. - $linksToDisplay[]=$link; // Add to array. + if (isset($link['thumbnail']) && $link['thumbnail'] !== false) { + $linksToDisplay[] = $link; // Add to array. } } @@ -626,7 +632,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $PAGE->assign($key, $value); } - $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); + $PAGE->renderPage('picwall'); exit; } @@ -1009,6 +1015,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $conf->set('api.secret', escape($_POST['apiSecret'])); $conf->set('translation.language', escape($_POST['language'])); + $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE; + if ($thumbnailsMode !== Thumbnailer::MODE_NONE + && $thumbnailsMode !== $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) + ) { + $_SESSION['warnings'][] = t( + 'You have enabled or changed thumbnails mode. Please synchronize them.' + ); + } + $conf->set('thumbnails.mode', $thumbnailsMode); + try { $conf->write($loginManager->isLoggedIn()); $history->updateSettings(); @@ -1047,6 +1063,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $PAGE->assign('api_secret', $conf->get('api.secret')); $PAGE->assign('languages', Languages::getAvailableLanguages()); $PAGE->assign('language', $conf->get('translation.language')); + $PAGE->assign('gd_enabled', extension_loaded('gd')); + $PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); $PAGE->renderPage('configure'); exit; @@ -1148,6 +1166,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $link['title'] = $link['url']; } + if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) { + $thumbnailer = new Thumbnailer($conf); + $link['thumbnail'] = $thumbnailer->get($url); + } + $pluginManager->executeHooks('save_link', $link); $LINKSDB[$id] = $link; @@ -1486,6 +1509,43 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, exit; } + // -------- Thumbnails Update + if ($targetPage == Router::$PAGE_THUMBS_UPDATE) { + $ids = []; + foreach ($LINKSDB as $link) { + // A note or not HTTP(S) + if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) { + continue; + } + $ids[] = $link['id']; + } + $PAGE->assign('ids', $ids); + $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli')); + $PAGE->renderPage('thumbnails'); + exit; + } + + // -------- Single Thumbnail Update + if ($targetPage == Router::$AJAX_THUMB_UPDATE) { + if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) { + http_response_code(400); + exit; + } + $id = (int) $_POST['id']; + if (empty($LINKSDB[$id])) { + http_response_code(404); + exit; + } + $thumbnailer = new Thumbnailer($conf); + $link = $LINKSDB[$id]; + $link['thumbnail'] = $thumbnailer->get($link['url']); + $LINKSDB[$id] = $link; + $LINKSDB->save($conf->get('resource.page_cache')); + + echo json_encode($link); + exit; + } + // -------- Otherwise, simply display search form and links: showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); exit; @@ -1549,6 +1609,12 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) // Start index. $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; $end = $i + $_SESSION['LINKS_PER_PAGE']; + + $thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE; + if ($thumbnailsEnabled) { + $thumbnailer = new Thumbnailer($conf); + } + $linkDisp = array(); while ($i<$end && $iget($link['url']); + $LINKSDB[$keys[$i]] = $elem; + $updateDB = true; + $link['thumbnail'] = $elem['thumbnail']; + } + // Check for both signs of a note: starting with ? and 7 chars long. - if ($link['url'][0] === '?' && - strlen($link['url']) === 7) { + if ($link['url'][0] === '?' && strlen($link['url']) === 7) { $link['url'] = index_url($_SERVER) . $link['url']; } @@ -1579,6 +1657,11 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) $i++; } + // If we retrieved new thumbnails, we update the database. + if (!empty($updateDB)) { + $LINKSDB->save($conf->get('resource.page_cache')); + } + // Compute paging navigation $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); @@ -1629,194 +1712,6 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) return; } -/** - * Compute the thumbnail for a link. - * - * With a link to the original URL. - * Understands various services (youtube.com...) - * Input: $url = URL for which the thumbnail must be found. - * $href = if provided, this URL will be followed instead of $url - * Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) - * Some of them may be missing. - * Return an empty array if no thumbnail available. - * - * @param ConfigManager $conf Configuration Manager instance. - * @param string $url - * @param string|bool $href - * - * @return array - */ -function computeThumbnail($conf, $url, $href = false) -{ - if (!$conf->get('thumbnail.enable_thumbnails')) return array(); - if ($href==false) $href=$url; - - // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. - // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) - // ^^^^^^^^^^^ ^^^^^^^^^^^ - $domain = parse_url($url,PHP_URL_HOST); - if ($domain=='youtube.com' || $domain=='www.youtube.com') - { - parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail - if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg', - 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); - } - if ($domain=='youtu.be') // Youtube short links - { - $path = parse_url($url,PHP_URL_PATH); - return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg', - 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); - } - if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting - { - parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename. - if (!empty($params) && !empty($params['img'])) return array('src'=>'http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']), - 'href'=>$href,'style'=>'max-width:120px; max-height:150px','alt'=>'pix.toile-libre.org thumbnail'); - } - - if ($domain=='imgur.com') - { - $path = parse_url($url,PHP_URL_PATH); - if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. - if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg', - 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); - if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg', - 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); - - if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg', - 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); - } - if ($domain=='i.imgur.com') - { - $pi = pathinfo(parse_url($url,PHP_URL_PATH)); - if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg', - 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); - } - if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') - { - if (strpos($url,'dailymotion.com/video/')!==false) - { - $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url); - return array('src'=>$thumburl, - 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'DailyMotion thumbnail'); - } - } - if (endsWith($domain,'.imageshack.us')) - { - $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); - if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') - { - $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext; - return array('src'=>$thumburl, - 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'imageshack.us thumbnail'); - } - } - - // Some other hosts are SLOW AS HELL and usually require an extra HTTP request to get the thumbnail URL. - // So we deport the thumbnail generation in order not to slow down page generation - // (and we also cache the thumbnail) - - if (! $conf->get('thumbnail.enable_localcache')) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache. - - if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') - || $domain=='vimeo.com' - || $domain=='ted.com' || endsWith($domain,'.ted.com') - || $domain=='xkcd.com' || endsWith($domain,'.xkcd.com') - ) - { - if ($domain=='vimeo.com') - { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric) - $path = parse_url($url,PHP_URL_PATH); - if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. - } - if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) - { // Make sure this URL points to a single comic (/xxx... where xxx is numeric) - $path = parse_url($url,PHP_URL_PATH); - if (!preg_match('!/\d+.+?!',$path)) return array(); - } - if ($domain=='ted.com' || endsWith($domain,'.ted.com')) - { // Make sure this TED URL points to a video (/talks/...) - $path = parse_url($url,PHP_URL_PATH); - if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. - } - $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) - return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), - 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); - } - - // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif - // Technically speaking, we should download ALL links and check their Content-Type to see if they are images. - // But using the extension will do. - $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); - if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') - { - $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) - return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), - 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); - } - return array(); // No thumbnail. - -} - - -// Returns the HTML code to display a thumbnail for a link -// with a link to the original URL. -// Understands various services (youtube.com...) -// Input: $url = URL for which the thumbnail must be found. -// $href = if provided, this URL will be followed instead of $url -// Returns '' if no thumbnail available. -function thumbnail($url,$href=false) -{ - // FIXME! - global $conf; - $t = computeThumbnail($conf, $url,$href); - if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. - - $html=''; - - // Lazy image - $html.='generateToken()); + $PAGE = new PageBuilder($conf, $_SESSION, null, $sessionManager->generateToken()); list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); $PAGE->assign('continents', $continents); $PAGE->assign('cities', $cities); @@ -1917,232 +1812,6 @@ function install($conf, $sessionManager, $loginManager) { exit; } -/** - * Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, - * I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. - * The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. - * This function is called by passing the URL: - * http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] - * [URL] is the URL of the link (e.g. a flickr page) - * [HMAC] is the signature for the [URL] (so that these URL cannot be forged). - * The function below will fetch the image from the webservice and store it in the cache. - * - * @param ConfigManager $conf Configuration Manager instance, - */ -function genThumbnail($conf) -{ - // Make sure the parameters in the URL were generated by us. - $sign = hash_hmac('sha256', $_GET['url'], $conf->get('credentials.salt')); - if ($sign!=$_GET['hmac']) die('Naughty boy!'); - - $cacheDir = $conf->get('resource.thumbnails_cache', 'cache'); - // Let's see if we don't already have the image for this URL in the cache. - $thumbname=hash('sha1',$_GET['url']).'.jpg'; - if (is_file($cacheDir .'/'. $thumbname)) - { // We have the thumbnail, just serve it: - header('Content-Type: image/jpeg'); - echo file_get_contents($cacheDir .'/'. $thumbname); - return; - } - // We may also serve a blank image (if service did not respond) - $blankname=hash('sha1',$_GET['url']).'.gif'; - if (is_file($cacheDir .'/'. $blankname)) - { - header('Content-Type: image/gif'); - echo file_get_contents($cacheDir .'/'. $blankname); - return; - } - - // Otherwise, generate the thumbnail. - $url = $_GET['url']; - $domain = parse_url($url,PHP_URL_HOST); - - if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) - { - // Crude replacement to handle new flickr domain policy (They prefer www. now) - $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); - - // Is this a link to an image, or to a flickr page ? - $imageurl=''; - if (endsWith(parse_url($url, PHP_URL_PATH), '.jpg')) - { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg - preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); - if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; - } - else // This is a flickr page (html) - { - // Get the flickr html page. - list($headers, $content) = get_http_response($url, 20); - if (strpos($headers[0], '200 OK') !== false) - { - // flickr now nicely provides the URL of the thumbnail in each flickr page. - preg_match('! - if ($imageurl=='') - { - preg_match('! tag on that page - // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html - // - list($headers, $content) = get_http_response($url, 5); - if (strpos($headers[0], '200 OK') !== false) { - // Extract the link to the thumbnail - preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!', $content, $matches); - if (!empty($matches[1])) - { // Let's download the image. - $imageurl=$matches[1]; - // No control on image size, so wait long enough - list($headers, $content) = get_http_response($imageurl, 20); - if (strpos($headers[0], '200 OK') !== false) { - $filepath = $cacheDir .'/'. $thumbname; - file_put_contents($filepath, $content); // Save image to cache. - if (resizeImage($filepath)) - { - header('Content-Type: image/jpeg'); - echo file_get_contents($filepath); - return; - } - } - } - } - } - - elseif ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) - { - // There is no thumbnail available for xkcd comics, so download the whole image and resize it. - // http://xkcd.com/327/ - // <BLABLA> - list($headers, $content) = get_http_response($url, 5); - if (strpos($headers[0], '200 OK') !== false) { - // Extract the link to the thumbnail - preg_match('!rrmdirContent($dir."/".$object); + else + unlink($dir."/".$object); + } + } + } + } +} diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index 94e3c7d..cacee2d 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php @@ -2,6 +2,7 @@ use Shaarli\Config\ConfigJson; use Shaarli\Config\ConfigManager; use Shaarli\Config\ConfigPhp; +use Shaarli\Thumbnailer; require_once 'tests/Updater/DummyUpdater.php'; require_once 'inc/rain.tpl.class.php'; @@ -20,7 +21,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase /** * @var string Config file path (without extension). */ - protected static $configFile = 'tests/utils/config/configJson'; + protected static $configFile = 'sandbox/config'; /** * @var ConfigManager @@ -32,6 +33,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase */ public function setUp() { + copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); $this->conf = new ConfigManager(self::$configFile); } @@ -684,4 +686,50 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; $this->assertEquals(4194304, $this->conf->get('general.download_max_size')); $this->assertEquals(3, $this->conf->get('general.download_timeout')); } + + /** + * Test updateMethodWebThumbnailer with thumbnails enabled. + */ + public function testUpdateMethodWebThumbnailerEnabled() + { + $this->conf->remove('thumbnails'); + $this->conf->set('thumbnail.enable_thumbnails', true); + $updater = new Updater([], [], $this->conf, true, $_SESSION); + $this->assertTrue($updater->updateMethodWebThumbnailer()); + $this->assertFalse($this->conf->exists('thumbnail')); + $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode')); + $this->assertEquals(125, $this->conf->get('thumbnails.width')); + $this->assertEquals(90, $this->conf->get('thumbnails.height')); + $this->assertContains('You have enabled or changed thumbnails', $_SESSION['warnings'][0]); + } + + /** + * Test updateMethodWebThumbnailer with thumbnails disabled. + */ + public function testUpdateMethodWebThumbnailerDisabled() + { + $this->conf->remove('thumbnails'); + $this->conf->set('thumbnail.enable_thumbnails', false); + $updater = new Updater([], [], $this->conf, true, $_SESSION); + $this->assertTrue($updater->updateMethodWebThumbnailer()); + $this->assertFalse($this->conf->exists('thumbnail')); + $this->assertEquals(Thumbnailer::MODE_NONE, $this->conf->get('thumbnails.mode')); + $this->assertEquals(125, $this->conf->get('thumbnails.width')); + $this->assertEquals(90, $this->conf->get('thumbnails.height')); + $this->assertTrue(empty($_SESSION['warnings'])); + } + + /** + * Test updateMethodWebThumbnailer with thumbnails disabled. + */ + public function testUpdateMethodWebThumbnailerNothingToDo() + { + $updater = new Updater([], [], $this->conf, true, $_SESSION); + $this->assertTrue($updater->updateMethodWebThumbnailer()); + $this->assertFalse($this->conf->exists('thumbnail')); + $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode')); + $this->assertEquals(90, $this->conf->get('thumbnails.width')); + $this->assertEquals(53, $this->conf->get('thumbnails.height')); + $this->assertTrue(empty($_SESSION['warnings'])); + } } diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php index 1ec447b..4a4e94a 100644 --- a/tests/config/ConfigManagerTest.php +++ b/tests/config/ConfigManagerTest.php @@ -81,6 +81,18 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); } + public function testSetDeleteNested() + { + $this->conf->set('foo.bar.key.stuff', 'testSetDeleteNested'); + $this->assertTrue($this->conf->exists('foo.bar')); + $this->assertTrue($this->conf->exists('foo.bar.key.stuff')); + $this->assertEquals('testSetDeleteNested', $this->conf->get('foo.bar.key.stuff')); + + $this->conf->remove('foo.bar'); + $this->assertFalse($this->conf->exists('foo.bar.key.stuff')); + $this->assertFalse($this->conf->exists('foo.bar')); + } + /** * Set with an empty key. * @@ -103,6 +115,17 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase $this->conf->set(array('foo' => 'bar'), 'stuff'); } + /** + * Remove with an empty key. + * + * @expectedException \Exception + * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# + */ + public function testRmoveEmptyKey() + { + $this->conf->remove(''); + } + /** * Try to write the config without mandatory parameter (e.g. 'login'). * diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php index 9c9288f..1549ddf 100644 --- a/tests/utils/config/configJson.json.php +++ b/tests/utils/config/configJson.json.php @@ -1,35 +1,84 @@ - diff --git a/tests/utils/config/wt.json b/tests/utils/config/wt.json new file mode 100644 index 0000000..69ce49a --- /dev/null +++ b/tests/utils/config/wt.json @@ -0,0 +1,12 @@ +{ + "settings": { + "default": { + "_comment": "infinite cache", + "cache_duration": -1, + "timeout": 10 + }, + "path": { + "cache": "sandbox/" + } + } +} \ No newline at end of file diff --git a/tpl/default/configure.html b/tpl/default/configure.html index a63c7ad..42e3223 100644 --- a/tpl/default/configure.html +++ b/tpl/default/configure.html @@ -242,6 +242,37 @@ +
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html index 322cddd..8ea2ce6 100644 --- a/tpl/default/linklist.html +++ b/tpl/default/linklist.html @@ -131,9 +131,17 @@ {/if} +{if="!empty($global_warnings) && $is_logged_in"} +
+
+
+ {loop="global_warnings"} +

{$value}

+ {/loop} +
+
+ +
+
+{/if} +
diff --git a/tpl/default/picwall.html b/tpl/default/picwall.html index 2f7e03d..9a0b10d 100644 --- a/tpl/default/picwall.html +++ b/tpl/default/picwall.html @@ -5,41 +5,61 @@ {include="page.header"} - -
-
-
- {$countPics=count($linksToDisplay)} -

{'Picture Wall'|t} - {$countPics} {'pics'|t}

- -
- {loop="$plugin_start_zone"} - {$value} - {/loop} -
- -
- {loop="$linksToDisplay"} -
- {$value.thumbnail}{$value.title} - {loop="$value.picwall_plugin"} - {$value} - {/loop} -
- {/loop} -
-
- -
- {loop="$plugin_end_zone"} - {$value} - {/loop} -
+{if="!$thumbnails_enabled"} +
+
+ {'Picture wall unavailable (thumbnails are disabled).'|t}
+{else} + {if="count($linksToDisplay)===0 && $is_logged_in"} +
+
+ {'There is no cached thumbnail. Try to synchronize them.'|t} +
+
+ {/if} + +
+
+
+ {$countPics=count($linksToDisplay)} +

{'Picture Wall'|t} - {$countPics} {'pics'|t}

+ +
+ {loop="$plugin_start_zone"} + {$value} + {/loop} +
+ +
+ {loop="$linksToDisplay"} +
+ {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore} + thumbnail + {$value.title} + {loop="$value.picwall_plugin"} + {$value} + {/loop} +
+ {/loop} +
+
+ +
+ {loop="$plugin_end_zone"} + {$value} + {/loop} +
+
+
+
+{/if} {include="page.footer"} - + diff --git a/tpl/default/thumbnails.html b/tpl/default/thumbnails.html new file mode 100644 index 0000000..a8cf904 --- /dev/null +++ b/tpl/default/thumbnails.html @@ -0,0 +1,48 @@ + + + + {include="includes"} + + +{include="page.header"} + +
+
+
+

{'Thumbnails update'|t}

+ +
+
+
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
+
+ 0 / {$ids|count} +
+
+
+ + +
+
+ +{include="page.footer"} + + + diff --git a/tpl/default/tools.html b/tpl/default/tools.html index ece6688..2006099 100644 --- a/tpl/default/tools.html +++ b/tpl/default/tools.html @@ -45,6 +45,14 @@
+ {if="$thumbnails_enabled"} + + {/if} + {loop="$tools_plugin"}
{$value} diff --git a/tpl/vintage/configure.html b/tpl/vintage/configure.html index 479284e..9466c23 100644 --- a/tpl/vintage/configure.html +++ b/tpl/vintage/configure.html @@ -128,6 +128,29 @@ + + Enable thumbnails + + + + + diff --git a/tpl/vintage/linklist.html b/tpl/vintage/linklist.html index 1ca51be..3f20284 100644 --- a/tpl/vintage/linklist.html +++ b/tpl/vintage/linklist.html @@ -80,7 +80,16 @@ {loop="$links"} -
{$value.url|thumbnail}
+ {if="$thumbnails_enabled && !empty($value.thumbnail)"} + + {/if}