aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2018-06-08 12:50:49 +0200
committerArthurHoaro <arthur@hoa.ro>2018-07-05 20:34:22 +0200
commit28f26524609338316cc6e51c743058e6e8c7b12b (patch)
treee03f1e5dde3779a47a682cf1461151fbd687e4bb
parent787faa42f3a2bcbf83a7853f23f3667a6febf9da (diff)
downloadShaarli-28f26524609338316cc6e51c743058e6e8c7b12b.tar.gz
Shaarli-28f26524609338316cc6e51c743058e6e8c7b12b.tar.zst
Shaarli-28f26524609338316cc6e51c743058e6e8c7b12b.zip
Add a page to update all thumbnails through AJAX requests in both templates
-rw-r--r--application/PageBuilder.php28
-rw-r--r--application/Router.php12
-rw-r--r--application/Updater.php25
-rw-r--r--application/config/ConfigManager.php7
-rw-r--r--assets/common/js/thumbnails-update.js51
-rw-r--r--assets/default/scss/shaarli.scss44
-rw-r--r--assets/vintage/css/shaarli.css40
-rw-r--r--composer.lock142
-rw-r--r--index.php81
-rw-r--r--tests/Updater/UpdaterTest.php40
-rw-r--r--tests/utils/config/configJson.json.php12
-rw-r--r--tpl/default/configure.html8
-rw-r--r--tpl/default/page.header.html14
-rw-r--r--tpl/default/thumbnails.html48
-rw-r--r--tpl/vintage/configure.html10
-rw-r--r--tpl/vintage/thumbnails.html28
-rw-r--r--webpack.config.js2
17 files changed, 487 insertions, 105 deletions
diff --git a/application/PageBuilder.php b/application/PageBuilder.php
index 3dba7677..5da70811 100644
--- a/application/PageBuilder.php
+++ b/application/PageBuilder.php
@@ -22,10 +22,20 @@ class PageBuilder
22 protected $conf; 22 protected $conf;
23 23
24 /** 24 /**
25 * @var array $_SESSION
26 */
27 protected $session;
28
29 /**
25 * @var LinkDB $linkDB instance. 30 * @var LinkDB $linkDB instance.
26 */ 31 */
27 protected $linkDB; 32 protected $linkDB;
28 33
34 /**
35 * @var null|string XSRF token
36 */
37 protected $token;
38
29 /** @var bool $isLoggedIn Whether the user is logged in **/ 39 /** @var bool $isLoggedIn Whether the user is logged in **/
30 protected $isLoggedIn = false; 40 protected $isLoggedIn = false;
31 41
@@ -33,14 +43,17 @@ class PageBuilder
33 * PageBuilder constructor. 43 * PageBuilder constructor.
34 * $tpl is initialized at false for lazy loading. 44 * $tpl is initialized at false for lazy loading.
35 * 45 *
36 * @param ConfigManager $conf Configuration Manager instance (reference). 46 * @param ConfigManager $conf Configuration Manager instance (reference).
37 * @param LinkDB $linkDB instance. 47 * @param array $session $_SESSION array
38 * @param string $token Session token 48 * @param LinkDB $linkDB instance.
49 * @param string $token Session token
50 * @param bool $isLoggedIn
39 */ 51 */
40 public function __construct(&$conf, $linkDB = null, $token = null, $isLoggedIn = false) 52 public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
41 { 53 {
42 $this->tpl = false; 54 $this->tpl = false;
43 $this->conf = $conf; 55 $this->conf = $conf;
56 $this->session = $session;
44 $this->linkDB = $linkDB; 57 $this->linkDB = $linkDB;
45 $this->token = $token; 58 $this->token = $token;
46 $this->isLoggedIn = $isLoggedIn; 59 $this->isLoggedIn = $isLoggedIn;
@@ -110,6 +123,11 @@ class PageBuilder
110 $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width')); 123 $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
111 $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height')); 124 $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
112 125
126 if (! empty($_SESSION['warnings'])) {
127 $this->tpl->assign('global_warnings', $_SESSION['warnings']);
128 unset($_SESSION['warnings']);
129 }
130
113 // To be removed with a proper theme configuration. 131 // To be removed with a proper theme configuration.
114 $this->tpl->assign('conf', $this->conf); 132 $this->tpl->assign('conf', $this->conf);
115 } 133 }
diff --git a/application/Router.php b/application/Router.php
index 4df0387c..bf86b884 100644
--- a/application/Router.php
+++ b/application/Router.php
@@ -7,6 +7,8 @@
7 */ 7 */
8class Router 8class Router
9{ 9{
10 public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';
11
10 public static $PAGE_LOGIN = 'login'; 12 public static $PAGE_LOGIN = 'login';
11 13
12 public static $PAGE_PICWALL = 'picwall'; 14 public static $PAGE_PICWALL = 'picwall';
@@ -47,6 +49,8 @@ class Router
47 49
48 public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin'; 50 public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin';
49 51
52 public static $PAGE_THUMBS_UPDATE = 'thumbs_update';
53
50 public static $GET_TOKEN = 'token'; 54 public static $GET_TOKEN = 'token';
51 55
52 /** 56 /**
@@ -101,6 +105,14 @@ class Router
101 return self::$PAGE_FEED_RSS; 105 return self::$PAGE_FEED_RSS;
102 } 106 }
103 107
108 if (startsWith($query, 'do='. self::$PAGE_THUMBS_UPDATE)) {
109 return self::$PAGE_THUMBS_UPDATE;
110 }
111
112 if (startsWith($query, 'do='. self::$AJAX_THUMB_UPDATE)) {
113 return self::$AJAX_THUMB_UPDATE;
114 }
115
104 // At this point, only loggedin pages. 116 // At this point, only loggedin pages.
105 if (!$loggedIn) { 117 if (!$loggedIn) {
106 return self::$PAGE_LINKLIST; 118 return self::$PAGE_LINKLIST;
diff --git a/application/Updater.php b/application/Updater.php
index 6fbe3e00..f6b9e205 100644
--- a/application/Updater.php
+++ b/application/Updater.php
@@ -31,6 +31,11 @@ class Updater
31 protected $isLoggedIn; 31 protected $isLoggedIn;
32 32
33 /** 33 /**
34 * @var array $_SESSION
35 */
36 protected $session;
37
38 /**
34 * @var ReflectionMethod[] List of current class methods. 39 * @var ReflectionMethod[] List of current class methods.
35 */ 40 */
36 protected $methods; 41 protected $methods;
@@ -42,13 +47,17 @@ class Updater
42 * @param LinkDB $linkDB LinkDB instance. 47 * @param LinkDB $linkDB LinkDB instance.
43 * @param ConfigManager $conf Configuration Manager instance. 48 * @param ConfigManager $conf Configuration Manager instance.
44 * @param boolean $isLoggedIn True if the user is logged in. 49 * @param boolean $isLoggedIn True if the user is logged in.
50 * @param array $session $_SESSION (by reference)
51 *
52 * @throws ReflectionException
45 */ 53 */
46 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) 54 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = [])
47 { 55 {
48 $this->doneUpdates = $doneUpdates; 56 $this->doneUpdates = $doneUpdates;
49 $this->linkDB = $linkDB; 57 $this->linkDB = $linkDB;
50 $this->conf = $conf; 58 $this->conf = $conf;
51 $this->isLoggedIn = $isLoggedIn; 59 $this->isLoggedIn = $isLoggedIn;
60 $this->session = &$session;
52 61
53 // Retrieve all update methods. 62 // Retrieve all update methods.
54 $class = new ReflectionClass($this); 63 $class = new ReflectionClass($this);
@@ -488,11 +497,23 @@ class Updater
488 */ 497 */
489 public function updateMethodWebThumbnailer() 498 public function updateMethodWebThumbnailer()
490 { 499 {
491 $this->conf->set('thumbnails.enabled', $this->conf->get('thumbnail.enable_thumbnails', true)); 500 if ($this->conf->exists('thumbnails.enabled')) {
501 return true;
502 }
503
504 $thumbnailsEnabled = $this->conf->get('thumbnail.enable_thumbnails', true);
505 $this->conf->set('thumbnails.enabled', $thumbnailsEnabled);
492 $this->conf->set('thumbnails.width', 125); 506 $this->conf->set('thumbnails.width', 125);
493 $this->conf->set('thumbnails.height', 90); 507 $this->conf->set('thumbnails.height', 90);
494 $this->conf->remove('thumbnail'); 508 $this->conf->remove('thumbnail');
495 $this->conf->write(true); 509 $this->conf->write(true);
510
511 if ($thumbnailsEnabled) {
512 $this->session['warnings'][] = t(
513 'You have enabled thumbnails. <a href="?do=thumbs_update">Please synchonize them</a>.'
514 );
515 }
516
496 return true; 517 return true;
497 } 518 }
498} 519}
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index faf25426..96e2e912 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -367,10 +367,6 @@ class ConfigManager
367 $this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS); 367 $this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS);
368 $this->setEmpty('general.default_note_title', 'Note: '); 368 $this->setEmpty('general.default_note_title', 'Note: ');
369 369
370 $this->setEmpty('thumbnails.enabled', true);
371 $this->setEmpty('thumbnails.width', 120);
372 $this->setEmpty('thumbnails.height', 120);
373
374 $this->setEmpty('updates.check_updates', false); 370 $this->setEmpty('updates.check_updates', false);
375 $this->setEmpty('updates.check_updates_branch', 'stable'); 371 $this->setEmpty('updates.check_updates_branch', 'stable');
376 $this->setEmpty('updates.check_updates_interval', 86400); 372 $this->setEmpty('updates.check_updates_interval', 86400);
@@ -385,9 +381,6 @@ class ConfigManager
385 // default state of the 'remember me' checkbox of the login form 381 // default state of the 'remember me' checkbox of the login form
386 $this->setEmpty('privacy.remember_user_default', true); 382 $this->setEmpty('privacy.remember_user_default', true);
387 383
388 $this->setEmpty('thumbnail.enable_thumbnails', true);
389 $this->setEmpty('thumbnail.enable_localcache', true);
390
391 $this->setEmpty('redirector.url', ''); 384 $this->setEmpty('redirector.url', '');
392 $this->setEmpty('redirector.encode_url', true); 385 $this->setEmpty('redirector.encode_url', true);
393 386
diff --git a/assets/common/js/thumbnails-update.js b/assets/common/js/thumbnails-update.js
new file mode 100644
index 00000000..b66ca3ae
--- /dev/null
+++ b/assets/common/js/thumbnails-update.js
@@ -0,0 +1,51 @@
1/**
2 * Script used in the thumbnails update page.
3 *
4 * It retrieves the list of link IDs to update, and execute AJAX requests
5 * to update their thumbnails, while updating the progress bar.
6 */
7
8/**
9 * Update the thumbnail of the link with the current i index in ids.
10 * It contains a recursive call to retrieve the thumb of the next link when it succeed.
11 * It also update the progress bar and other visual feedback elements.
12 *
13 * @param {array} ids List of LinkID to update
14 * @param {int} i Current index in ids
15 * @param {object} elements List of DOM element to avoid retrieving them at each iteration
16 */
17function updateThumb(ids, i, elements) {
18 const xhr = new XMLHttpRequest();
19 xhr.open('POST', '?do=ajax_thumb_update');
20 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
21 xhr.responseType = 'json';
22 xhr.onload = () => {
23 if (xhr.status !== 200) {
24 alert(`An error occurred. Return code: ${xhr.status}`);
25 } else {
26 const { response } = xhr;
27 i += 1;
28 elements.progressBar.style.width = `${(i * 100) / ids.length}%`;
29 elements.current.innerHTML = i;
30 elements.title.innerHTML = response.title;
31 if (response.thumbnail !== false) {
32 elements.thumbnail.innerHTML = `<img src="${response.thumbnail}">`;
33 }
34 if (i < ids.length) {
35 updateThumb(ids, i, elements);
36 }
37 }
38 };
39 xhr.send(`id=${ids[i]}`);
40}
41
42(() => {
43 const ids = document.getElementsByName('ids')[0].value.split(',');
44 const elements = {
45 progressBar: document.querySelector('.progressbar > div'),
46 current: document.querySelector('.progress-current'),
47 thumbnail: document.querySelector('.thumbnail-placeholder'),
48 title: document.querySelector('.thumbnail-link-title'),
49 };
50 updateThumb(ids, 0, elements);
51})();
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index 425a0490..6a8a8bc7 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -146,6 +146,13 @@ body,
146 background-color: $main-green; 146 background-color: $main-green;
147} 147}
148 148
149.pure-alert-warning {
150 a {
151 color: $warning-text;
152 font-weight: bold;
153 }
154}
155
149.page-single-alert { 156.page-single-alert {
150 margin-top: 100px; 157 margin-top: 100px;
151} 158}
@@ -1547,3 +1554,40 @@ form {
1547.pure-button-shaarli { 1554.pure-button-shaarli {
1548 background-color: $main-green; 1555 background-color: $main-green;
1549} 1556}
1557
1558.progressbar {
1559 border-radius: 6px;
1560 background-color: $main-green;
1561 padding: 1px;
1562
1563 > div {
1564 border-radius: 10px;
1565 background: repeating-linear-gradient(
1566 -45deg,
1567 $almost-white,
1568 $almost-white 6px,
1569 $background-color 6px,
1570 $background-color 12px
1571 );
1572 width: 0%;
1573 height: 10px;
1574 }
1575}
1576
1577.thumbnails-page-container {
1578 .progress-counter {
1579 padding: 10px 0 20px;
1580 }
1581
1582 .thumbnail-placeholder {
1583 margin: 10px auto;
1584 background-color: $light-grey;
1585 }
1586
1587 .thumbnail-link-title {
1588 padding-bottom: 20px;
1589 overflow: hidden;
1590 text-overflow: ellipsis;
1591 white-space: nowrap;
1592 }
1593}
diff --git a/assets/vintage/css/shaarli.css b/assets/vintage/css/shaarli.css
index 05e2c17e..87c440c8 100644
--- a/assets/vintage/css/shaarli.css
+++ b/assets/vintage/css/shaarli.css
@@ -1210,3 +1210,43 @@ ul.errors {
1210 width: 13px; 1210 width: 13px;
1211 height: 13px; 1211 height: 13px;
1212} 1212}
1213
1214.thumbnails-update-container {
1215 padding: 20px 0;
1216 width: 50%;
1217 margin: auto;
1218}
1219
1220.thumbnails-update-container .thumbnail-placeholder {
1221 background: grey;
1222 margin: auto;
1223}
1224
1225.thumbnails-update-container .thumbnail-link-title {
1226 width: 75%;
1227 margin: auto;
1228
1229 padding-bottom: 20px;
1230 overflow: hidden;
1231 text-overflow: ellipsis;
1232 white-space: nowrap;
1233}
1234
1235.progressbar {
1236 border-radius: 6px;
1237 background-color: #111;
1238 padding: 1px;
1239}
1240
1241.progressbar > div {
1242 border-radius: 10px;
1243 background: repeating-linear-gradient(
1244 -45deg,
1245 #f5f5f5,
1246 #f5f5f5 6px,
1247 #d0d0d0 6px,
1248 #d0d0d0 12px
1249 );
1250 width: 0%;
1251 height: 10px;
1252}
diff --git a/composer.lock b/composer.lock
index f97a688c..43c4ba68 100644
--- a/composer.lock
+++ b/composer.lock
@@ -551,12 +551,12 @@
551 "source": { 551 "source": {
552 "type": "git", 552 "type": "git",
553 "url": "https://github.com/pubsubhubbub/php-publisher.git", 553 "url": "https://github.com/pubsubhubbub/php-publisher.git",
554 "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f" 554 "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5"
555 }, 555 },
556 "dist": { 556 "dist": {
557 "type": "zip", 557 "type": "zip",
558 "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/0d224daebd504ab61c22fee4db58f8d1fc18945f", 558 "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/5008fc529b057251b48f4d17a10fdb20047ea8f5",
559 "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f", 559 "reference": "5008fc529b057251b48f4d17a10fdb20047ea8f5",
560 "shasum": "" 560 "shasum": ""
561 }, 561 },
562 "require": { 562 "require": {
@@ -586,7 +586,7 @@
586 "publishers", 586 "publishers",
587 "pubsubhubbub" 587 "pubsubhubbub"
588 ], 588 ],
589 "time": "2017-10-08T10:59:41+00:00" 589 "time": "2018-05-22T11:56:26+00:00"
590 }, 590 },
591 { 591 {
592 "name": "shaarli/netscape-bookmark-parser", 592 "name": "shaarli/netscape-bookmark-parser",
@@ -2254,21 +2254,22 @@
2254 }, 2254 },
2255 { 2255 {
2256 "name": "symfony/config", 2256 "name": "symfony/config",
2257 "version": "v3.4.9", 2257 "version": "v3.4.11",
2258 "source": { 2258 "source": {
2259 "type": "git", 2259 "type": "git",
2260 "url": "https://github.com/symfony/config.git", 2260 "url": "https://github.com/symfony/config.git",
2261 "reference": "7c2a9d44f4433863e9bca682e7f03609234657f9" 2261 "reference": "73e055cf2e6467715f187724a0347ea32079967c"
2262 }, 2262 },
2263 "dist": { 2263 "dist": {
2264 "type": "zip", 2264 "type": "zip",
2265 "url": "https://api.github.com/repos/symfony/config/zipball/7c2a9d44f4433863e9bca682e7f03609234657f9", 2265 "url": "https://api.github.com/repos/symfony/config/zipball/73e055cf2e6467715f187724a0347ea32079967c",
2266 "reference": "7c2a9d44f4433863e9bca682e7f03609234657f9", 2266 "reference": "73e055cf2e6467715f187724a0347ea32079967c",
2267 "shasum": "" 2267 "shasum": ""
2268 }, 2268 },
2269 "require": { 2269 "require": {
2270 "php": "^5.5.9|>=7.0.8", 2270 "php": "^5.5.9|>=7.0.8",
2271 "symfony/filesystem": "~2.8|~3.0|~4.0" 2271 "symfony/filesystem": "~2.8|~3.0|~4.0",
2272 "symfony/polyfill-ctype": "~1.8"
2272 }, 2273 },
2273 "conflict": { 2274 "conflict": {
2274 "symfony/dependency-injection": "<3.3", 2275 "symfony/dependency-injection": "<3.3",
@@ -2313,20 +2314,20 @@
2313 ], 2314 ],
2314 "description": "Symfony Config Component", 2315 "description": "Symfony Config Component",
2315 "homepage": "https://symfony.com", 2316 "homepage": "https://symfony.com",
2316 "time": "2018-03-19T22:32:39+00:00" 2317 "time": "2018-05-14T16:49:53+00:00"
2317 }, 2318 },
2318 { 2319 {
2319 "name": "symfony/console", 2320 "name": "symfony/console",
2320 "version": "v3.4.9", 2321 "version": "v3.4.11",
2321 "source": { 2322 "source": {
2322 "type": "git", 2323 "type": "git",
2323 "url": "https://github.com/symfony/console.git", 2324 "url": "https://github.com/symfony/console.git",
2324 "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf" 2325 "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27"
2325 }, 2326 },
2326 "dist": { 2327 "dist": {
2327 "type": "zip", 2328 "type": "zip",
2328 "url": "https://api.github.com/repos/symfony/console/zipball/5b1fdfa8eb93464bcc36c34da39cedffef822cdf", 2329 "url": "https://api.github.com/repos/symfony/console/zipball/36f83f642443c46f3cf751d4d2ee5d047d757a27",
2329 "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf", 2330 "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27",
2330 "shasum": "" 2331 "shasum": ""
2331 }, 2332 },
2332 "require": { 2333 "require": {
@@ -2382,20 +2383,20 @@
2382 ], 2383 ],
2383 "description": "Symfony Console Component", 2384 "description": "Symfony Console Component",
2384 "homepage": "https://symfony.com", 2385 "homepage": "https://symfony.com",
2385 "time": "2018-04-30T01:22:56+00:00" 2386 "time": "2018-05-16T08:49:21+00:00"
2386 }, 2387 },
2387 { 2388 {
2388 "name": "symfony/debug", 2389 "name": "symfony/debug",
2389 "version": "v3.4.9", 2390 "version": "v3.4.11",
2390 "source": { 2391 "source": {
2391 "type": "git", 2392 "type": "git",
2392 "url": "https://github.com/symfony/debug.git", 2393 "url": "https://github.com/symfony/debug.git",
2393 "reference": "1b95888cfd996484527cb41e8952d9a5eaf7454f" 2394 "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68"
2394 }, 2395 },
2395 "dist": { 2396 "dist": {
2396 "type": "zip", 2397 "type": "zip",
2397 "url": "https://api.github.com/repos/symfony/debug/zipball/1b95888cfd996484527cb41e8952d9a5eaf7454f", 2398 "url": "https://api.github.com/repos/symfony/debug/zipball/b28fd73fefbac341f673f5efd707d539d6a19f68",
2398 "reference": "1b95888cfd996484527cb41e8952d9a5eaf7454f", 2399 "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68",
2399 "shasum": "" 2400 "shasum": ""
2400 }, 2401 },
2401 "require": { 2402 "require": {
@@ -2438,20 +2439,20 @@
2438 ], 2439 ],
2439 "description": "Symfony Debug Component", 2440 "description": "Symfony Debug Component",
2440 "homepage": "https://symfony.com", 2441 "homepage": "https://symfony.com",
2441 "time": "2018-04-30T16:53:52+00:00" 2442 "time": "2018-05-16T14:03:39+00:00"
2442 }, 2443 },
2443 { 2444 {
2444 "name": "symfony/dependency-injection", 2445 "name": "symfony/dependency-injection",
2445 "version": "v3.4.9", 2446 "version": "v3.4.11",
2446 "source": { 2447 "source": {
2447 "type": "git", 2448 "type": "git",
2448 "url": "https://github.com/symfony/dependency-injection.git", 2449 "url": "https://github.com/symfony/dependency-injection.git",
2449 "reference": "54ff9d78b56429f9a1ac12e60bfb6d169c0468e3" 2450 "reference": "8a4672aca8db6d807905d695799ea7d83c8e5bba"
2450 }, 2451 },
2451 "dist": { 2452 "dist": {
2452 "type": "zip", 2453 "type": "zip",
2453 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/54ff9d78b56429f9a1ac12e60bfb6d169c0468e3", 2454 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8a4672aca8db6d807905d695799ea7d83c8e5bba",
2454 "reference": "54ff9d78b56429f9a1ac12e60bfb6d169c0468e3", 2455 "reference": "8a4672aca8db6d807905d695799ea7d83c8e5bba",
2455 "shasum": "" 2456 "shasum": ""
2456 }, 2457 },
2457 "require": { 2458 "require": {
@@ -2509,24 +2510,25 @@
2509 ], 2510 ],
2510 "description": "Symfony DependencyInjection Component", 2511 "description": "Symfony DependencyInjection Component",
2511 "homepage": "https://symfony.com", 2512 "homepage": "https://symfony.com",
2512 "time": "2018-04-29T14:04:08+00:00" 2513 "time": "2018-05-25T11:57:15+00:00"
2513 }, 2514 },
2514 { 2515 {
2515 "name": "symfony/filesystem", 2516 "name": "symfony/filesystem",
2516 "version": "v3.4.9", 2517 "version": "v3.4.11",
2517 "source": { 2518 "source": {
2518 "type": "git", 2519 "type": "git",
2519 "url": "https://github.com/symfony/filesystem.git", 2520 "url": "https://github.com/symfony/filesystem.git",
2520 "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541" 2521 "reference": "8e03ca3fa52a0f56b87506f38cf7bd3f9442b3a0"
2521 }, 2522 },
2522 "dist": { 2523 "dist": {
2523 "type": "zip", 2524 "type": "zip",
2524 "url": "https://api.github.com/repos/symfony/filesystem/zipball/253a4490b528597aa14d2bf5aeded6f5e5e4a541", 2525 "url": "https://api.github.com/repos/symfony/filesystem/zipball/8e03ca3fa52a0f56b87506f38cf7bd3f9442b3a0",
2525 "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541", 2526 "reference": "8e03ca3fa52a0f56b87506f38cf7bd3f9442b3a0",
2526 "shasum": "" 2527 "shasum": ""
2527 }, 2528 },
2528 "require": { 2529 "require": {
2529 "php": "^5.5.9|>=7.0.8" 2530 "php": "^5.5.9|>=7.0.8",
2531 "symfony/polyfill-ctype": "~1.8"
2530 }, 2532 },
2531 "type": "library", 2533 "type": "library",
2532 "extra": { 2534 "extra": {
@@ -2558,20 +2560,20 @@
2558 ], 2560 ],
2559 "description": "Symfony Filesystem Component", 2561 "description": "Symfony Filesystem Component",
2560 "homepage": "https://symfony.com", 2562 "homepage": "https://symfony.com",
2561 "time": "2018-02-22T10:48:49+00:00" 2563 "time": "2018-05-16T08:49:21+00:00"
2562 }, 2564 },
2563 { 2565 {
2564 "name": "symfony/finder", 2566 "name": "symfony/finder",
2565 "version": "v3.4.9", 2567 "version": "v3.4.11",
2566 "source": { 2568 "source": {
2567 "type": "git", 2569 "type": "git",
2568 "url": "https://github.com/symfony/finder.git", 2570 "url": "https://github.com/symfony/finder.git",
2569 "reference": "bd14efe8b1fabc4de82bf50dce62f05f9a102433" 2571 "reference": "472a92f3df8b247b49ae364275fb32943b9656c6"
2570 }, 2572 },
2571 "dist": { 2573 "dist": {
2572 "type": "zip", 2574 "type": "zip",
2573 "url": "https://api.github.com/repos/symfony/finder/zipball/bd14efe8b1fabc4de82bf50dce62f05f9a102433", 2575 "url": "https://api.github.com/repos/symfony/finder/zipball/472a92f3df8b247b49ae364275fb32943b9656c6",
2574 "reference": "bd14efe8b1fabc4de82bf50dce62f05f9a102433", 2576 "reference": "472a92f3df8b247b49ae364275fb32943b9656c6",
2575 "shasum": "" 2577 "shasum": ""
2576 }, 2578 },
2577 "require": { 2579 "require": {
@@ -2607,7 +2609,62 @@
2607 ], 2609 ],
2608 "description": "Symfony Finder Component", 2610 "description": "Symfony Finder Component",
2609 "homepage": "https://symfony.com", 2611 "homepage": "https://symfony.com",
2610 "time": "2018-04-04T05:07:11+00:00" 2612 "time": "2018-05-16T08:49:21+00:00"
2613 },
2614 {
2615 "name": "symfony/polyfill-ctype",
2616 "version": "v1.8.0",
2617 "source": {
2618 "type": "git",
2619 "url": "https://github.com/symfony/polyfill-ctype.git",
2620 "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae"
2621 },
2622 "dist": {
2623 "type": "zip",
2624 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae",
2625 "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae",
2626 "shasum": ""
2627 },
2628 "require": {
2629 "php": ">=5.3.3"
2630 },
2631 "type": "library",
2632 "extra": {
2633 "branch-alias": {
2634 "dev-master": "1.8-dev"
2635 }
2636 },
2637 "autoload": {
2638 "psr-4": {
2639 "Symfony\\Polyfill\\Ctype\\": ""
2640 },
2641 "files": [
2642 "bootstrap.php"
2643 ]
2644 },
2645 "notification-url": "https://packagist.org/downloads/",
2646 "license": [
2647 "MIT"
2648 ],
2649 "authors": [
2650 {
2651 "name": "Symfony Community",
2652 "homepage": "https://symfony.com/contributors"
2653 },
2654 {
2655 "name": "Gert de Pagter",
2656 "email": "BackEndTea@gmail.com"
2657 }
2658 ],
2659 "description": "Symfony polyfill for ctype functions",
2660 "homepage": "https://symfony.com",
2661 "keywords": [
2662 "compatibility",
2663 "ctype",
2664 "polyfill",
2665 "portable"
2666 ],
2667 "time": "2018-04-30T19:57:29+00:00"
2611 }, 2668 },
2612 { 2669 {
2613 "name": "symfony/polyfill-mbstring", 2670 "name": "symfony/polyfill-mbstring",
@@ -2670,20 +2727,21 @@
2670 }, 2727 },
2671 { 2728 {
2672 "name": "symfony/yaml", 2729 "name": "symfony/yaml",
2673 "version": "v3.4.9", 2730 "version": "v3.4.11",
2674 "source": { 2731 "source": {
2675 "type": "git", 2732 "type": "git",
2676 "url": "https://github.com/symfony/yaml.git", 2733 "url": "https://github.com/symfony/yaml.git",
2677 "reference": "033cfa61ef06ee0847e056e530201842b6e926c3" 2734 "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0"
2678 }, 2735 },
2679 "dist": { 2736 "dist": {
2680 "type": "zip", 2737 "type": "zip",
2681 "url": "https://api.github.com/repos/symfony/yaml/zipball/033cfa61ef06ee0847e056e530201842b6e926c3", 2738 "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0",
2682 "reference": "033cfa61ef06ee0847e056e530201842b6e926c3", 2739 "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0",
2683 "shasum": "" 2740 "shasum": ""
2684 }, 2741 },
2685 "require": { 2742 "require": {
2686 "php": "^5.5.9|>=7.0.8" 2743 "php": "^5.5.9|>=7.0.8",
2744 "symfony/polyfill-ctype": "~1.8"
2687 }, 2745 },
2688 "conflict": { 2746 "conflict": {
2689 "symfony/console": "<3.4" 2747 "symfony/console": "<3.4"
@@ -2724,7 +2782,7 @@
2724 ], 2782 ],
2725 "description": "Symfony Yaml Component", 2783 "description": "Symfony Yaml Component",
2726 "homepage": "https://symfony.com", 2784 "homepage": "https://symfony.com",
2727 "time": "2018-04-08T08:21:29+00:00" 2785 "time": "2018-05-03T23:18:14+00:00"
2728 }, 2786 },
2729 { 2787 {
2730 "name": "theseer/fdomdocument", 2788 "name": "theseer/fdomdocument",
diff --git a/index.php b/index.php
index 953f1085..d5a3e93d 100644
--- a/index.php
+++ b/index.php
@@ -514,7 +514,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
514 read_updates_file($conf->get('resource.updates')), 514 read_updates_file($conf->get('resource.updates')),
515 $LINKSDB, 515 $LINKSDB,
516 $conf, 516 $conf,
517 $loginManager->isLoggedIn() 517 $loginManager->isLoggedIn(),
518 $_SESSION
518 ); 519 );
519 try { 520 try {
520 $newUpdates = $updater->update(); 521 $newUpdates = $updater->update();
@@ -529,7 +530,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
529 die($e->getMessage()); 530 die($e->getMessage());
530 } 531 }
531 532
532 $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); 533 $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn());
533 $PAGE->assign('linkcount', count($LINKSDB)); 534 $PAGE->assign('linkcount', count($LINKSDB));
534 $PAGE->assign('privateLinkcount', count_private($LINKSDB)); 535 $PAGE->assign('privateLinkcount', count_private($LINKSDB));
535 $PAGE->assign('plugin_errors', $pluginManager->getErrors()); 536 $PAGE->assign('plugin_errors', $pluginManager->getErrors());
@@ -611,38 +612,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
611 $links = $LINKSDB->filterSearch($_GET); 612 $links = $LINKSDB->filterSearch($_GET);
612 $linksToDisplay = array(); 613 $linksToDisplay = array();
613 614
614 $thumbnailer = new Thumbnailer($conf);
615
616
617 $newThumbnailsCpt = 0;
618 // Get only links which have a thumbnail. 615 // Get only links which have a thumbnail.
616 // Note: we do not retrieve thumbnails here, the request is too heavy.
619 foreach($links as $key => $link) 617 foreach($links as $key => $link)
620 { 618 {
621 // Not a note,
622 // and (never retrieved yet or no valid cache file)
623 if ($link['url'][0] != '?'
624 && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail'])))
625 ) {
626 $item = $LINKSDB[$key];
627 $item['thumbnail'] = $thumbnailer->get($link['url']);
628 $LINKSDB[$key] = $item;
629 $newThumbnailsCpt++;
630 }
631
632 if (isset($link['thumbnail']) && $link['thumbnail'] !== false) { 619 if (isset($link['thumbnail']) && $link['thumbnail'] !== false) {
633 $linksToDisplay[] = $link; // Add to array. 620 $linksToDisplay[] = $link; // Add to array.
634 } 621 }
635
636 // If we retrieved new thumbnails, we update the database every 20 links.
637 // Downloading everything the first time may take a very long time
638 if ($newThumbnailsCpt == 20) {
639 $LINKSDB->save($conf->get('resource.page_cache'));
640 $newThumbnailsCpt = 0;
641 }
642 }
643
644 if ($newThumbnailsCpt > 0) {
645 $LINKSDB->save($conf->get('resource.page_cache'));
646 } 622 }
647 623
648 $data = array( 624 $data = array(
@@ -1036,7 +1012,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1036 $conf->set('api.enabled', !empty($_POST['enableApi'])); 1012 $conf->set('api.enabled', !empty($_POST['enableApi']));
1037 $conf->set('api.secret', escape($_POST['apiSecret'])); 1013 $conf->set('api.secret', escape($_POST['apiSecret']));
1038 $conf->set('translation.language', escape($_POST['language'])); 1014 $conf->set('translation.language', escape($_POST['language']));
1039 $conf->set('thumbnails.enabled', extension_loaded('gd') && !empty($_POST['enableThumbnails'])); 1015
1016 $thumbnailsEnabled = extension_loaded('gd') && !empty($_POST['enableThumbnails']);
1017 $conf->set('thumbnails.enabled', $thumbnailsEnabled);
1018
1019 if (! $conf->get('thumbnails.enabled') && $thumbnailsEnabled) {
1020 $_SESSION['warnings'][] = t(
1021 'You have enabled thumbnails. <a href="?do=thumbs_update">Please synchonize them</a>.'
1022 );
1023 }
1040 1024
1041 try { 1025 try {
1042 $conf->write($loginManager->isLoggedIn()); 1026 $conf->write($loginManager->isLoggedIn());
@@ -1521,6 +1505,43 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1521 exit; 1505 exit;
1522 } 1506 }
1523 1507
1508 // -------- Thumbnails Update
1509 if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
1510 $ids = [];
1511 foreach ($LINKSDB as $link) {
1512 // A note or not HTTP(S)
1513 if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) {
1514 continue;
1515 }
1516 $ids[] = $link['id'];
1517 }
1518 $PAGE->assign('ids', $ids);
1519 $PAGE->assign('pagetitle', t('Thumbnail update') .' - '. $conf->get('general.title', 'Shaarli'));
1520 $PAGE->renderPage('thumbnails');
1521 exit;
1522 }
1523
1524 // -------- Single Thumbnail Update
1525 if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
1526 if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) {
1527 http_response_code(400);
1528 exit;
1529 }
1530 $id = (int) $_POST['id'];
1531 if (empty($LINKSDB[$id])) {
1532 http_response_code(404);
1533 exit;
1534 }
1535 $thumbnailer = new Thumbnailer($conf);
1536 $link = $LINKSDB[$id];
1537 $link['thumbnail'] = $thumbnailer->get($link['url']);
1538 $LINKSDB[$id] = $link;
1539 $LINKSDB->save($conf->get('resource.page_cache'));
1540
1541 echo json_encode($link);
1542 exit;
1543 }
1544
1524 // -------- Otherwise, simply display search form and links: 1545 // -------- Otherwise, simply display search form and links:
1525 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 1546 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
1526 exit; 1547 exit;
@@ -1777,7 +1798,7 @@ function install($conf, $sessionManager, $loginManager) {
1777 exit; 1798 exit;
1778 } 1799 }
1779 1800
1780 $PAGE = new PageBuilder($conf, null, $sessionManager->generateToken()); 1801 $PAGE = new PageBuilder($conf, $_SESSION, null, $sessionManager->generateToken());
1781 list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); 1802 list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get());
1782 $PAGE->assign('continents', $continents); 1803 $PAGE->assign('continents', $continents);
1783 $PAGE->assign('cities', $cities); 1804 $PAGE->assign('cities', $cities);
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php
index 8b90fd5e..92ff5690 100644
--- a/tests/Updater/UpdaterTest.php
+++ b/tests/Updater/UpdaterTest.php
@@ -20,7 +20,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
20 /** 20 /**
21 * @var string Config file path (without extension). 21 * @var string Config file path (without extension).
22 */ 22 */
23 protected static $configFile = 'tests/utils/config/configJson'; 23 protected static $configFile = 'sandbox/config';
24 24
25 /** 25 /**
26 * @var ConfigManager 26 * @var ConfigManager
@@ -32,6 +32,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
32 */ 32 */
33 public function setUp() 33 public function setUp()
34 { 34 {
35 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
35 $this->conf = new ConfigManager(self::$configFile); 36 $this->conf = new ConfigManager(self::$configFile);
36 } 37 }
37 38
@@ -686,17 +687,48 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
686 } 687 }
687 688
688 /** 689 /**
689 * Test updateMethodAtomDefault with show_atom set to true. 690 * Test updateMethodWebThumbnailer with thumbnails enabled.
690 * => nothing to do
691 */ 691 */
692 public function testUpdateMethodWebThumbnailerEnabled() 692 public function testUpdateMethodWebThumbnailerEnabled()
693 { 693 {
694 $this->conf->remove('thumbnails');
694 $this->conf->set('thumbnail.enable_thumbnails', true); 695 $this->conf->set('thumbnail.enable_thumbnails', true);
695 $updater = new Updater([], [], $this->conf, true); 696 $updater = new Updater([], [], $this->conf, true, $_SESSION);
696 $this->assertTrue($updater->updateMethodWebThumbnailer()); 697 $this->assertTrue($updater->updateMethodWebThumbnailer());
697 $this->assertFalse($this->conf->exists('thumbnail')); 698 $this->assertFalse($this->conf->exists('thumbnail'));
698 $this->assertTrue($this->conf->get('thumbnails.enabled')); 699 $this->assertTrue($this->conf->get('thumbnails.enabled'));
699 $this->assertEquals(125, $this->conf->get('thumbnails.width')); 700 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
700 $this->assertEquals(90, $this->conf->get('thumbnails.height')); 701 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
702 $this->assertContains('You have enabled thumbnails', $_SESSION['warnings'][0]);
703 }
704
705 /**
706 * Test updateMethodWebThumbnailer with thumbnails disabled.
707 */
708 public function testUpdateMethodWebThumbnailerDisabled()
709 {
710 $this->conf->remove('thumbnails');
711 $this->conf->set('thumbnail.enable_thumbnails', false);
712 $updater = new Updater([], [], $this->conf, true, $_SESSION);
713 $this->assertTrue($updater->updateMethodWebThumbnailer());
714 $this->assertFalse($this->conf->exists('thumbnail'));
715 $this->assertFalse($this->conf->get('thumbnails.enabled'));
716 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
717 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
718 $this->assertTrue(empty($_SESSION['warnings']));
719 }
720
721 /**
722 * Test updateMethodWebThumbnailer with thumbnails disabled.
723 */
724 public function testUpdateMethodWebThumbnailerNothingToDo()
725 {
726 $updater = new Updater([], [], $this->conf, true, $_SESSION);
727 $this->assertTrue($updater->updateMethodWebThumbnailer());
728 $this->assertFalse($this->conf->exists('thumbnail'));
729 $this->assertTrue($this->conf->get('thumbnails.enabled'));
730 $this->assertEquals(90, $this->conf->get('thumbnails.width'));
731 $this->assertEquals(53, $this->conf->get('thumbnails.height'));
732 $this->assertTrue(empty($_SESSION['warnings']));
701 } 733 }
702} 734}
diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php
index 061d4c28..a656b67c 100644
--- a/tests/utils/config/configJson.json.php
+++ b/tests/utils/config/configJson.json.php
@@ -61,11 +61,6 @@
61 "dev": { 61 "dev": {
62 "debug": true 62 "debug": true
63 }, 63 },
64 "thumbnails": {
65 "enabled": true,
66 "width": 125,
67 "height": 90
68 },
69 "updates": { 64 "updates": {
70 "check_updates": false, 65 "check_updates": false,
71 "check_updates_branch": "stable", 66 "check_updates_branch": "stable",
@@ -79,6 +74,11 @@
79 "language": "auto", 74 "language": "auto",
80 "mode": "php", 75 "mode": "php",
81 "extensions": [] 76 "extensions": []
77 },
78 "thumbnails": {
79 "enabled": true,
80 "width": 90,
81 "height": 53
82 } 82 }
83} 83}
84*/ ?> \ No newline at end of file 84*/ ?>
diff --git a/tpl/default/configure.html b/tpl/default/configure.html
index 5695ae8e..dca9503b 100644
--- a/tpl/default/configure.html
+++ b/tpl/default/configure.html
@@ -248,12 +248,10 @@
248 <label for="enableThumbnails"> 248 <label for="enableThumbnails">
249 <span class="label-name">{'Enable thumbnails'|t}</span><br> 249 <span class="label-name">{'Enable thumbnails'|t}</span><br>
250 <span class="label-desc"> 250 <span class="label-desc">
251 {'Warning: '|t} 251 {if="! $gd_enabled"}
252 {if="$gd_enabled"}
253 {'It\'s recommended to visit the picture wall after enabling this feature.'|t}
254 {'If you have a large database, the first retrieval may take a few minutes.'|t}
255 {else}
256 {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t} 252 {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
253 {elseif="$thumbnails_enabled"}
254 <a href="?do=thumbs_update">{'Synchonize thumbnails'|t}</a>
257 {/if} 255 {/if}
258 </span> 256 </span>
259 </label> 257 </label>
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 840e4352..fc03404e 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -171,4 +171,18 @@
171 </div> 171 </div>
172{/if} 172{/if}
173 173
174{if="!empty($global_warnings) && $is_logged_in"}
175 <div class="pure-g pure-alert pure-alert-warning pure-alert-closable" id="shaarli-warnings-alert">
176 <div class="pure-u-2-24"></div>
177 <div class="pure-u-20-24">
178 {loop="global_warnings"}
179 <p>{$value}</p>
180 {/loop}
181 </div>
182 <div class="pure-u-2-24">
183 <i class="fa fa-times pure-alert-close"></i>
184 </div>
185 </div>
186{/if}
187
174 <div class="clear"></div> 188 <div class="clear"></div>
diff --git a/tpl/default/thumbnails.html b/tpl/default/thumbnails.html
new file mode 100644
index 00000000..a8cf904e
--- /dev/null
+++ b/tpl/default/thumbnails.html
@@ -0,0 +1,48 @@
1<!DOCTYPE html>
2<html>
3<head>
4 {include="includes"}
5</head>
6<body>
7{include="page.header"}
8
9<div class="pure-g thumbnails-page-container">
10 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
11 <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
12 <h2 class="window-title">{'Thumbnails update'|t}</h2>
13
14 <div class="pure-g">
15 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
16 <div class="pure-u-lg-1-3 pure-u-22-24">
17 <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
18 </div>
19 </div>
20
21 <div class="pure-g">
22 <div class="pure-u-1-12"></div>
23 <div class="pure-u-5-6">
24 <div class="thumbnail-link-title"></div>
25
26 <div class="progressbar">
27 <div></div>
28 </div>
29 </div>
30 </div>
31
32 <div class="pure-g">
33 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
34 <div class="pure-u-lg-1-3 pure-u-22-24">
35 <div class="progress-counter">
36 <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
37 </div>
38 </div>
39 </div>
40
41 <input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
42 </div>
43</div>
44
45{include="page.footer"}
46<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
47</body>
48</html>
diff --git a/tpl/vintage/configure.html b/tpl/vintage/configure.html
index e47c71ad..fc3a563b 100644
--- a/tpl/vintage/configure.html
+++ b/tpl/vintage/configure.html
@@ -132,11 +132,13 @@
132 <td valign="top"><b>Enable thumbnails</b></td> 132 <td valign="top"><b>Enable thumbnails</b></td>
133 <td> 133 <td>
134 <input type="checkbox" name="enableThumbnails" id="enableThumbnails" 134 <input type="checkbox" name="enableThumbnails" id="enableThumbnails"
135 {if="$thumbnails_enabled"}checked{/if}/> 135 {if="$thumbnails_enabled"}checked{/if} {if="!$gd_enabled"}disabled{/if}>
136 <label for="enableThumbnails"> 136 <label for="enableThumbnails">
137 &nbsp;<strong>Warning:</strong> 137 {if="! $gd_enabled"}
138 If you have a large database, the first retrieval may take a few minutes. 138 {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
139 It's recommended to visit the picture wall after enabling this feature 139 {elseif="$thumbnails_enabled"}
140 <a href="?do=thumbs_update">{'Synchonize thumbnails'|t}</a>
141 {/if}
140 </label> 142 </label>
141 </td> 143 </td>
142 </tr> 144 </tr>
diff --git a/tpl/vintage/thumbnails.html b/tpl/vintage/thumbnails.html
new file mode 100644
index 00000000..79aebf8d
--- /dev/null
+++ b/tpl/vintage/thumbnails.html
@@ -0,0 +1,28 @@
1<!DOCTYPE html>
2<html>
3<head>{include="includes"}</head>
4<body>
5<div id="pageheader">
6{include="page.header"}
7</div>
8
9<div class="center thumbnails-update-container">
10 <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
11
12 <div class="thumbnail-link-title"></div>
13
14 <div class="progressbar">
15 <div></div>
16 </div>
17
18 <div class="progress-counter">
19 <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
20 </div>
21</div>
22
23<input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
24
25{include="page.footer"}
26<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
27</body>
28</html>
diff --git a/webpack.config.js b/webpack.config.js
index 1fc5d016..ed548c73 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -24,6 +24,7 @@ module.exports = [
24 { 24 {
25 entry: { 25 entry: {
26 thumbnails: './assets/common/js/thumbnails.js', 26 thumbnails: './assets/common/js/thumbnails.js',
27 thumbnails_update: './assets/common/js/thumbnails-update.js',
27 pluginsadmin: './assets/default/js/plugins-admin.js', 28 pluginsadmin: './assets/default/js/plugins-admin.js',
28 shaarli: [ 29 shaarli: [
29 './assets/default/js/base.js', 30 './assets/default/js/base.js',
@@ -97,6 +98,7 @@ module.exports = [
97 './assets/vintage/css/shaarli.css', 98 './assets/vintage/css/shaarli.css',
98 ].concat(glob.sync('./assets/vintage/img/*')), 99 ].concat(glob.sync('./assets/vintage/img/*')),
99 thumbnails: './assets/common/js/thumbnails.js', 100 thumbnails: './assets/common/js/thumbnails.js',
101 thumbnails_update: './assets/common/js/thumbnails-update.js',
100 }, 102 },
101 output: { 103 output: {
102 filename: '[name].min.js', 104 filename: '[name].min.js',