Process thumbnail synchronize page through Slim controllers

This commit is contained in:
ArthurHoaro 2020-06-27 12:08:26 +02:00
parent 764d34a7d3
commit 6132d64748
12 changed files with 253 additions and 40 deletions

View file

@ -4,7 +4,6 @@
use Shaarli\Config\ConfigManager; use Shaarli\Config\ConfigManager;
use WebThumbnailer\Application\ConfigManager as WTConfigManager; use WebThumbnailer\Application\ConfigManager as WTConfigManager;
use WebThumbnailer\Exception\WebThumbnailerException;
use WebThumbnailer\WebThumbnailer; use WebThumbnailer\WebThumbnailer;
/** /**
@ -90,7 +89,7 @@ public function get($url)
try { try {
return $this->wt->thumbnail($url); return $this->wt->thumbnail($url);
} catch (WebThumbnailerException $e) { } catch (\Throwable $e) {
// Exceptions are only thrown in debug mode. // Exceptions are only thrown in debug mode.
error_log(get_class($e) . ': ' . $e->getMessage()); error_log(get_class($e) . ': ' . $e->getMessage());
} }

View file

@ -99,7 +99,7 @@ public function save(Request $request, Response $response): Response
) { ) {
$this->saveWarningMessage(t( $this->saveWarningMessage(t(
'You have enabled or changed thumbnails mode. ' 'You have enabled or changed thumbnails mode. '
.'<a href="./?do=thumbs_update">Please synchronize them</a>.' .'<a href="'. $this->container->basePath .'/admin/thumbnails">Please synchronize them</a>.'
)); ));
} }
$this->container->conf->set('thumbnails.mode', $thumbnailsMode); $this->container->conf->set('thumbnails.mode', $thumbnailsMode);

View file

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class ToolsController
*
* Slim controller used to handle thumbnails update.
*/
class ThumbnailsController extends ShaarliAdminController
{
/**
* GET /admin/thumbnails - Display thumbnails update page
*/
public function index(Request $request, Response $response): Response
{
$ids = [];
foreach ($this->container->bookmarkService->search() as $bookmark) {
// A note or not HTTP(S)
if ($bookmark->isNote() || !startsWith(strtolower($bookmark->getUrl()), 'http')) {
continue;
}
$ids[] = $bookmark->getId();
}
$this->assignView('ids', $ids);
$this->assignView(
'pagetitle',
t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('thumbnails'));
}
/**
* PATCH /admin/shaare/{id}/thumbnail-update - Route for AJAX calls
*/
public function ajaxUpdate(Request $request, Response $response, array $args): Response
{
$id = $args['id'] ?? null;
if (false === ctype_digit($id)) {
return $response->withStatus(400);
}
try {
$bookmark = $this->container->bookmarkService->get($id);
} catch (BookmarkNotFoundException $e) {
return $response->withStatus(404);
}
$bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
$this->container->bookmarkService->set($bookmark);
return $response->withJson($this->container->formatterFactory->getFormatter('raw')->format($bookmark));
}
/**
* @param mixed[] $data Variables passed to the template engine
*
* @return mixed[] Template data after active plugins render_picwall hook execution.
*/
protected function executeHooks(array $data): array
{
$this->container->pluginManager->executeHooks(
'render_tools',
$data
);
return $data;
}
}

View file

@ -534,7 +534,7 @@ public function updateMethodWebThumbnailer()
if ($thumbnailsEnabled) { if ($thumbnailsEnabled) {
$this->session['warnings'][] = t( $this->session['warnings'][] = t(
'You have enabled or changed thumbnails mode. <a href="./?do=thumbs_update">Please synchronize them</a>.' 'You have enabled or changed thumbnails mode. <a href="./admin/thumbnails">Please synchronize them</a>.'
); );
} }

View file

@ -17,7 +17,7 @@
*/ */
function updateThumb(basePath, ids, i, elements) { function updateThumb(basePath, ids, i, elements) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', `${basePath}/?do=ajax_thumb_update`); xhr.open('PATCH', `${basePath}/admin/shaare/${ids[i]}/update-thumbnail`);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.responseType = 'json'; xhr.responseType = 'json';
xhr.onload = () => { xhr.onload = () => {
@ -30,14 +30,14 @@ function updateThumb(basePath, ids, i, elements) {
elements.current.innerHTML = i; elements.current.innerHTML = i;
elements.title.innerHTML = response.title; elements.title.innerHTML = response.title;
if (response.thumbnail !== false) { if (response.thumbnail !== false) {
elements.thumbnail.innerHTML = `<img src="${response.thumbnail}">`; elements.thumbnail.innerHTML = `<img src="${basePath}/${response.thumbnail}">`;
} }
if (i < ids.length) { if (i < ids.length) {
updateThumb(basePath, ids, i, elements); updateThumb(basePath, ids, i, elements);
} }
} }
}; };
xhr.send(`id=${ids[i]}`); xhr.send();
} }
(() => { (() => {

View file

@ -603,40 +603,16 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
// -------- Thumbnails Update // -------- Thumbnails Update
if ($targetPage == Router::$PAGE_THUMBS_UPDATE) { if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
$ids = []; header('Location: ./admin/thumbnails');
foreach ($bookmarkService->search() as $bookmark) {
// A note or not HTTP(S)
if ($bookmark->isNote() || ! startsWith(strtolower($bookmark->getUrl()), 'http')) {
continue;
}
$ids[] = $bookmark->getId();
}
$PAGE->assign('ids', $ids);
$PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli'));
$PAGE->renderPage('thumbnails');
exit; exit;
} }
// -------- Single Thumbnail Update // -------- Single Thumbnail Update
if ($targetPage == Router::$AJAX_THUMB_UPDATE) { if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) { // This route is no longer supported in legacy mode
http_response_code(400);
exit;
}
$id = (int) $_POST['id'];
if (! $bookmarkService->exists($id)) {
http_response_code(404); http_response_code(404);
exit; exit;
} }
$thumbnailer = new Thumbnailer($conf);
$bookmark = $bookmarkService->get($id);
$bookmark->setThumbnail($thumbnailer->get($bookmark->getUrl()));
$bookmarkService->set($bookmark);
$factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
echo json_encode($factory->getFormatter('raw')->format($bookmark));
exit;
}
// -------- Otherwise, simply display search form and bookmarks: // -------- Otherwise, simply display search form and bookmarks:
showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager); showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
@ -971,6 +947,10 @@ function install($conf, $sessionManager, $loginManager)
$this->get('/admin/shaare/delete', '\Shaarli\Front\Controller\Admin\ManageShaareController:deleteBookmark'); $this->get('/admin/shaare/delete', '\Shaarli\Front\Controller\Admin\ManageShaareController:deleteBookmark');
$this->get('/admin/shaare/visibility', '\Shaarli\Front\Controller\Admin\ManageShaareController:changeVisibility'); $this->get('/admin/shaare/visibility', '\Shaarli\Front\Controller\Admin\ManageShaareController:changeVisibility');
$this->get('/admin/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ManageShaareController:pinBookmark'); $this->get('/admin/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ManageShaareController:pinBookmark');
$this->patch(
'/admin/shaare/{id:[0-9]+}/update-thumbnail',
'\Shaarli\Front\Controller\Admin\ThumbnailsController:ajaxUpdate'
);
$this->get('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:index'); $this->get('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:index');
$this->post('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:export'); $this->post('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:export');
$this->get('/admin/import', '\Shaarli\Front\Controller\Admin\ImportController:index'); $this->get('/admin/import', '\Shaarli\Front\Controller\Admin\ImportController:index');
@ -978,6 +958,7 @@ function install($conf, $sessionManager, $loginManager)
$this->get('/admin/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:index'); $this->get('/admin/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:index');
$this->post('/admin/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save'); $this->post('/admin/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save');
$this->get('/admin/token', '\Shaarli\Front\Controller\Admin\TokenController:getToken'); $this->get('/admin/token', '\Shaarli\Front\Controller\Admin\TokenController:getToken');
$this->get('/admin/thumbnails', '\Shaarli\Front\Controller\Admin\ThumbnailsController:index');
$this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage'); $this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage');
$this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility'); $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility');

View file

@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class ThumbnailsControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
/** @var ThumbnailsController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new ThumbnailsController($this->container);
}
/**
* Test displaying the thumbnails update page
* Note that only non-note and HTTP bookmarks should be returned.
*/
public function testIndex(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->willReturn([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('?abcdef')->setTitle('Note 1'),
(new Bookmark())->setId(3)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(4)->setUrl('ftp://domain.tld', ['ftp'])->setTitle('FTP'),
])
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('thumbnails', (string) $result->getBody());
static::assertSame('Thumbnails update - Shaarli', $assignedVariables['pagetitle']);
static::assertSame([1, 3], $assignedVariables['ids']);
}
/**
* Test updating a bookmark thumbnail with valid parameters
*/
public function testAjaxUpdateValid(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$bookmark = (new Bookmark())
->setId($id = 123)
->setUrl($url = 'http://url1.tld')
->setTitle('Title 1')
->setThumbnail(false)
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer
->expects(static::once())
->method('get')
->with($url)
->willReturn($thumb = 'http://img.tld/pic.png')
;
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willReturn($bookmark)
;
$this->container->bookmarkService
->expects(static::once())
->method('set')
->willReturnCallback(function (Bookmark $bookmark) use ($thumb) {
static::assertSame($thumb, $bookmark->getThumbnail());
})
;
$result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
static::assertSame(200, $result->getStatusCode());
$payload = json_decode((string) $result->getBody(), true);
static::assertSame($id, $payload['id']);
static::assertSame($url, $payload['url']);
static::assertSame($thumb, $payload['thumbnail']);
}
/**
* Test updating a bookmark thumbnail - Invalid ID
*/
public function testAjaxUpdateInvalidId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->ajaxUpdate($request, $response, ['id' => 'nope']);
static::assertSame(400, $result->getStatusCode());
}
/**
* Test updating a bookmark thumbnail - No ID
*/
public function testAjaxUpdateNoId(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->ajaxUpdate($request, $response, []);
static::assertSame(400, $result->getStatusCode());
}
/**
* Test updating a bookmark thumbnail with valid parameters
*/
public function testAjaxUpdateBookmarkNotFound(): void
{
$id = 123;
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('get')
->with($id)
->willThrowException(new BookmarkNotFoundException())
;
$result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
static::assertSame(404, $result->getStatusCode());
}
}

View file

@ -35,7 +35,7 @@ <h2 class="window-title">{'Configure'|t}</h2>
<div class="form-label"> <div class="form-label">
<label for="titleLink"> <label for="titleLink">
<span class="label-name">{'Home link'|t}</span><br> <span class="label-name">{'Home link'|t}</span><br>
<span class="label-desc">{'Default value'|t}: {$base_path}</span> <span class="label-desc">{'Default value'|t}: {$base_path}/</span>
</label> </label>
</div> </div>
</div> </div>
@ -289,7 +289,7 @@ <h2 class="window-title">{'Configure'|t}</h2>
{if="! $gd_enabled"} {if="! $gd_enabled"}
{'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t} {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
{elseif="$thumbnails_enabled"} {elseif="$thumbnails_enabled"}
<a href="{$base_path}/?do=thumbs_update">{'Synchronize thumbnails'|t}</a> <a href="{$base_path}/admin/thumbnails">{'Synchronize thumbnails'|t}</a>
{/if} {/if}
</span> </span>
</label> </label>

View file

@ -140,7 +140,7 @@
<div class="thumbnail"> <div class="thumbnail">
{ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore} {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
<a href="{$value.real_url}" aria-hidden="true" tabindex="-1"> <a href="{$value.real_url}" aria-hidden="true" tabindex="-1">
<img data-src="{$value.thumbnail}#" class="b-lazy" <img data-src="{$base_path}/{$value.thumbnail}#" class="b-lazy"
src="" src=""
alt="" width="{$thumbnails_width}" height="{$thumbnails_height}" /> alt="" width="{$thumbnails_width}" height="{$thumbnails_height}" />
</a> </a>

View file

@ -9,7 +9,7 @@
{if="count($linksToDisplay)===0 && $is_logged_in"} {if="count($linksToDisplay)===0 && $is_logged_in"}
<div class="pure-g pure-alert pure-alert-warning page-single-alert"> <div class="pure-g pure-alert pure-alert-warning page-single-alert">
<div class="pure-u-1 center"> <div class="pure-u-1 center">
{'There is no cached thumbnail. Try to <a href="{$base_path}/do=thumbs_update">synchronize them</a>.'|t} {'There is no cached thumbnail. Try to <a href="{$base_path}/admin/thumbnails">synchronize them</a>.'|t}
</div> </div>
</div> </div>
{/if} {/if}

View file

@ -47,7 +47,7 @@ <h2 class="window-title">{'Settings'|t}</h2>
{if="$thumbnails_enabled"} {if="$thumbnails_enabled"}
<div class="tools-item"> <div class="tools-item">
<a href="{$base_path}/?do=thumbs_update" title="{'Synchronize all link thumbnails'|t}"> <a href="{$base_path}/admin/thumbnails" title="{'Synchronize all link thumbnails'|t}">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span> <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
</a> </a>
</div> </div>

View file

@ -159,7 +159,7 @@
{if="! $gd_enabled"} {if="! $gd_enabled"}
{'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t} {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
{elseif="$thumbnails_enabled"} {elseif="$thumbnails_enabled"}
<a href="{$base_path}/?do=thumbs_update">{'Synchonize thumbnails'|t}</a> <a href="{$base_path}/admin/thumbnails">{'Synchonize thumbnails'|t}</a>
{/if} {/if}
</label> </label>
</td> </td>