]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Process picwall rendering through Slim controller + UT
authorArthurHoaro <arthur@hoa.ro>
Sun, 26 Jan 2020 10:15:15 +0000 (11:15 +0100)
committerArthurHoaro <arthur@hoa.ro>
Thu, 23 Jul 2020 19:19:21 +0000 (21:19 +0200)
15 files changed:
.editorconfig
application/bookmark/Bookmark.php
application/container/ContainerBuilder.php
application/container/ShaarliContainer.php
application/front/controllers/PictureWallController.php [new file with mode: 0644]
application/front/exceptions/ThumbnailsDisabledException.php [new file with mode: 0644]
application/updater/Updater.php
doc/md/RSS-feeds.md
doc/md/Translations.md
index.php
tests/container/ContainerBuilderTest.php
tests/front/controller/PictureWallControllerTest.php [new file with mode: 0644]
tpl/default/page.header.html
tpl/default/picwall.html
tpl/vintage/page.header.html

index 34bd7994d68f53311b9b961692db7ba407503b76..c2ab80ebc8bc5e105ce209e23aa5cc3dcb84006f 100644 (file)
@@ -14,7 +14,7 @@ indent_size = 4
 indent_size = 2
 
 [*.php]
-max_line_length = 100
+max_line_length = 120
 
 [Dockerfile]
 max_line_length = 80
index f9b21d3d0b59dda47bf9f055607d258b7e13376b..83ddab825abed20492cd37452f3cd446bb6b3de1 100644 (file)
@@ -346,7 +346,7 @@ class Bookmark
     /**
      * Get the Thumbnail.
      *
-     * @return string|bool
+     * @return string|bool|null
      */
     public function getThumbnail()
     {
index e2c78ccc44f4c47a412a4ba10a1e95c0c2f93fdb..99c123347c5b736720048b69729ec1dc0d203031 100644 (file)
@@ -7,6 +7,7 @@ namespace Shaarli\Container;
 use Shaarli\Bookmark\BookmarkFileService;
 use Shaarli\Bookmark\BookmarkServiceInterface;
 use Shaarli\Config\ConfigManager;
+use Shaarli\Formatter\FormatterFactory;
 use Shaarli\History;
 use Shaarli\Plugin\PluginManager;
 use Shaarli\Render\PageBuilder;
@@ -76,6 +77,10 @@ class ContainerBuilder
             return new PluginManager($container->conf);
         };
 
+        $container['formatterFactory'] = function (ShaarliContainer $container): FormatterFactory {
+            return new FormatterFactory($container->conf, $container->loginManager->isLoggedIn());
+        };
+
         return $container;
     }
 }
index 3fa9116e3544ee2e6b73f8b552a9bf30b6b92aa7..fdf2f77fe5ac23054ed5b508fe99a8d04852294b 100644 (file)
@@ -6,6 +6,7 @@ namespace Shaarli\Container;
 
 use Shaarli\Bookmark\BookmarkServiceInterface;
 use Shaarli\Config\ConfigManager;
+use Shaarli\Formatter\FormatterFactory;
 use Shaarli\History;
 use Shaarli\Plugin\PluginManager;
 use Shaarli\Render\PageBuilder;
@@ -23,6 +24,7 @@ use Slim\Container;
  * @property BookmarkServiceInterface $bookmarkService
  * @property PageBuilder              $pageBuilder
  * @property PluginManager            $pluginManager
+ * @property FormatterFactory         $formatterFactory
  */
 class ShaarliContainer extends Container
 {
diff --git a/application/front/controllers/PictureWallController.php b/application/front/controllers/PictureWallController.php
new file mode 100644 (file)
index 0000000..08d31b2
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller;
+
+use Shaarli\Front\Exception\ThumbnailsDisabledException;
+use Shaarli\Thumbnailer;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+/**
+ * Class PicturesWallController
+ *
+ * Slim controller used to render the pictures wall page.
+ * If thumbnails mode is set to NONE, we just render the template without any image.
+ *
+ * @package Front\Controller
+ */
+class PictureWallController extends ShaarliController
+{
+    public function index(Request $request, Response $response): Response
+    {
+        if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) {
+            throw new ThumbnailsDisabledException();
+        }
+
+        $this->assignView(
+            'pagetitle',
+            t('Picture wall') .' - '. $this->container->conf->get('general.title', 'Shaarli')
+        );
+
+        // Optionally filter the results:
+        $links = $this->container->bookmarkService->search($request->getQueryParams());
+        $linksToDisplay = [];
+
+        // Get only bookmarks which have a thumbnail.
+        // Note: we do not retrieve thumbnails here, the request is too heavy.
+        $formatter = $this->container->formatterFactory->getFormatter('raw');
+        foreach ($links as $key => $link) {
+            if (!empty($link->getThumbnail())) {
+                $linksToDisplay[] = $formatter->format($link);
+            }
+        }
+
+        $data = $this->executeHooks($linksToDisplay);
+        foreach ($data as $key => $value) {
+            $this->assignView($key, $value);
+        }
+
+        return $response->write($this->render('picwall'));
+    }
+
+    /**
+     * @param mixed[] $linksToDisplay List of formatted bookmarks
+     *
+     * @return mixed[] Template data after active plugins render_picwall hook execution.
+     */
+    protected function executeHooks(array $linksToDisplay): array
+    {
+        $data = [
+            'linksToDisplay' => $linksToDisplay,
+        ];
+        $this->container->pluginManager->executeHooks(
+            'render_picwall',
+            $data,
+            ['loggedin' => $this->container->loginManager->isLoggedIn()]
+        );
+
+        return $data;
+    }
+}
diff --git a/application/front/exceptions/ThumbnailsDisabledException.php b/application/front/exceptions/ThumbnailsDisabledException.php
new file mode 100644 (file)
index 0000000..1b9cf5b
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Exception;
+
+class ThumbnailsDisabledException extends ShaarliException
+{
+    public function __construct()
+    {
+        $message = t('Picture wall unavailable (thumbnails are disabled).');
+
+        parent::__construct($message, 400);
+    }
+}
index 95654d81da96c0d23c2d26424c47983b39c25961..4bbcb9f270664a8e11266716548f502368765aec 100644 (file)
@@ -2,8 +2,8 @@
 
 namespace Shaarli\Updater;
 
-use Shaarli\Config\ConfigManager;
 use Shaarli\Bookmark\BookmarkServiceInterface;
+use Shaarli\Config\ConfigManager;
 use Shaarli\Updater\Exception\UpdaterException;
 
 /**
@@ -111,4 +111,20 @@ class Updater
     {
         return $this->doneUpdates;
     }
+
+    /**
+     * With the Slim routing system, default header link should be `./` instead of `?`.
+     * Otherwise you can not go back to the home page. Example: `/picture-wall` -> `/picture-wall?` instead of `/`.
+     */
+    public function updateMethodRelativeHomeLink(): bool
+    {
+        $link = trim($this->conf->get('general.header_link'));
+        if ($link[0] === '?') {
+            $link = './'. ltrim($link, '?');
+
+            $this->conf->set('general.header_link', $link, true, true);
+        }
+
+        return true;
+    }
 }
index d943218e655eb0ca4c1533feb0542880dbf2e36d..71f4d7eeff39d7fd88825f4283493f415d6637c6 100644 (file)
@@ -21,8 +21,8 @@ For example, if you want to subscribe only to links tagged `photography`:
 - Click on the `RSS Feed` button.
 - You are presented with an RSS feed showing only these links. Subscribe to it to receive only updates with this tag.
 - The same method **also works for a full-text search** (_Search_ box) **and for the Picture Wall** (want to only see pictures about `nature`?)
-- You can also build the URLs manually: 
+- You can also build the URLs manually:
     - `https://my.shaarli.domain/?do=rss&searchtags=nature`
-    - `https://my.shaarli.domain/links/?do=picwall&searchterm=poney`
+    - `https://my.shaarli.domain/links/picture-wall?searchterm=poney`
 
 ![](images/rss-filter-1.png) ![](images/rss-filter-2.png)
index 58b92da387732ef330a54e3ba030c6b0a81ee68d..dfdd021e22e35f15b80d37a64fc7e12908384332 100644 (file)
@@ -42,7 +42,7 @@ http://<replace_domain>/?post
 http://<replace_domain>/?do=export
 http://<replace_domain>/?do=import
 http://<replace_domain>/login
-http://<replace_domain>/?do=picwall
+http://<replace_domain>/picture-wall
 http://<replace_domain>/?do=pluginadmin
 http://<replace_domain>/?do=tagcloud
 http://<replace_domain>/?do=taglist
index d3cb33d380b69e69087d1d4c995406f69ae6d718..c639a3bce141b734fa13407fbbfef6b29b249c8f 100644 (file)
--- a/index.php
+++ b/index.php
@@ -610,37 +610,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
 
     // -------- 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 = $bookmarkService->search($_GET);
-        $linksToDisplay = [];
-
-        // Get only bookmarks which have a thumbnail.
-        // Note: we do not retrieve thumbnails here, the request is too heavy.
-        $factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
-        $formatter = $factory->getFormatter();
-        foreach ($links as $key => $link) {
-            if ($link->getThumbnail() !== false) {
-                $linksToDisplay[] = $formatter->format($link);
-            }
-        }
-
-        $data = [
-            'linksToDisplay' => $linksToDisplay,
-        ];
-        $pluginManager->executeHooks('render_picwall', $data, ['loggedin' => $loginManager->isLoggedIn()]);
-
-        foreach ($data as $key => $value) {
-            $PAGE->assign($key, $value);
-        }
-
-        $PAGE->renderPage('picwall');
+        header('Location: ./picture-wall');
         exit;
     }
 
@@ -1944,6 +1914,7 @@ $app->group('/api/v1', function () {
 
 $app->group('', function () {
     $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login');
+    $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall');
 })->add('\Shaarli\Front\ShaarliMiddleware');
 
 $response = $app->run(true);
index 9b97ed6d6f4b71c5b342189f406a7d57c075c637..cc2eb37faa899171a071bdbd16363b2ab1d76df4 100644 (file)
@@ -7,6 +7,7 @@ namespace Shaarli\Container;
 use PHPUnit\Framework\TestCase;
 use Shaarli\Bookmark\BookmarkServiceInterface;
 use Shaarli\Config\ConfigManager;
+use Shaarli\Formatter\FormatterFactory;
 use Shaarli\History;
 use Shaarli\Render\PageBuilder;
 use Shaarli\Security\LoginManager;
@@ -30,7 +31,9 @@ class ContainerBuilderTest extends TestCase
     {
         $this->conf = new ConfigManager('tests/utils/config/configJson');
         $this->sessionManager = $this->createMock(SessionManager::class);
+
         $this->loginManager = $this->createMock(LoginManager::class);
+        $this->loginManager->method('isLoggedIn')->willReturn(true);
 
         $this->containerBuilder = new ContainerBuilder($this->conf, $this->sessionManager, $this->loginManager);
     }
@@ -45,5 +48,6 @@ class ContainerBuilderTest extends TestCase
         static::assertInstanceOf(History::class, $container->history);
         static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
         static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
+        static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
     }
 }
diff --git a/tests/front/controller/PictureWallControllerTest.php b/tests/front/controller/PictureWallControllerTest.php
new file mode 100644 (file)
index 0000000..63802ab
--- /dev/null
@@ -0,0 +1,180 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller;
+
+use PHPUnit\Framework\TestCase;
+use Shaarli\Bookmark\Bookmark;
+use Shaarli\Bookmark\BookmarkServiceInterface;
+use Shaarli\Config\ConfigManager;
+use Shaarli\Container\ShaarliContainer;
+use Shaarli\Formatter\BookmarkFormatter;
+use Shaarli\Formatter\BookmarkRawFormatter;
+use Shaarli\Formatter\FormatterFactory;
+use Shaarli\Front\Exception\ThumbnailsDisabledException;
+use Shaarli\Plugin\PluginManager;
+use Shaarli\Render\PageBuilder;
+use Shaarli\Security\LoginManager;
+use Shaarli\Thumbnailer;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+class PictureWallControllerTest extends TestCase
+{
+    /** @var ShaarliContainer */
+    protected $container;
+
+    /** @var PictureWallController */
+    protected $controller;
+
+    public function setUp(): void
+    {
+        $this->container = $this->createMock(ShaarliContainer::class);
+        $this->controller = new PictureWallController($this->container);
+    }
+
+    public function testValidControllerInvokeDefault(): void
+    {
+        $this->createValidContainerMockSet();
+
+        $request = $this->createMock(Request::class);
+        $request->expects(static::once())->method('getQueryParams')->willReturn([]);
+        $response = new Response();
+
+        // ConfigManager: thumbnails are enabled
+        $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
+            if ($parameter === 'thumbnails.mode') {
+                return Thumbnailer::MODE_COMMON;
+            }
+
+            return $default;
+        });
+
+        // Save RainTPL assigned variables
+        $assignedVariables = [];
+        $this->container->pageBuilder
+            ->expects(static::atLeastOnce())
+            ->method('assign')
+            ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
+                $assignedVariables[$key] = $value;
+
+                return $this;
+            })
+        ;
+
+        // Links dataset: 2 links with thumbnails
+        $this->container->bookmarkService
+            ->expects(static::once())
+            ->method('search')
+            ->willReturnCallback(function (array $parameters, ?string $visibility): array {
+                // Visibility is set through the container, not the call
+                static::assertNull($visibility);
+
+                // No query parameters
+                if (count($parameters) === 0) {
+                    return [
+                        (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'),
+                        (new Bookmark())->setId(2)->setUrl('http://url2.tld'),
+                        (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'),
+                    ];
+                }
+            })
+        ;
+
+        // Make sure that PluginManager hook is triggered
+        $this->container->pluginManager
+            ->expects(static::at(0))
+            ->method('executeHooks')
+            ->willReturnCallback(function (string $hook, array $data, array $param): array {
+                static::assertSame('render_picwall', $hook);
+                static::assertArrayHasKey('linksToDisplay', $data);
+                static::assertCount(2, $data['linksToDisplay']);
+                static::assertSame(1, $data['linksToDisplay'][0]['id']);
+                static::assertSame(3, $data['linksToDisplay'][1]['id']);
+                static::assertArrayHasKey('loggedin', $param);
+
+                return $data;
+            });
+
+        $result = $this->controller->index($request, $response);
+
+        static::assertSame(200, $result->getStatusCode());
+        static::assertSame('picwall', (string) $result->getBody());
+        static::assertSame('Picture wall - Shaarli', $assignedVariables['pagetitle']);
+        static::assertCount(2, $assignedVariables['linksToDisplay']);
+
+        $link = $assignedVariables['linksToDisplay'][0];
+
+        static::assertSame(1, $link['id']);
+        static::assertSame('http://url.tld', $link['url']);
+        static::assertSame('thumb1', $link['thumbnail']);
+
+        $link = $assignedVariables['linksToDisplay'][1];
+
+        static::assertSame(3, $link['id']);
+        static::assertSame('http://url3.tld', $link['url']);
+        static::assertSame('thumb2', $link['thumbnail']);
+    }
+
+    public function testControllerWithThumbnailsDisabled(): void
+    {
+        $this->expectException(ThumbnailsDisabledException::class);
+
+        $this->createValidContainerMockSet();
+
+        $request = $this->createMock(Request::class);
+        $response = new Response();
+
+        // ConfigManager: thumbnails are disabled
+        $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
+            if ($parameter === 'thumbnails.mode') {
+                return Thumbnailer::MODE_NONE;
+            }
+
+            return $default;
+        });
+
+        $this->controller->index($request, $response);
+    }
+
+    protected function createValidContainerMockSet(): void
+    {
+        $loginManager = $this->createMock(LoginManager::class);
+        $this->container->loginManager = $loginManager;
+
+        // Config
+        $conf = $this->createMock(ConfigManager::class);
+        $this->container->conf = $conf;
+
+        // PageBuilder
+        $pageBuilder = $this->createMock(PageBuilder::class);
+        $pageBuilder
+            ->method('render')
+            ->willReturnCallback(function (string $template): string {
+                return $template;
+            })
+        ;
+        $this->container->pageBuilder = $pageBuilder;
+
+        // Plugin Manager
+        $pluginManager = $this->createMock(PluginManager::class);
+        $this->container->pluginManager = $pluginManager;
+
+        // BookmarkService
+        $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
+        $this->container->bookmarkService = $bookmarkService;
+
+        // Formatter
+        $formatterFactory = $this->createMock(FormatterFactory::class);
+        $formatterFactory
+            ->method('getFormatter')
+            ->willReturnCallback(function (string $type): BookmarkFormatter {
+                if ($type === 'raw') {
+                    return new BookmarkRawFormatter($this->container->conf, true);
+                }
+            })
+        ;
+        $this->container->formatterFactory = $formatterFactory;
+    }
+}
index 69e2fcd7644e6da6403fcc4a05e4f8e06751204a..116265a50025224f71dfcfdb524a38f1fd5ca515 100644 (file)
@@ -34,7 +34,7 @@
         </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>
+            <a href="./picture-wall?{function="ltrim($searchcrits, '&')"}" class="pure-menu-link">{'Picture wall'|t}</a>
           </li>
         {/if}
         <li class="pure-menu-item" id="shaarli-menu-daily">
index da5101db644abd18c5996d9ed961d76188520f96..5343abd6a7421131b6f7aca004c171d770d0de79 100644 (file)
@@ -5,58 +5,51 @@
 </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>
+
+{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>
-  {/if}
+  </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" role="list">
-        {loop="$linksToDisplay"}
-          <div class="picwall-pictureframe" role="listitem">
-            {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="" 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="picwall-container" class="picwall-container" role="list">
+      {loop="$linksToDisplay"}
+        <div class="picwall-pictureframe" role="listitem">
+          {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="" 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>
+    <div id="plugin_zone_end_picwall" class="plugin_zone">
+      {loop="$plugin_end_zone"}
+        {$value}
+      {/loop}
     </div>
-    <div class="pure-u-lg-1-6 pure-u-1-24"></div>
   </div>
-{/if}
+  <div class="pure-u-lg-1-6 pure-u-1-24"></div>
+</div>
 
 {include="page.footer"}
 <script src="js/thumbnails.min.js?v={$version_hash}"></script>
index f1869a04ceff494d1cda432bd2773ffe81491bee..ce9384216a739962864bcf3877760945ee484568 100644 (file)
@@ -32,7 +32,7 @@
     <li><a href="{$feedurl}?do=atom{$searchcrits}" class="nomobile">ATOM Feed</a></li>
     {/if}
     <li><a href="./?do=tagcloud">Tag cloud</a></li>
-    <li><a href="./?do=picwall{$searchcrits}">Picture wall</a></li>
+    <li><a href="./picture-wall{function="ltrim($searchcrits, '&')"}">Picture wall</a></li>
     <li><a href="./?do=daily">Daily</a></li>
     {loop="$plugins_header.buttons_toolbar"}
         <li><a