use Shaarli\Config\ConfigManager;
use WebThumbnailer\Application\ConfigManager as WTConfigManager;
-use WebThumbnailer\Exception\WebThumbnailerException;
use WebThumbnailer\WebThumbnailer;
/**
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());
}
) {
$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);
--- /dev/null
+<?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;
+ }
+}
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>.'
);
}
*/
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 = () => {
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();
}
(() => {
// -------- 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)) {
- 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));
+ // This route is no longer supported in legacy mode
+ http_response_code(404);
exit;
}
$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');
$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');
--- /dev/null
+<?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());
+ }
+}
<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>
{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>
<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>
{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}
{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>
{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>