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

View file

@ -99,7 +99,7 @@ public function save(Request $request, Response $response): Response
) {
$this->saveWarningMessage(t(
'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);

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) {
$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) {
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.responseType = 'json';
xhr.onload = () => {
@ -30,14 +30,14 @@ function updateThumb(basePath, ids, i, elements) {
elements.current.innerHTML = i;
elements.title.innerHTML = response.title;
if (response.thumbnail !== false) {
elements.thumbnail.innerHTML = `<img src="${response.thumbnail}">`;
elements.thumbnail.innerHTML = `<img src="${basePath}/${response.thumbnail}">`;
}
if (i < ids.length) {
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
if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
$ids = [];
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');
header('Location: ./admin/thumbnails');
exit;
}
// -------- Single Thumbnail Update
if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) {
http_response_code(400);
exit;
}
$id = (int) $_POST['id'];
if (! $bookmarkService->exists($id)) {
// This route is no longer supported in legacy mode
http_response_code(404);
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:
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/visibility', '\Shaarli\Front\Controller\Admin\ManageShaareController:changeVisibility');
$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->post('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:export');
$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->post('/admin/plugins', '\Shaarli\Front\Controller\Admin\PluginsController:save');
$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('/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">
<label for="titleLink">
<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>
</div>
</div>
@ -289,7 +289,7 @@ <h2 class="window-title">{'Configure'|t}</h2>
{if="! $gd_enabled"}
{'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
{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}
</span>
</label>

View file

@ -140,7 +140,7 @@
<div class="thumbnail">
{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">
<img data-src="{$value.thumbnail}#" class="b-lazy"
<img data-src="{$base_path}/{$value.thumbnail}#" class="b-lazy"
src=""
alt="" width="{$thumbnails_width}" height="{$thumbnails_height}" />
</a>

View file

@ -9,7 +9,7 @@
{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="{$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>
{/if}

View file

@ -47,7 +47,7 @@ <h2 class="window-title">{'Settings'|t}</h2>
{if="$thumbnails_enabled"}
<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>
</a>
</div>

View file

@ -159,7 +159,7 @@
{if="! $gd_enabled"}
{'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
{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}
</label>
</td>