<?php
use Shaarli\Config\ConfigManager;
+use Shaarli\Thumbnailer;
/**
* This class is in charge of building the final page.
*/
protected $conf;
+ /**
+ * @var array $_SESSION
+ */
+ protected $session;
+
/**
* @var LinkDB $linkDB instance.
*/
protected $linkDB;
-
+
+ /**
+ * @var null|string XSRF token
+ */
+ protected $token;
+
/** @var bool $isLoggedIn Whether the user is logged in **/
protected $isLoggedIn = false;
* PageBuilder constructor.
* $tpl is initialized at false for lazy loading.
*
- * @param ConfigManager $conf Configuration Manager instance (reference).
- * @param LinkDB $linkDB instance.
- * @param string $token Session token
+ * @param ConfigManager $conf Configuration Manager instance (reference).
+ * @param array $session $_SESSION array
+ * @param LinkDB $linkDB instance.
+ * @param string $token Session token
+ * @param bool $isLoggedIn
*/
- public function __construct(&$conf, $linkDB = null, $token = null, $isLoggedIn = false)
+ public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
{
$this->tpl = false;
$this->conf = $conf;
+ $this->session = $session;
$this->linkDB = $linkDB;
$this->token = $token;
$this->isLoggedIn = $isLoggedIn;
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);
}
*/
class Router
{
+ public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';
+
public static $PAGE_LOGIN = 'login';
public static $PAGE_PICWALL = 'picwall';
public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin';
+ public static $PAGE_THUMBS_UPDATE = 'thumbs_update';
+
public static $GET_TOKEN = 'token';
/**
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;
--- /dev/null
+<?php
+
+namespace Shaarli;
+
+use Shaarli\Config\ConfigManager;
+use WebThumbnailer\Exception\WebThumbnailerException;
+use WebThumbnailer\WebThumbnailer;
+use WebThumbnailer\Application\ConfigManager as WTConfigManager;
+
+/**
+ * Class Thumbnailer
+ *
+ * Utility class used to retrieve thumbnails using web-thumbnailer dependency.
+ */
+class Thumbnailer
+{
+ const COMMON_MEDIA_DOMAINS = [
+ 'imgur.com',
+ 'flickr.com',
+ 'youtube.com',
+ 'wikimedia.org',
+ 'redd.it',
+ 'gfycat.com',
+ 'media.giphy.com',
+ 'twitter.com',
+ 'twimg.com',
+ 'instagram.com',
+ 'pinterest.com',
+ 'pinterest.fr',
+ 'tumblr.com',
+ 'deviantart.com',
+ ];
+
+ const MODE_ALL = 'all';
+ const MODE_COMMON = 'common';
+ const MODE_NONE = 'none';
+
+ /**
+ * @var WebThumbnailer instance.
+ */
+ protected $wt;
+
+ /**
+ * @var ConfigManager instance.
+ */
+ protected $conf;
+
+ /**
+ * Thumbnailer constructor.
+ *
+ * @param ConfigManager $conf instance.
+ */
+ public function __construct($conf)
+ {
+ $this->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');
+ }
+}
use Shaarli\Config\ConfigJson;
use Shaarli\Config\ConfigPhp;
use Shaarli\Config\ConfigManager;
+use Shaarli\Thumbnailer;
/**
* Class Updater.
*/
protected $isLoggedIn;
+ /**
+ * @var array $_SESSION
+ */
+ protected $session;
+
/**
* @var ReflectionMethod[] List of current class methods.
*/
* @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);
}
$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. <a href="?do=thumbs_update">Please synchronize them</a>.'
+ );
+ }
return true;
}
}
}
+ /**
+ * 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.
*
*
* @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.
*/
$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.
*/
// 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', []);
+++ /dev/null
-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();
- }
-})();
--- /dev/null
+/**
+ * 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 = `<img src="${response.thumbnail}">`;
+ }
+ 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);
+})();
--- /dev/null
+import Blazy from 'blazy';
+
+(() => {
+ // Suppress ESLint error because that's how bLazy works
+ /* eslint-disable no-new */
+ new Blazy();
+})();
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;
}
.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: '';
}
}
}
.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;
+ }
+}
position: relative;
display: table-cell;
vertical-align: middle;
- width: 90px;
- height: 90px;
+ width: 120px;
+ height: 120px;
overflow: hidden;
text-align: center;
float: left;
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;
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;
+}
"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"
},
{
"_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",
},
{
"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": {
},
"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": "*",
"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": {
"translations",
"unicode"
],
- "time": "2017-03-23T17:02:28+00:00"
+ "time": "2018-06-21T15:58:36+00:00"
},
{
"name": "katzgrau/klogger",
],
"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",
"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": {
"publishers",
"pubsubhubbub"
],
- "time": "2017-10-08T10:59:41+00:00"
+ "time": "2018-05-22T11:56:26+00:00"
},
{
"name": "shaarli/netscape-bookmark-parser",
},
{
"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": {
"micro",
"router"
],
- "time": "2017-11-26T19:13:09+00:00"
+ "time": "2018-04-19T19:29:08+00:00"
}
],
"packages-dev": [
},
{
"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": {
"spy",
"stub"
],
- "time": "2018-02-19T10:16:54+00:00"
+ "time": "2018-04-18T13:57:24+00:00"
},
{
"name": "phpunit/php-code-coverage",
],
"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",
},
{
"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",
],
"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": {
"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": ""
],
"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": {
],
"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": {
],
"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": {
],
"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": {
],
"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-ctype",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae"
+ },
+ "dist": {
+ "type": "zip",
+ "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.7.0",
+ "version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
- "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171",
+ "reference": "3296adf6a6454a050679cde90f95350ad604b171",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.7-dev"
+ "dev-master": "1.8-dev"
}
},
"autoload": {
"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"
],
"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",
--- /dev/null
+## 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
---|:---:|---
[`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)
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"
#: 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"
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."
"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"
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. <a href=\"?do=thumbs_update"
+"\">Please synchronize them</a>."
+msgstr ""
+"Vous avez activé ou changé le mode de miniatures. <a href=\"?do=thumbs_update"
+"\">Merci de les synchroniser</a>."
+
+#: 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 "
"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 : "
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 "
"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 ""
"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
"cookies. Nous vous recommandons d'accéder à votre serveur depuis son adresse "
"IP ou un <em>Fully Qualified Domain Name</em>.<br>"
-#: index.php:1940
+#: index.php:1745
msgid "Click to try again."
msgstr "Cliquer ici pour réessayer."
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.<br><strong>Warning</"
"strong>:\n"
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"
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"
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 <code>php-gd</code> to use thumbnails."
+msgstr ""
+"Vous devez activer l'extension <code>php-gd</code> pour utiliser les "
+"miniatures."
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:285
+#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56
+msgid "Synchronize thumbnails"
+msgstr "Synchroniser les miniatures"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:296
+#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
+msgid "All"
+msgstr "Tous"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:300
+msgid "Only common media hosts"
+msgstr "Seulement les hébergeurs de média connus"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:304
+msgid "None"
+msgstr "Aucune"
+
+#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:312
+#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
msgid "Save"
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
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"
msgid "Selection"
msgstr "Choisir"
-#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
-msgid "All"
-msgstr "Tous"
-
#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
msgid "Public"
msgstr "Publics"
#: 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"
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
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"
"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é"
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 <a href=\"?do=thumbs_update"
+#| "\">synchronize them</a>."
+msgid ""
+"There is no cached thumbnail. Try to <a href=\"?do=thumbs_update"
+"\">synchronize them</a>."
+msgstr ""
+"Il n'y a aucune miniature en cache. Essayer de <a href=\"?do=thumbs_update"
+"\">les synchroniser</a>."
+
+#: 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"
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\""
"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"
"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 <strong>HTTPS</strong> to use this "
"functionality."
"Vous devez utiliser Shaarli en <strong>HTTPS</strong> 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"
"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"
--- /dev/null
+{
+ "settings": {
+ "default": {
+ "download_mode": "DOWNLOAD",
+ "_comment": "infinite cache",
+ "cache_duration": -1,
+ "timeout": 10
+ },
+ "path": {
+ "cache": "cache/"
+ }
+ }
+}
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 {
read_updates_file($conf->get('resource.updates')),
$LINKSDB,
$conf,
- $loginManager->isLoggedIn()
+ $loginManager->isLoggedIn(),
+ $_SESSION
);
try {
$newUpdates = $updater->update();
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());
// -------- 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.
}
}
$PAGE->assign($key, $value);
}
- $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli'));
+
$PAGE->renderPage('picwall');
exit;
}
$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. <a href="?do=thumbs_update">Please synchronize them</a>.'
+ );
+ }
+ $conf->set('thumbnails.mode', $thumbnailsMode);
+
try {
$conf->write($loginManager->isLoggedIn());
$history->updateSettings();
$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;
$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;
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;
// 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 && $i<count($keys))
{
$taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY);
uasort($taglist, 'strcasecmp');
$link['taglist'] = $taglist;
+
+ // Thumbnails enabled, not a note,
+ // and (never retrieved yet or no valid cache file)
+ if ($thumbnailsEnabled && $link['url'][0] != '?'
+ && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail'])))
+ ) {
+ $elem = $LINKSDB[$keys[$i]];
+ $elem['thumbnail'] = $thumbnailer->get($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'];
}
$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);
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='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"';
- if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
- $html.='></a>';
- return $html;
-}
-
-// Returns the HTML code to display a thumbnail for a link
-// for the picture wall (using lazy image loading)
-// 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 lazyThumbnail($conf, $url,$href=false)
-{
- // FIXME!
- global $conf;
- $t = computeThumbnail($conf, $url,$href);
- if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
-
- $html='<a href="'.escape($t['href']).'">';
-
- // Lazy image
- $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"';
-
- if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
- $html.='>';
-
- // No-JavaScript fallback.
- $html.='<noscript><img src="'.escape($t['src']).'"';
- if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
- if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
- if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
- if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
- $html.='></noscript></a>';
-
- return $html;
-}
-
-
/**
* Installation
* This function should NEVER be called if the file data/config.php exists.
exit;
}
- $PAGE = new PageBuilder($conf, null, $sessionManager->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);
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('!<link rel=\"image_src\" href=\"(.+?)\"!', $content, $matches);
- if (!empty($matches[1])) $imageurl=$matches[1];
-
- // In albums (and some other pages), the link rel="image_src" is not provided,
- // but flickr provides:
- // <meta property="og:image" content="http://farm4.staticflickr.com/3398/3239339068_25d13535ff_z.jpg" />
- if ($imageurl=='')
- {
- preg_match('!<meta property=\"og:image\" content=\"(.+?)\"!', $content, $matches);
- if (!empty($matches[1])) $imageurl=$matches[1];
- }
- }
- }
-
- if ($imageurl!='')
- { // Let's download the image.
- // Image is 240x120, so 10 seconds to download should be enough.
- list($headers, $content) = get_http_response($imageurl, 10);
- if (strpos($headers[0], '200 OK') !== false) {
- // Save image to cache.
- file_put_contents($cacheDir .'/'. $thumbname, $content);
- header('Content-Type: image/jpeg');
- echo $content;
- return;
- }
- }
- }
-
- elseif ($domain=='vimeo.com' )
- {
- // This is more complex: we have to perform a HTTP request, then parse the result.
- // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
- $vid = substr(parse_url($url,PHP_URL_PATH),1);
- list($headers, $content) = get_http_response('https://vimeo.com/api/v2/video/'.escape($vid).'.php', 5);
- if (strpos($headers[0], '200 OK') !== false) {
- $t = unserialize($content);
- $imageurl = $t[0]['thumbnail_medium'];
- // Then we download the image and serve it to our client.
- list($headers, $content) = get_http_response($imageurl, 10);
- if (strpos($headers[0], '200 OK') !== false) {
- // Save image to cache.
- file_put_contents($cacheDir .'/'. $thumbname, $content);
- header('Content-Type: image/jpeg');
- echo $content;
- return;
- }
- }
- }
-
- elseif ($domain=='ted.com' || endsWith($domain,'.ted.com'))
- {
- // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page
- // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html
- // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" />
- 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/
- // <img src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" title="<BLABLA>" alt="<BLABLA>" />
- list($headers, $content) = get_http_response($url, 5);
- if (strpos($headers[0], '200 OK') !== false) {
- // Extract the link to the thumbnail
- preg_match('!<img src="(http://imgs.xkcd.com/comics/.*)" title="[^s]!', $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;
- // Save image to cache.
- file_put_contents($filepath, $content);
- if (resizeImage($filepath))
- {
- header('Content-Type: image/jpeg');
- echo file_get_contents($filepath);
- return;
- }
- }
- }
- }
- }
-
- else
- {
- // For all other domains, we try to download the image and make a thumbnail.
- // We allow 30 seconds max to download (and downloads are limited to 4 Mb)
- list($headers, $content) = get_http_response($url, 30);
- if (strpos($headers[0], '200 OK') !== false) {
- $filepath = $cacheDir .'/'.$thumbname;
- // Save image to cache.
- file_put_contents($filepath, $content);
- if (resizeImage($filepath))
- {
- header('Content-Type: image/jpeg');
- echo file_get_contents($filepath);
- return;
- }
- }
- }
-
-
- // Otherwise, return an empty image (8x8 transparent gif)
- $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7');
- // Also put something in cache so that this URL is not requested twice.
- file_put_contents($cacheDir .'/'. $blankname, $blankgif);
- header('Content-Type: image/gif');
- echo $blankgif;
-}
-
-// Make a thumbnail of the image (to width: 120 pixels)
-// Returns true if success, false otherwise.
-function resizeImage($filepath)
-{
- if (!function_exists('imagecreatefromjpeg')) return false; // GD not present: no thumbnail possible.
-
- // Trick: some stupid people rename GIF as JPEG... or else.
- // So we really try to open each image type whatever the extension is.
- $header=file_get_contents($filepath,false,NULL,0,256); // Read first 256 bytes and try to sniff file type.
- $im=false;
- $i=strpos($header,'GIF8'); if (($i!==false) && ($i==0)) $im = imagecreatefromgif($filepath); // Well this is crude, but it should be enough.
- $i=strpos($header,'PNG'); if (($i!==false) && ($i==1)) $im = imagecreatefrompng($filepath);
- $i=strpos($header,'JFIF'); if ($i!==false) $im = imagecreatefromjpeg($filepath);
- if (!$im) return false; // Unable to open image (corrupted or not an image)
- $w = imagesx($im);
- $h = imagesy($im);
- $ystart = 0; $yheight=$h;
- if ($h>$w) { $ystart= ($h/2)-($w/2); $yheight=$w/2; }
- $nw = 120; // Desired width
- $nh = min(floor(($h*$nw)/$w),120); // Compute new width/height, but maximum 120 pixels height.
- // Resize image:
- $im2 = imagecreatetruecolor($nw,$nh);
- imagecopyresampled($im2, $im, 0, 0, 0, $ystart, $nw, $nh, $w, $yheight);
- imageinterlace($im2,true); // For progressive JPEG.
- $tempname=$filepath.'_TEMP.jpg';
- imagejpeg($im2, $tempname, 90);
- imagedestroy($im);
- imagedestroy($im2);
- unlink($filepath);
- rename($tempname,$filepath); // Overwrite original picture with thumbnail.
- return true;
-}
-
-if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail($conf); exit; } // Thumbnail generation/cache does not need the link database.
if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; }
if (!isset($_SESSION['LINKS_PER_PAGE'])) {
$_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
- Continuous integration tools: Continuous-integration-tools.md
- GnuPG signature: GnuPG-signature.md
- Directory structure: Directory-structure.md
+ - Link Structure: Link-structure.md
- 3rd party libraries: 3rd-party-libraries.md
- Plugin System: Plugin-System.md
- Release Shaarli: Release-Shaarli.md
--- /dev/null
+<?php
+
+namespace Shaarli;
+
+use PHPUnit\Framework\TestCase;
+use Shaarli\Config\ConfigManager;
+use WebThumbnailer\Application\ConfigManager as WTConfigManager;
+
+/**
+ * Class ThumbnailerTest
+ *
+ * We only make 1 thumb test because:
+ *
+ * 1. the thumbnailer library is itself tested
+ * 2. we don't want to make too many external requests during the tests
+ */
+class ThumbnailerTest extends TestCase
+{
+ const WIDTH = 190;
+
+ const HEIGHT = 210;
+
+ /**
+ * @var Thumbnailer;
+ */
+ protected $thumbnailer;
+
+ /**
+ * @var ConfigManager
+ */
+ protected $conf;
+
+ public function setUp()
+ {
+ $this->conf = new ConfigManager('tests/utils/config/configJson');
+ $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL);
+ $this->conf->set('thumbnails.width', self::WIDTH);
+ $this->conf->set('thumbnails.height', self::HEIGHT);
+ $this->conf->set('dev.debug', true);
+
+ $this->thumbnailer = new Thumbnailer($this->conf);
+ // cache files in the sandbox
+ WTConfigManager::addFile('tests/utils/config/wt.json');
+ }
+
+ public function tearDown()
+ {
+ $this->rrmdirContent('sandbox/');
+ }
+
+ /**
+ * Test a thumbnail with a custom size in 'all' mode.
+ */
+ public function testThumbnailAllValid()
+ {
+ $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/');
+ $this->assertNotFalse($thumb);
+ $image = imagecreatefromstring(file_get_contents($thumb));
+ $this->assertEquals(self::WIDTH, imagesx($image));
+ $this->assertEquals(self::HEIGHT, imagesy($image));
+ }
+
+ /**
+ * Test a thumbnail with a custom size in 'common' mode.
+ */
+ public function testThumbnailCommonValid()
+ {
+ $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
+ $thumb = $this->thumbnailer->get('https://imgur.com/jlFgGpe');
+ $this->assertNotFalse($thumb);
+ $image = imagecreatefromstring(file_get_contents($thumb));
+ $this->assertEquals(self::WIDTH, imagesx($image));
+ $this->assertEquals(self::HEIGHT, imagesy($image));
+ }
+
+ /**
+ * Test a thumbnail in 'common' mode which isn't include in common websites.
+ */
+ public function testThumbnailCommonInvalid()
+ {
+ $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
+ $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/');
+ $this->assertFalse($thumb);
+ }
+
+ /**
+ * Test a thumbnail that can't be retrieved.
+ */
+ public function testThumbnailNotValid()
+ {
+ $oldlog = ini_get('error_log');
+ ini_set('error_log', '/dev/null');
+
+ $thumbnailer = new Thumbnailer(new ConfigManager());
+ $thumb = $thumbnailer->get('nope');
+ $this->assertFalse($thumb);
+
+ ini_set('error_log', $oldlog);
+ }
+
+ protected function rrmdirContent($dir) {
+ if (is_dir($dir)) {
+ $objects = scandir($dir);
+ foreach ($objects as $object) {
+ if ($object != "." && $object != "..") {
+ if (is_dir($dir."/".$object))
+ $this->rrmdirContent($dir."/".$object);
+ else
+ unlink($dir."/".$object);
+ }
+ }
+ }
+ }
+}
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';
/**
* @var string Config file path (without extension).
*/
- protected static $configFile = 'tests/utils/config/configJson';
+ protected static $configFile = 'sandbox/config';
/**
* @var ConfigManager
*/
public function setUp()
{
+ copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
$this->conf = new ConfigManager(self::$configFile);
}
$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']));
+ }
}
$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.
*
$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').
*
<?php /*
{
"credentials": {
- "login":"root",
- "hash":"hash",
- "salt":"salt"
+ "login": "root",
+ "hash": "hash",
+ "salt": "salt"
},
"security": {
- "session_protection_disabled":false
+ "session_protection_disabled": false,
+ "ban_after": 4,
+ "ban_duration": 1800,
+ "open_shaarli": false,
+ "allowed_protocols": [
+ "ftp",
+ "ftps",
+ "magnet"
+ ]
},
"general": {
- "timezone":"Europe\/Paris",
+ "timezone": "Europe\/Paris",
"title": "Shaarli",
- "header_link": "?"
+ "header_link": "?",
+ "links_per_page": 20,
+ "enabled_plugins": [
+ "qrcode"
+ ],
+ "default_note_title": "Note: "
},
"privacy": {
- "default_private_links":true
+ "default_private_links": true,
+ "hide_public_links": false,
+ "force_login": false,
+ "hide_timestamps": false,
+ "remember_user_default": true
},
"redirector": {
- "url":"lala"
+ "url": "lala",
+ "encode_url": true
},
"config": {
"foo": "bar"
},
"resource": {
"datastore": "tests\/utils\/config\/datastore.php",
- "data_dir": "sandbox/",
- "raintpl_tpl": "tpl/"
+ "data_dir": "sandbox\/",
+ "raintpl_tpl": "tpl\/",
+ "config": "data\/config.php",
+ "ban_file": "data\/ipbans.php",
+ "updates": "data\/updates.txt",
+ "log": "data\/log.txt",
+ "update_check": "data\/lastupdatecheck.txt",
+ "history": "data\/history.php",
+ "theme": "default",
+ "raintpl_tmp": "tmp\/",
+ "thumbnails_cache": "cache",
+ "page_cache": "pagecache"
},
"plugins": {
"WALLABAG_VERSION": 1
+ },
+ "dev": {
+ "debug": true
+ },
+ "updates": {
+ "check_updates": false,
+ "check_updates_branch": "stable",
+ "check_updates_interval": 86400
+ },
+ "feed": {
+ "rss_permalinks": true,
+ "show_atom": true
+ },
+ "translation": {
+ "language": "auto",
+ "mode": "php",
+ "extensions": []
+ },
+ "thumbnails": {
+ "mode": "common",
+ "width": 90,
+ "height": 53
}
}
*/ ?>
-
--- /dev/null
+{
+ "settings": {
+ "default": {
+ "_comment": "infinite cache",
+ "cache_duration": -1,
+ "timeout": 10
+ },
+ "path": {
+ "cache": "sandbox/"
+ }
+ }
+}
\ No newline at end of file
</div>
</div>
</div>
+ <div class="pure-g">
+ <div class="pure-u-lg-{$ratioLabel} pure-u-{$ratioLabelMobile}">
+ <div class="form-label">
+ <label for="enableThumbnails">
+ <span class="label-name">{'Enable thumbnails'|t}</span><br>
+ <span class="label-desc">
+ {if="! $gd_enabled"}
+ {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
+ {elseif="$thumbnails_enabled"}
+ <a href="?do=thumbs_update">{'Synchronize thumbnails'|t}</a>
+ {/if}
+ </span>
+ </label>
+ </div>
+ </div>
+ <div class="pure-u-lg-{$ratioInput} pure-u-{$ratioInputMobile}">
+ <div class="form-input">
+ <select name="enableThumbnails" id="enableThumbnails" class="align">
+ <option value="all" {if="$thumbnails_mode=='all'"}selected{/if}>
+ {'All'|t}
+ </option>
+ <option value="common" {if="$thumbnails_mode=='common'"}selected{/if}>
+ {'Only common media hosts'|t}
+ </option>
+ <option value="none" {if="$thumbnails_mode=='none'"}selected{/if}>
+ {'None'|t}
+ </option>
+ </select>
+ </div>
+ </div>
+ </div>
<div class="center">
<input type="submit" value="{'Save'|t}" name="save">
</div>
<div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
<div class="linklist-item-title">
- {$thumb=thumbnail($value.url)}
- {if="$thumb!=false"}
- <div class="linklist-item-thumbnail">{$thumb}</div>
+ {if="$thumbnails_enabled && !empty($value.thumbnail)"}
+ <div class="linklist-item-thumbnail" style="width:{$thumbnails_width}px;height:{$thumbnails_height}px;">
+ <div class="thumbnail">
+ <a href="{$value.real_url}">
+ {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
+ <img data-src="{$value.thumbnail}#" class="b-lazy"
+ src="#"
+ alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
+ </a>
+ </div>
+ </div>
{/if}
{if="$is_logged_in"}
</div>
{include="page.footer"}
+<script src="js/thumbnails.min.js?v={$version_hash}"></script>
</body>
</html>
<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" id="shaarli-menu-picwall">
- <a href="?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a>
- </li>
+ {if="$thumbnails_enabled"}
+ <li class="pure-menu-item" id="shaarli-menu-picwall">
+ <a href="?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a>
+ </li>
+ {/if}
<li class="pure-menu-item" id="shaarli-menu-daily">
<a href="?do=daily" class="pure-menu-link">{'Daily'|t}</a>
</li>
</div>
{/if}
+{if="!empty($global_warnings) && $is_logged_in"}
+ <div class="pure-g pure-alert pure-alert-warning pure-alert-closable" id="shaarli-warnings-alert">
+ <div class="pure-u-2-24"></div>
+ <div class="pure-u-20-24">
+ {loop="global_warnings"}
+ <p>{$value}</p>
+ {/loop}
+ </div>
+ <div class="pure-u-2-24">
+ <i class="fa fa-times pure-alert-close"></i>
+ </div>
+ </div>
+{/if}
+
<div class="clear"></div>
</head>
<body>
{include="page.header"}
+{if="!$thumbnails_enabled"}
+<div class="pure-g pure-alert pure-alert-warning page-single-alert">
+ <div class="pure-u-1 center">
+ {'Picture wall unavailable (thumbnails are disabled).'|t}
+ </div>
+</div>
+{else}
+ {if="count($linksToDisplay)===0 && $is_logged_in"}
+ <div class="pure-g pure-alert pure-alert-warning page-single-alert">
+ <div class="pure-u-1 center">
+ {'There is no cached thumbnail. Try to <a href="?do=thumbs_update">synchronize them</a>.'|t}
+ </div>
+ </div>
+ {/if}
-<div class="pure-g">
- <div class="pure-u-lg-1-6 pure-u-1-24"></div>
- <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor">
- {$countPics=count($linksToDisplay)}
- <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2>
+ <div class="pure-g">
+ <div class="pure-u-lg-1-6 pure-u-1-24"></div>
+ <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor">
+ {$countPics=count($linksToDisplay)}
+ <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2>
- <div id="plugin_zone_start_picwall" class="plugin_zone">
- {loop="$plugin_start_zone"}
- {$value}
- {/loop}
- </div>
+ <div id="plugin_zone_start_picwall" class="plugin_zone">
+ {loop="$plugin_start_zone"}
+ {$value}
+ {/loop}
+ </div>
- <div id="picwall_container" class="picwall-container">
- {loop="$linksToDisplay"}
- <div class="picwall-pictureframe">
- {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
- {loop="$value.picwall_plugin"}
- {$value}
- {/loop}
- </div>
- {/loop}
- <div class="clear"></div>
- </div>
+ <div id="picwall-container" class="picwall-container">
+ {loop="$linksToDisplay"}
+ <div class="picwall-pictureframe">
+ {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
+ <img data-src="{$value.thumbnail}#" class="b-lazy"
+ src="#"
+ alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
+ <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
+ {loop="$value.picwall_plugin"}
+ {$value}
+ {/loop}
+ </div>
+ {/loop}
+ <div class="clear"></div>
+ </div>
- <div id="plugin_zone_end_picwall" class="plugin_zone">
- {loop="$plugin_end_zone"}
- {$value}
- {/loop}
+ <div id="plugin_zone_end_picwall" class="plugin_zone">
+ {loop="$plugin_end_zone"}
+ {$value}
+ {/loop}
+ </div>
</div>
+ <div class="pure-u-lg-1-6 pure-u-1-24"></div>
</div>
-</div>
+{/if}
{include="page.footer"}
-<script src="js/picwall.min.js?v={$version_hash}"></script>
+<script src="js/thumbnails.min.js?v={$version_hash}"></script>
</body>
</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ {include="includes"}
+</head>
+<body>
+{include="page.header"}
+
+<div class="pure-g thumbnails-page-container">
+ <div class="pure-u-lg-1-3 pure-u-1-24"></div>
+ <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
+ <h2 class="window-title">{'Thumbnails update'|t}</h2>
+
+ <div class="pure-g">
+ <div class="pure-u-lg-1-3 pure-u-1-24"></div>
+ <div class="pure-u-lg-1-3 pure-u-22-24">
+ <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
+ </div>
+ </div>
+
+ <div class="pure-g">
+ <div class="pure-u-1-12"></div>
+ <div class="pure-u-5-6">
+ <div class="thumbnail-link-title"></div>
+
+ <div class="progressbar">
+ <div></div>
+ </div>
+ </div>
+ </div>
+
+ <div class="pure-g">
+ <div class="pure-u-lg-1-3 pure-u-1-24"></div>
+ <div class="pure-u-lg-1-3 pure-u-22-24">
+ <div class="progress-counter">
+ <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
+ </div>
+ </div>
+ </div>
+
+ <input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
+ </div>
+</div>
+
+{include="page.footer"}
+<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
+</body>
+</html>
</a>
</div>
+ {if="$thumbnails_enabled"}
+ <div class="tools-item">
+ <a href="?do=thumbs_update" title="{'Synchronize all link thumbnails'|t}">
+ <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
+ </a>
+ </div>
+ {/if}
+
{loop="$tools_plugin"}
<div class="tools-item">
{$value}
<input type="text" name="apiSecret" id="apiSecret" size="50" value="{$api_secret}" />
</td>
</tr>
+ <tr>
+ <td valign="top"><b>Enable thumbnails</b></td>
+ <td>
+ <select name="enableThumbnails" id="enableThumbnails" class="align">
+ <option value="all" {if="$thumbnails_mode=='all'"}selected{/if}>
+ {'All'|t}
+ </option>
+ <option value="common" {if="$thumbnails_mode=='common'"}selected{/if}>
+ {'Only common media hosts'|t}
+ </option>
+ <option value="none" {if="$thumbnails_mode=='none'"}selected{/if}>
+ {'None'|t}
+ </option>
+ </select>
+ <label for="enableThumbnails">
+ {if="! $gd_enabled"}
+ {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
+ {elseif="$thumbnails_enabled"}
+ <a href="?do=thumbs_update">{'Synchonize thumbnails'|t}</a>
+ {/if}
+ </label>
+ </td>
+ </tr>
<tr>
<td></td>
{loop="$links"}
<li{if="$value.class"} class="{$value.class}"{/if}>
<a id="{$value.shorturl}"></a>
- <div class="thumbnail">{$value.url|thumbnail}</div>
+ {if="$thumbnails_enabled && !empty($value.thumbnail)"}
+ <div class="thumbnail">
+ <a href="{$value.real_url}">
+ {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
+ <img data-src="{$value.thumbnail}#" class="b-lazy"
+ src="#"
+ alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
+ </a>
+ </div>
+ {/if}
<div class="linkcontainer">
{if="$is_logged_in"}
<div class="linkeditbuttons">
</div>
{include="page.footer"}
+<script src="js/thumbnails.min.js"></script>
</body>
</html>
<div id="picwall_container">
{loop="$linksToDisplay"}
<div class="picwall_pictureframe">
- {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
+ {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
+ <img data-src="{$value.thumbnail}#" class="b-lazy"
+ src="#"
+ alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
+ <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
{loop="$value.picwall_plugin"}
{$value}
{/loop}
{include="page.footer"}
-<script src="js/picwall.min.js"></script>
+<script src="js/thumbnails.min.js"></script>
</body>
</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>{include="includes"}</head>
+<body>
+<div id="pageheader">
+{include="page.header"}
+</div>
+
+<div class="center thumbnails-update-container">
+ <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
+
+ <div class="thumbnail-link-title"></div>
+
+ <div class="progressbar">
+ <div></div>
+ </div>
+
+ <div class="progress-counter">
+ <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
+ </div>
+</div>
+
+<input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
+
+{include="page.footer"}
+<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
+</body>
+</html>
module.exports = [
{
entry: {
- picwall: './assets/common/js/picwall.js',
+ thumbnails: './assets/common/js/thumbnails.js',
+ thumbnails_update: './assets/common/js/thumbnails-update.js',
pluginsadmin: './assets/default/js/plugins-admin.js',
shaarli: [
'./assets/default/js/base.js',
'./assets/vintage/css/reset.css',
'./assets/vintage/css/shaarli.css',
].concat(glob.sync('./assets/vintage/img/*')),
- picwall: './assets/common/js/picwall.js',
+ thumbnails: './assets/common/js/thumbnails.js',
+ thumbnails_update: './assets/common/js/thumbnails-update.js',
},
output: {
filename: '[name].min.js',