aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.editorconfig2
-rw-r--r--application/bookmark/Bookmark.php2
-rw-r--r--application/container/ContainerBuilder.php5
-rw-r--r--application/container/ShaarliContainer.php2
-rw-r--r--application/front/controllers/PictureWallController.php72
-rw-r--r--application/front/exceptions/ThumbnailsDisabledException.php15
-rw-r--r--application/updater/Updater.php18
-rw-r--r--doc/md/RSS-feeds.md4
-rw-r--r--doc/md/Translations.md2
-rw-r--r--index.php33
-rw-r--r--tests/container/ContainerBuilderTest.php4
-rw-r--r--tests/front/controller/PictureWallControllerTest.php180
-rw-r--r--tpl/default/page.header.html2
-rw-r--r--tpl/default/picwall.html83
-rw-r--r--tpl/vintage/page.header.html2
15 files changed, 342 insertions, 84 deletions
diff --git a/.editorconfig b/.editorconfig
index 34bd7994..c2ab80eb 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,7 +14,7 @@ indent_size = 4
14indent_size = 2 14indent_size = 2
15 15
16[*.php] 16[*.php]
17max_line_length = 100 17max_line_length = 120
18 18
19[Dockerfile] 19[Dockerfile]
20max_line_length = 80 20max_line_length = 80
diff --git a/application/bookmark/Bookmark.php b/application/bookmark/Bookmark.php
index f9b21d3d..83ddab82 100644
--- a/application/bookmark/Bookmark.php
+++ b/application/bookmark/Bookmark.php
@@ -346,7 +346,7 @@ class Bookmark
346 /** 346 /**
347 * Get the Thumbnail. 347 * Get the Thumbnail.
348 * 348 *
349 * @return string|bool 349 * @return string|bool|null
350 */ 350 */
351 public function getThumbnail() 351 public function getThumbnail()
352 { 352 {
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php
index e2c78ccc..99c12334 100644
--- a/application/container/ContainerBuilder.php
+++ b/application/container/ContainerBuilder.php
@@ -7,6 +7,7 @@ namespace Shaarli\Container;
7use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Bookmark\BookmarkServiceInterface; 8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Formatter\FormatterFactory;
10use Shaarli\History; 11use Shaarli\History;
11use Shaarli\Plugin\PluginManager; 12use Shaarli\Plugin\PluginManager;
12use Shaarli\Render\PageBuilder; 13use Shaarli\Render\PageBuilder;
@@ -76,6 +77,10 @@ class ContainerBuilder
76 return new PluginManager($container->conf); 77 return new PluginManager($container->conf);
77 }; 78 };
78 79
80 $container['formatterFactory'] = function (ShaarliContainer $container): FormatterFactory {
81 return new FormatterFactory($container->conf, $container->loginManager->isLoggedIn());
82 };
83
79 return $container; 84 return $container;
80 } 85 }
81} 86}
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php
index 3fa9116e..fdf2f77f 100644
--- a/application/container/ShaarliContainer.php
+++ b/application/container/ShaarliContainer.php
@@ -6,6 +6,7 @@ namespace Shaarli\Container;
6 6
7use Shaarli\Bookmark\BookmarkServiceInterface; 7use Shaarli\Bookmark\BookmarkServiceInterface;
8use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
9use Shaarli\Formatter\FormatterFactory;
9use Shaarli\History; 10use Shaarli\History;
10use Shaarli\Plugin\PluginManager; 11use Shaarli\Plugin\PluginManager;
11use Shaarli\Render\PageBuilder; 12use Shaarli\Render\PageBuilder;
@@ -23,6 +24,7 @@ use Slim\Container;
23 * @property BookmarkServiceInterface $bookmarkService 24 * @property BookmarkServiceInterface $bookmarkService
24 * @property PageBuilder $pageBuilder 25 * @property PageBuilder $pageBuilder
25 * @property PluginManager $pluginManager 26 * @property PluginManager $pluginManager
27 * @property FormatterFactory $formatterFactory
26 */ 28 */
27class ShaarliContainer extends Container 29class ShaarliContainer extends Container
28{ 30{
diff --git a/application/front/controllers/PictureWallController.php b/application/front/controllers/PictureWallController.php
new file mode 100644
index 00000000..08d31b29
--- /dev/null
+++ b/application/front/controllers/PictureWallController.php
@@ -0,0 +1,72 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use Shaarli\Front\Exception\ThumbnailsDisabledException;
8use Shaarli\Thumbnailer;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * Class PicturesWallController
14 *
15 * Slim controller used to render the pictures wall page.
16 * If thumbnails mode is set to NONE, we just render the template without any image.
17 *
18 * @package Front\Controller
19 */
20class PictureWallController extends ShaarliController
21{
22 public function index(Request $request, Response $response): Response
23 {
24 if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) {
25 throw new ThumbnailsDisabledException();
26 }
27
28 $this->assignView(
29 'pagetitle',
30 t('Picture wall') .' - '. $this->container->conf->get('general.title', 'Shaarli')
31 );
32
33 // Optionally filter the results:
34 $links = $this->container->bookmarkService->search($request->getQueryParams());
35 $linksToDisplay = [];
36
37 // Get only bookmarks which have a thumbnail.
38 // Note: we do not retrieve thumbnails here, the request is too heavy.
39 $formatter = $this->container->formatterFactory->getFormatter('raw');
40 foreach ($links as $key => $link) {
41 if (!empty($link->getThumbnail())) {
42 $linksToDisplay[] = $formatter->format($link);
43 }
44 }
45
46 $data = $this->executeHooks($linksToDisplay);
47 foreach ($data as $key => $value) {
48 $this->assignView($key, $value);
49 }
50
51 return $response->write($this->render('picwall'));
52 }
53
54 /**
55 * @param mixed[] $linksToDisplay List of formatted bookmarks
56 *
57 * @return mixed[] Template data after active plugins render_picwall hook execution.
58 */
59 protected function executeHooks(array $linksToDisplay): array
60 {
61 $data = [
62 'linksToDisplay' => $linksToDisplay,
63 ];
64 $this->container->pluginManager->executeHooks(
65 'render_picwall',
66 $data,
67 ['loggedin' => $this->container->loginManager->isLoggedIn()]
68 );
69
70 return $data;
71 }
72}
diff --git a/application/front/exceptions/ThumbnailsDisabledException.php b/application/front/exceptions/ThumbnailsDisabledException.php
new file mode 100644
index 00000000..1b9cf5b7
--- /dev/null
+++ b/application/front/exceptions/ThumbnailsDisabledException.php
@@ -0,0 +1,15 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7class ThumbnailsDisabledException extends ShaarliException
8{
9 public function __construct()
10 {
11 $message = t('Picture wall unavailable (thumbnails are disabled).');
12
13 parent::__construct($message, 400);
14 }
15}
diff --git a/application/updater/Updater.php b/application/updater/Updater.php
index 95654d81..4bbcb9f2 100644
--- a/application/updater/Updater.php
+++ b/application/updater/Updater.php
@@ -2,8 +2,8 @@
2 2
3namespace Shaarli\Updater; 3namespace Shaarli\Updater;
4 4
5use Shaarli\Config\ConfigManager;
6use Shaarli\Bookmark\BookmarkServiceInterface; 5use Shaarli\Bookmark\BookmarkServiceInterface;
6use Shaarli\Config\ConfigManager;
7use Shaarli\Updater\Exception\UpdaterException; 7use Shaarli\Updater\Exception\UpdaterException;
8 8
9/** 9/**
@@ -111,4 +111,20 @@ class Updater
111 { 111 {
112 return $this->doneUpdates; 112 return $this->doneUpdates;
113 } 113 }
114
115 /**
116 * With the Slim routing system, default header link should be `./` instead of `?`.
117 * Otherwise you can not go back to the home page. Example: `/picture-wall` -> `/picture-wall?` instead of `/`.
118 */
119 public function updateMethodRelativeHomeLink(): bool
120 {
121 $link = trim($this->conf->get('general.header_link'));
122 if ($link[0] === '?') {
123 $link = './'. ltrim($link, '?');
124
125 $this->conf->set('general.header_link', $link, true, true);
126 }
127
128 return true;
129 }
114} 130}
diff --git a/doc/md/RSS-feeds.md b/doc/md/RSS-feeds.md
index d943218e..71f4d7ee 100644
--- a/doc/md/RSS-feeds.md
+++ b/doc/md/RSS-feeds.md
@@ -21,8 +21,8 @@ For example, if you want to subscribe only to links tagged `photography`:
21- Click on the `RSS Feed` button. 21- Click on the `RSS Feed` button.
22- You are presented with an RSS feed showing only these links. Subscribe to it to receive only updates with this tag. 22- You are presented with an RSS feed showing only these links. Subscribe to it to receive only updates with this tag.
23- The same method **also works for a full-text search** (_Search_ box) **and for the Picture Wall** (want to only see pictures about `nature`?) 23- The same method **also works for a full-text search** (_Search_ box) **and for the Picture Wall** (want to only see pictures about `nature`?)
24- You can also build the URLs manually: 24- You can also build the URLs manually:
25 - `https://my.shaarli.domain/?do=rss&searchtags=nature` 25 - `https://my.shaarli.domain/?do=rss&searchtags=nature`
26 - `https://my.shaarli.domain/links/?do=picwall&searchterm=poney` 26 - `https://my.shaarli.domain/links/picture-wall?searchterm=poney`
27 27
28![](images/rss-filter-1.png) ![](images/rss-filter-2.png) 28![](images/rss-filter-1.png) ![](images/rss-filter-2.png)
diff --git a/doc/md/Translations.md b/doc/md/Translations.md
index 58b92da3..dfdd021e 100644
--- a/doc/md/Translations.md
+++ b/doc/md/Translations.md
@@ -42,7 +42,7 @@ http://<replace_domain>/?post
42http://<replace_domain>/?do=export 42http://<replace_domain>/?do=export
43http://<replace_domain>/?do=import 43http://<replace_domain>/?do=import
44http://<replace_domain>/login 44http://<replace_domain>/login
45http://<replace_domain>/?do=picwall 45http://<replace_domain>/picture-wall
46http://<replace_domain>/?do=pluginadmin 46http://<replace_domain>/?do=pluginadmin
47http://<replace_domain>/?do=tagcloud 47http://<replace_domain>/?do=tagcloud
48http://<replace_domain>/?do=taglist 48http://<replace_domain>/?do=taglist
diff --git a/index.php b/index.php
index d3cb33d3..c639a3bc 100644
--- a/index.php
+++ b/index.php
@@ -610,37 +610,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
610 610
611 // -------- Picture wall 611 // -------- Picture wall
612 if ($targetPage == Router::$PAGE_PICWALL) { 612 if ($targetPage == Router::$PAGE_PICWALL) {
613 $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); 613 header('Location: ./picture-wall');
614 if (! $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) {
615 $PAGE->assign('linksToDisplay', []);
616 $PAGE->renderPage('picwall');
617 exit;
618 }
619
620 // Optionally filter the results:
621 $links = $bookmarkService->search($_GET);
622 $linksToDisplay = [];
623
624 // Get only bookmarks which have a thumbnail.
625 // Note: we do not retrieve thumbnails here, the request is too heavy.
626 $factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
627 $formatter = $factory->getFormatter();
628 foreach ($links as $key => $link) {
629 if ($link->getThumbnail() !== false) {
630 $linksToDisplay[] = $formatter->format($link);
631 }
632 }
633
634 $data = [
635 'linksToDisplay' => $linksToDisplay,
636 ];
637 $pluginManager->executeHooks('render_picwall', $data, ['loggedin' => $loginManager->isLoggedIn()]);
638
639 foreach ($data as $key => $value) {
640 $PAGE->assign($key, $value);
641 }
642
643 $PAGE->renderPage('picwall');
644 exit; 614 exit;
645 } 615 }
646 616
@@ -1944,6 +1914,7 @@ $app->group('/api/v1', function () {
1944 1914
1945$app->group('', function () { 1915$app->group('', function () {
1946 $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login'); 1916 $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login');
1917 $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall');
1947})->add('\Shaarli\Front\ShaarliMiddleware'); 1918})->add('\Shaarli\Front\ShaarliMiddleware');
1948 1919
1949$response = $app->run(true); 1920$response = $app->run(true);
diff --git a/tests/container/ContainerBuilderTest.php b/tests/container/ContainerBuilderTest.php
index 9b97ed6d..cc2eb37f 100644
--- a/tests/container/ContainerBuilderTest.php
+++ b/tests/container/ContainerBuilderTest.php
@@ -7,6 +7,7 @@ namespace Shaarli\Container;
7use PHPUnit\Framework\TestCase; 7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkServiceInterface; 8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Formatter\FormatterFactory;
10use Shaarli\History; 11use Shaarli\History;
11use Shaarli\Render\PageBuilder; 12use Shaarli\Render\PageBuilder;
12use Shaarli\Security\LoginManager; 13use Shaarli\Security\LoginManager;
@@ -30,7 +31,9 @@ class ContainerBuilderTest extends TestCase
30 { 31 {
31 $this->conf = new ConfigManager('tests/utils/config/configJson'); 32 $this->conf = new ConfigManager('tests/utils/config/configJson');
32 $this->sessionManager = $this->createMock(SessionManager::class); 33 $this->sessionManager = $this->createMock(SessionManager::class);
34
33 $this->loginManager = $this->createMock(LoginManager::class); 35 $this->loginManager = $this->createMock(LoginManager::class);
36 $this->loginManager->method('isLoggedIn')->willReturn(true);
34 37
35 $this->containerBuilder = new ContainerBuilder($this->conf, $this->sessionManager, $this->loginManager); 38 $this->containerBuilder = new ContainerBuilder($this->conf, $this->sessionManager, $this->loginManager);
36 } 39 }
@@ -45,5 +48,6 @@ class ContainerBuilderTest extends TestCase
45 static::assertInstanceOf(History::class, $container->history); 48 static::assertInstanceOf(History::class, $container->history);
46 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService); 49 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
47 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); 50 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
51 static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
48 } 52 }
49} 53}
diff --git a/tests/front/controller/PictureWallControllerTest.php b/tests/front/controller/PictureWallControllerTest.php
new file mode 100644
index 00000000..63802abd
--- /dev/null
+++ b/tests/front/controller/PictureWallControllerTest.php
@@ -0,0 +1,180 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Bookmark\BookmarkServiceInterface;
10use Shaarli\Config\ConfigManager;
11use Shaarli\Container\ShaarliContainer;
12use Shaarli\Formatter\BookmarkFormatter;
13use Shaarli\Formatter\BookmarkRawFormatter;
14use Shaarli\Formatter\FormatterFactory;
15use Shaarli\Front\Exception\ThumbnailsDisabledException;
16use Shaarli\Plugin\PluginManager;
17use Shaarli\Render\PageBuilder;
18use Shaarli\Security\LoginManager;
19use Shaarli\Thumbnailer;
20use Slim\Http\Request;
21use Slim\Http\Response;
22
23class PictureWallControllerTest extends TestCase
24{
25 /** @var ShaarliContainer */
26 protected $container;
27
28 /** @var PictureWallController */
29 protected $controller;
30
31 public function setUp(): void
32 {
33 $this->container = $this->createMock(ShaarliContainer::class);
34 $this->controller = new PictureWallController($this->container);
35 }
36
37 public function testValidControllerInvokeDefault(): void
38 {
39 $this->createValidContainerMockSet();
40
41 $request = $this->createMock(Request::class);
42 $request->expects(static::once())->method('getQueryParams')->willReturn([]);
43 $response = new Response();
44
45 // ConfigManager: thumbnails are enabled
46 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
47 if ($parameter === 'thumbnails.mode') {
48 return Thumbnailer::MODE_COMMON;
49 }
50
51 return $default;
52 });
53
54 // Save RainTPL assigned variables
55 $assignedVariables = [];
56 $this->container->pageBuilder
57 ->expects(static::atLeastOnce())
58 ->method('assign')
59 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
60 $assignedVariables[$key] = $value;
61
62 return $this;
63 })
64 ;
65
66 // Links dataset: 2 links with thumbnails
67 $this->container->bookmarkService
68 ->expects(static::once())
69 ->method('search')
70 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
71 // Visibility is set through the container, not the call
72 static::assertNull($visibility);
73
74 // No query parameters
75 if (count($parameters) === 0) {
76 return [
77 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'),
78 (new Bookmark())->setId(2)->setUrl('http://url2.tld'),
79 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'),
80 ];
81 }
82 })
83 ;
84
85 // Make sure that PluginManager hook is triggered
86 $this->container->pluginManager
87 ->expects(static::at(0))
88 ->method('executeHooks')
89 ->willReturnCallback(function (string $hook, array $data, array $param): array {
90 static::assertSame('render_picwall', $hook);
91 static::assertArrayHasKey('linksToDisplay', $data);
92 static::assertCount(2, $data['linksToDisplay']);
93 static::assertSame(1, $data['linksToDisplay'][0]['id']);
94 static::assertSame(3, $data['linksToDisplay'][1]['id']);
95 static::assertArrayHasKey('loggedin', $param);
96
97 return $data;
98 });
99
100 $result = $this->controller->index($request, $response);
101
102 static::assertSame(200, $result->getStatusCode());
103 static::assertSame('picwall', (string) $result->getBody());
104 static::assertSame('Picture wall - Shaarli', $assignedVariables['pagetitle']);
105 static::assertCount(2, $assignedVariables['linksToDisplay']);
106
107 $link = $assignedVariables['linksToDisplay'][0];
108
109 static::assertSame(1, $link['id']);
110 static::assertSame('http://url.tld', $link['url']);
111 static::assertSame('thumb1', $link['thumbnail']);
112
113 $link = $assignedVariables['linksToDisplay'][1];
114
115 static::assertSame(3, $link['id']);
116 static::assertSame('http://url3.tld', $link['url']);
117 static::assertSame('thumb2', $link['thumbnail']);
118 }
119
120 public function testControllerWithThumbnailsDisabled(): void
121 {
122 $this->expectException(ThumbnailsDisabledException::class);
123
124 $this->createValidContainerMockSet();
125
126 $request = $this->createMock(Request::class);
127 $response = new Response();
128
129 // ConfigManager: thumbnails are disabled
130 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
131 if ($parameter === 'thumbnails.mode') {
132 return Thumbnailer::MODE_NONE;
133 }
134
135 return $default;
136 });
137
138 $this->controller->index($request, $response);
139 }
140
141 protected function createValidContainerMockSet(): void
142 {
143 $loginManager = $this->createMock(LoginManager::class);
144 $this->container->loginManager = $loginManager;
145
146 // Config
147 $conf = $this->createMock(ConfigManager::class);
148 $this->container->conf = $conf;
149
150 // PageBuilder
151 $pageBuilder = $this->createMock(PageBuilder::class);
152 $pageBuilder
153 ->method('render')
154 ->willReturnCallback(function (string $template): string {
155 return $template;
156 })
157 ;
158 $this->container->pageBuilder = $pageBuilder;
159
160 // Plugin Manager
161 $pluginManager = $this->createMock(PluginManager::class);
162 $this->container->pluginManager = $pluginManager;
163
164 // BookmarkService
165 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
166 $this->container->bookmarkService = $bookmarkService;
167
168 // Formatter
169 $formatterFactory = $this->createMock(FormatterFactory::class);
170 $formatterFactory
171 ->method('getFormatter')
172 ->willReturnCallback(function (string $type): BookmarkFormatter {
173 if ($type === 'raw') {
174 return new BookmarkRawFormatter($this->container->conf, true);
175 }
176 })
177 ;
178 $this->container->formatterFactory = $formatterFactory;
179 }
180}
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 69e2fcd7..116265a5 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -34,7 +34,7 @@
34 </li> 34 </li>
35 {if="$thumbnails_enabled"} 35 {if="$thumbnails_enabled"}
36 <li class="pure-menu-item" id="shaarli-menu-picwall"> 36 <li class="pure-menu-item" id="shaarli-menu-picwall">
37 <a href="./?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a> 37 <a href="./picture-wall?{function="ltrim($searchcrits, '&')"}" class="pure-menu-link">{'Picture wall'|t}</a>
38 </li> 38 </li>
39 {/if} 39 {/if}
40 <li class="pure-menu-item" id="shaarli-menu-daily"> 40 <li class="pure-menu-item" id="shaarli-menu-daily">
diff --git a/tpl/default/picwall.html b/tpl/default/picwall.html
index da5101db..5343abd6 100644
--- a/tpl/default/picwall.html
+++ b/tpl/default/picwall.html
@@ -5,58 +5,51 @@
5</head> 5</head>
6<body> 6<body>
7{include="page.header"} 7{include="page.header"}
8{if="!$thumbnails_enabled"} 8
9<div class="pure-g pure-alert pure-alert-warning page-single-alert"> 9{if="count($linksToDisplay)===0 && $is_logged_in"}
10 <div class="pure-u-1 center"> 10 <div class="pure-g pure-alert pure-alert-warning page-single-alert">
11 {'Picture wall unavailable (thumbnails are disabled).'|t} 11 <div class="pure-u-1 center">
12 </div> 12 {'There is no cached thumbnail. Try to <a href="./?do=thumbs_update">synchronize them</a>.'|t}
13</div>
14{else}
15 {if="count($linksToDisplay)===0 && $is_logged_in"}
16 <div class="pure-g pure-alert pure-alert-warning page-single-alert">
17 <div class="pure-u-1 center">
18 {'There is no cached thumbnail. Try to <a href="./?do=thumbs_update">synchronize them</a>.'|t}
19 </div>
20 </div> 13 </div>
21 {/if} 14 </div>
15{/if}
22 16
23 <div class="pure-g"> 17<div class="pure-g">
24 <div class="pure-u-lg-1-6 pure-u-1-24"></div> 18 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
25 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor"> 19 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor">
26 {$countPics=count($linksToDisplay)} 20 {$countPics=count($linksToDisplay)}
27 <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2> 21 <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2>
28 22
29 <div id="plugin_zone_start_picwall" class="plugin_zone"> 23 <div id="plugin_zone_start_picwall" class="plugin_zone">
30 {loop="$plugin_start_zone"} 24 {loop="$plugin_start_zone"}
31 {$value} 25 {$value}
32 {/loop} 26 {/loop}
33 </div> 27 </div>
34 28
35 <div id="picwall-container" class="picwall-container" role="list"> 29 <div id="picwall-container" class="picwall-container" role="list">
36 {loop="$linksToDisplay"} 30 {loop="$linksToDisplay"}
37 <div class="picwall-pictureframe" role="listitem"> 31 <div class="picwall-pictureframe" role="listitem">
38 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore} 32 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
39 <img data-src="{$value.thumbnail}#" class="b-lazy" 33 <img data-src="{$value.thumbnail}#" class="b-lazy"
40 src="" 34 src=""
41 alt="" width="{$thumbnails_width}" height="{$thumbnails_height}" /> 35 alt="" width="{$thumbnails_width}" height="{$thumbnails_height}" />
42 <a href="{$value.real_url}"><span class="info">{$value.title}</span></a> 36 <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
43 {loop="$value.picwall_plugin"} 37 {loop="$value.picwall_plugin"}
44 {$value} 38 {$value}
45 {/loop} 39 {/loop}
46 </div> 40 </div>
47 {/loop} 41 {/loop}
48 <div class="clear"></div> 42 <div class="clear"></div>
49 </div> 43 </div>
50 44
51 <div id="plugin_zone_end_picwall" class="plugin_zone"> 45 <div id="plugin_zone_end_picwall" class="plugin_zone">
52 {loop="$plugin_end_zone"} 46 {loop="$plugin_end_zone"}
53 {$value} 47 {$value}
54 {/loop} 48 {/loop}
55 </div>
56 </div> 49 </div>
57 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
58 </div> 50 </div>
59{/if} 51 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
52</div>
60 53
61{include="page.footer"} 54{include="page.footer"}
62<script src="js/thumbnails.min.js?v={$version_hash}"></script> 55<script src="js/thumbnails.min.js?v={$version_hash}"></script>
diff --git a/tpl/vintage/page.header.html b/tpl/vintage/page.header.html
index f1869a04..ce938421 100644
--- a/tpl/vintage/page.header.html
+++ b/tpl/vintage/page.header.html
@@ -32,7 +32,7 @@
32 <li><a href="{$feedurl}?do=atom{$searchcrits}" class="nomobile">ATOM Feed</a></li> 32 <li><a href="{$feedurl}?do=atom{$searchcrits}" class="nomobile">ATOM Feed</a></li>
33 {/if} 33 {/if}
34 <li><a href="./?do=tagcloud">Tag cloud</a></li> 34 <li><a href="./?do=tagcloud">Tag cloud</a></li>
35 <li><a href="./?do=picwall{$searchcrits}">Picture wall</a></li> 35 <li><a href="./picture-wall{function="ltrim($searchcrits, '&')"}">Picture wall</a></li>
36 <li><a href="./?do=daily">Daily</a></li> 36 <li><a href="./?do=daily">Daily</a></li>
37 {loop="$plugins_header.buttons_toolbar"} 37 {loop="$plugins_header.buttons_toolbar"}
38 <li><a 38 <li><a