Process main page (linklist) through Slim controller

Including a bunch of improvements on the container,
and helper used across new controllers.
This commit is contained in:
ArthurHoaro 2020-07-06 08:04:35 +02:00
parent 6132d64748
commit 1a8ac737e5
44 changed files with 1459 additions and 747 deletions

View file

@ -71,7 +71,14 @@ class ApiMiddleware
$response = $e->getApiResponse();
}
return $response;
return $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader(
'Access-Control-Allow-Headers',
'X-Requested-With, Content-Type, Accept, Origin, Authorization'
)
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
;
}
/**

View file

@ -93,7 +93,7 @@ class BookmarkFileService implements BookmarkServiceInterface
throw new Exception('Not authorized');
}
return $bookmark;
return $first;
}
/**

View file

@ -18,6 +18,8 @@ use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
use Shaarli\Updater\Updater;
use Shaarli\Updater\UpdaterUtils;
/**
* Class ContainerBuilder
@ -128,6 +130,15 @@ class ContainerBuilder
return new NetscapeBookmarkUtils($container->bookmarkService, $container->conf, $container->history);
};
$container['updater'] = function (ShaarliContainer $container): Updater {
return new Updater(
UpdaterUtils::read_updates_file($container->conf->get('resource.updates')),
$container->bookmarkService,
$container->conf,
$container->loginManager->isLoggedIn()
);
};
return $container;
}
}

View file

@ -17,15 +17,17 @@ use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
use Shaarli\Updater\Updater;
use Slim\Container;
/**
* Extension of Slim container to document the injected objects.
*
* @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
* @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
* @property BookmarkServiceInterface $bookmarkService
* @property ConfigManager $conf
* @property mixed[] $environment $_SERVER automatically injected by Slim
* @property mixed[] $environment $_SERVER automatically injected by Slim
* @property callable $errorHandler Overrides default Slim error display
* @property FeedBuilder $feedBuilder
* @property FormatterFactory $formatterFactory
* @property History $history
@ -37,6 +39,7 @@ use Slim\Container;
* @property PluginManager $pluginManager
* @property SessionManager $sessionManager
* @property Thumbnailer $thumbnailer
* @property Updater $updater
*/
class ShaarliContainer extends Container
{

View file

@ -25,6 +25,8 @@ class ShaarliMiddleware
/**
* Middleware execution:
* - run updates
* - if not logged in open shaarli, redirect to login
* - execute the controller
* - return the response
*
@ -36,27 +38,82 @@ class ShaarliMiddleware
*
* @return Response response.
*/
public function __invoke(Request $request, Response $response, callable $next)
public function __invoke(Request $request, Response $response, callable $next): Response
{
$this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
try {
$response = $next($request, $response);
$this->runUpdates();
$this->checkOpenShaarli($request, $response, $next);
return $next($request, $response);
} catch (ShaarliFrontException $e) {
// Possible functional error
$this->container->pageBuilder->reset();
$this->container->pageBuilder->assign('message', $e->getMessage());
if ($this->container->conf->get('dev.debug', false)) {
$this->container->pageBuilder->assign(
'stacktrace',
nl2br(get_class($this) .': '. $e->getTraceAsString())
);
}
$response = $response->withStatus($e->getCode());
$response = $response->write($this->container->pageBuilder->render('error'));
return $response->write($this->container->pageBuilder->render('error'));
} catch (UnauthorizedException $e) {
return $response->withRedirect($this->container->basePath . '/login');
} catch (\Throwable $e) {
// Unknown error encountered
$this->container->pageBuilder->reset();
if ($this->container->conf->get('dev.debug', false)) {
$this->container->pageBuilder->assign('message', $e->getMessage());
$this->container->pageBuilder->assign(
'stacktrace',
nl2br(get_class($e) .': '. PHP_EOL . $e->getTraceAsString())
);
} else {
$this->container->pageBuilder->assign('message', t('An unexpected error occurred.'));
}
$response = $response->withStatus(500);
return $response->write($this->container->pageBuilder->render('error'));
}
}
/**
* Run the updater for every requests processed while logged in.
*/
protected function runUpdates(): void
{
if ($this->container->loginManager->isLoggedIn() !== true) {
return;
}
return $response;
$newUpdates = $this->container->updater->update();
if (!empty($newUpdates)) {
$this->container->updater->writeUpdates(
$this->container->conf->get('resource.updates'),
$this->container->updater->getDoneUpdates()
);
$this->container->pageCacheManager->invalidateCaches();
}
}
/**
* Access is denied to most pages with `hide_public_links` + `force_login` settings.
*/
protected function checkOpenShaarli(Request $request, Response $response, callable $next): bool
{
if (// if the user isn't logged in
!$this->container->loginManager->isLoggedIn()
// and Shaarli doesn't have public content...
&& $this->container->conf->get('privacy.hide_public_links')
// and is configured to enforce the login
&& $this->container->conf->get('privacy.force_login')
// and the current page isn't already the login page
// and the user is not requesting a feed (which would lead to a different content-type as expected)
&& !in_array($next->getName(), ['login', 'atom', 'rss'], true)
) {
throw new UnauthorizedException();
}
return true;
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Languages;
use Shaarli\Render\TemplatePage;
use Shaarli\Render\ThemeUtils;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
@ -52,7 +53,7 @@ class ConfigureController extends ShaarliAdminController
$this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE));
$this->assignView('pagetitle', t('Configure') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
return $response->write($this->render('configure'));
return $response->write($this->render(TemplatePage::CONFIGURE));
}
/**

View file

@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin;
use DateTime;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -24,7 +25,7 @@ class ExportController extends ShaarliAdminController
{
$this->assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
return $response->write($this->render('export'));
return $response->write($this->render(TemplatePage::EXPORT));
}
/**
@ -74,6 +75,6 @@ class ExportController extends ShaarliAdminController
$this->assignView('eol', PHP_EOL);
$this->assignView('selection', $selection);
return $response->write($this->render('export.bookmarks'));
return $response->write($this->render(TemplatePage::NETSCAPE_EXPORT_BOOKMARKS));
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Psr\Http\Message\UploadedFileInterface;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -39,7 +40,7 @@ class ImportController extends ShaarliAdminController
);
$this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
return $response->write($this->render('import'));
return $response->write($this->render(TemplatePage::IMPORT));
}
/**

View file

@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Formatter\BookmarkMarkdownFormatter;
use Shaarli\Render\TemplatePage;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
@ -28,7 +29,7 @@ class ManageShaareController extends ShaarliAdminController
t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('addlink'));
return $response->write($this->render(TemplatePage::ADDLINK));
}
/**
@ -365,7 +366,7 @@ class ManageShaareController extends ShaarliAdminController
$editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('editlink'));
return $response->write($this->render(TemplatePage::EDIT_LINK));
}
/**

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\BookmarkFilter;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -28,7 +29,7 @@ class ManageTagController extends ShaarliAdminController
t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('changetag'));
return $response->write($this->render(TemplatePage::CHANGE_TAG));
}
/**

View file

@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin;
use Shaarli\Container\ShaarliContainer;
use Shaarli\Front\Exception\OpenShaarliPasswordException;
use Shaarli\Front\Exception\ShaarliFrontException;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
use Throwable;
@ -33,7 +34,7 @@ class PasswordController extends ShaarliAdminController
*/
public function index(Request $request, Response $response): Response
{
return $response->write($this->render('changepassword'));
return $response->write($this->render(TemplatePage::CHANGE_PASSWORD));
}
/**
@ -55,7 +56,7 @@ class PasswordController extends ShaarliAdminController
return $response
->withStatus(400)
->write($this->render('changepassword'))
->write($this->render(TemplatePage::CHANGE_PASSWORD))
;
}
@ -71,7 +72,7 @@ class PasswordController extends ShaarliAdminController
return $response
->withStatus(400)
->write($this->render('changepassword'))
->write($this->render(TemplatePage::CHANGE_PASSWORD))
;
}
@ -95,6 +96,6 @@ class PasswordController extends ShaarliAdminController
$this->saveSuccessMessage(t('Your password has been changed'));
return $response->write($this->render('changepassword'));
return $response->write($this->render(TemplatePage::CHANGE_PASSWORD));
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Exception;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -44,7 +45,7 @@ class PluginsController extends ShaarliAdminController
t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('pluginsadmin'));
return $response->write($this->render(TemplatePage::PLUGINS_ADMIN));
}
/**

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -36,7 +37,7 @@ class ThumbnailsController extends ShaarliAdminController
t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli')
);
return $response->write($this->render('thumbnails'));
return $response->write($this->render(TemplatePage::THUMBNAILS));
}
/**
@ -61,19 +62,4 @@ class ThumbnailsController extends ShaarliAdminController
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

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Admin;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -29,7 +30,7 @@ class ToolsController extends ShaarliAdminController
$this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
return $response->write($this->render('tools'));
return $response->write($this->render(TemplatePage::TOOLS));
}
/**

View file

@ -0,0 +1,248 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Legacy\LegacyController;
use Shaarli\Legacy\UnknowLegacyRouteException;
use Shaarli\Render\TemplatePage;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class BookmarkListController
*
* Slim controller used to render the bookmark list, the home page of Shaarli.
* It also displays permalinks, and process legacy routes based on GET parameters.
*/
class BookmarkListController extends ShaarliVisitorController
{
/**
* GET / - Displays the bookmark list, with optional filter parameters.
*/
public function index(Request $request, Response $response): Response
{
$legacyResponse = $this->processLegacyController($request, $response);
if (null !== $legacyResponse) {
return $legacyResponse;
}
$formatter = $this->container->formatterFactory->getFormatter();
$searchTags = escape(normalize_spaces($request->getParam('searchtags') ?? ''));
$searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));;
// Filter bookmarks according search parameters.
$visibility = $this->container->sessionManager->getSessionParameter('visibility');
$search = [
'searchtags' => $searchTags,
'searchterm' => $searchTerm,
];
$linksToDisplay = $this->container->bookmarkService->search(
$search,
$visibility,
false,
!!$this->container->sessionManager->getSessionParameter('untaggedonly')
) ?? [];
// ---- Handle paging.
$keys = [];
foreach ($linksToDisplay as $key => $value) {
$keys[] = $key;
}
$linksPerPage = $this->container->sessionManager->getSessionParameter('LINKS_PER_PAGE', 20) ?: 20;
// Select articles according to paging.
$pageCount = (int) ceil(count($keys) / $linksPerPage) ?: 1;
$page = (int) $request->getParam('page') ?? 1;
$page = $page < 1 ? 1 : $page;
$page = $page > $pageCount ? $pageCount : $page;
// Start index.
$i = ($page - 1) * $linksPerPage;
$end = $i + $linksPerPage;
$linkDisp = [];
$save = false;
while ($i < $end && $i < count($keys)) {
$save = $this->updateThumbnail($linksToDisplay[$keys[$i]], false) || $save;
$link = $formatter->format($linksToDisplay[$keys[$i]]);
$linkDisp[$keys[$i]] = $link;
$i++;
}
if ($save) {
$this->container->bookmarkService->save();
}
// Compute paging navigation
$searchtagsUrl = $searchTags === '' ? '' : '&searchtags=' . urlencode($searchTags);
$searchtermUrl = $searchTerm === '' ? '' : '&searchterm=' . urlencode($searchTerm);
$previous_page_url = '';
if ($i !== count($keys)) {
$previous_page_url = '?page=' . ($page + 1) . $searchtermUrl . $searchtagsUrl;
}
$next_page_url = '';
if ($page > 1) {
$next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl;
}
// Fill all template fields.
$data = array_merge(
$this->initializeTemplateVars(),
[
'previous_page_url' => $previous_page_url,
'next_page_url' => $next_page_url,
'page_current' => $page,
'page_max' => $pageCount,
'result_count' => count($linksToDisplay),
'search_term' => $searchTerm,
'search_tags' => $searchTags,
'visibility' => $visibility,
'links' => $linkDisp,
]
);
if (!empty($searchTerm) || !empty($searchTags)) {
$data['pagetitle'] = t('Search: ');
$data['pagetitle'] .= ! empty($searchTerm) ? $searchTerm . ' ' : '';
$bracketWrap = function ($tag) {
return '[' . $tag . ']';
};
$data['pagetitle'] .= ! empty($searchTags)
? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchTags))) . ' '
: '';
$data['pagetitle'] .= '- ';
}
$data['pagetitle'] = ($data['pagetitle'] ?? '') . $this->container->conf->get('general.title', 'Shaarli');
$this->executeHooks($data);
$this->assignAllView($data);
return $response->write($this->render(TemplatePage::LINKLIST));
}
/**
* GET /shaare/{hash} - Display a single shaare
*/
public function permalink(Request $request, Response $response, array $args): Response
{
try {
$bookmark = $this->container->bookmarkService->findByHash($args['hash']);
} catch (BookmarkNotFoundException $e) {
$this->assignView('error_message', $e->getMessage());
return $response->write($this->render(TemplatePage::ERROR_404));
}
$this->updateThumbnail($bookmark);
$data = array_merge(
$this->initializeTemplateVars(),
[
'pagetitle' => $bookmark->getTitle() .' - '. $this->container->conf->get('general.title', 'Shaarli'),
'links' => [$this->container->formatterFactory->getFormatter()->format($bookmark)],
]
);
$this->executeHooks($data);
$this->assignAllView($data);
return $response->write($this->render(TemplatePage::LINKLIST));
}
/**
* Update the thumbnail of a single bookmark if necessary.
*/
protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool
{
// Logged in, thumbnails enabled, not a note, is HTTP
// and (never retrieved yet or no valid cache file)
if ($this->container->loginManager->isLoggedIn()
&& $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
&& false !== $bookmark->getThumbnail()
&& !$bookmark->isNote()
&& (null === $bookmark->getThumbnail() || !is_file($bookmark->getThumbnail()))
&& startsWith(strtolower($bookmark->getUrl()), 'http')
) {
$bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
$this->container->bookmarkService->set($bookmark, $writeDatastore);
return true;
}
return false;
}
/**
* @param mixed[] $data Template vars to process in plugins, passed as reference.
*/
protected function executeHooks(array &$data): void
{
$this->container->pluginManager->executeHooks(
'render_linklist',
$data,
['loggedin' => $this->container->loginManager->isLoggedIn()]
);
}
/**
* @return string[] Default template variables without values.
*/
protected function initializeTemplateVars(): array
{
return [
'previous_page_url' => '',
'next_page_url' => '',
'page_max' => '',
'search_tags' => '',
'result_count' => '',
];
}
/**
* Process legacy routes if necessary. They used query parameters.
* If no legacy routes is passed, return null.
*/
protected function processLegacyController(Request $request, Response $response): ?Response
{
// Legacy smallhash filter
$queryString = $this->container->environment['QUERY_STRING'] ?? null;
if (null !== $queryString && 1 === preg_match('/^([a-zA-Z0-9-_@]{6})($|&|#)/', $queryString, $match)) {
return $this->redirect($response, '/shaare/' . $match[1]);
}
// Legacy controllers (mostly used for redirections)
if (null !== $request->getQueryParam('do')) {
$legacyController = new LegacyController($this->container);
try {
return $legacyController->process($request, $response, $request->getQueryParam('do'));
} catch (UnknowLegacyRouteException $e) {
// We ignore legacy 404
return null;
}
}
// Legacy GET admin routes
$legacyGetRoutes = array_intersect(
LegacyController::LEGACY_GET_ROUTES,
array_keys($request->getQueryParams() ?? [])
);
if (1 === count($legacyGetRoutes)) {
$legacyController = new LegacyController($this->container);
return $legacyController->process($request, $response, $legacyGetRoutes[0]);
}
return null;
}
}

View file

@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Visitor;
use DateTime;
use DateTimeImmutable;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -85,7 +86,7 @@ class DailyController extends ShaarliVisitorController
t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle
);
return $response->write($this->render('daily'));
return $response->write($this->render(TemplatePage::DAILY));
}
/**
@ -152,7 +153,7 @@ class DailyController extends ShaarliVisitorController
$this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false));
$this->assignView('days', $dataPerDay);
$rssContent = $this->render('dailyrss');
$rssContent = $this->render(TemplatePage::DAILY_RSS);
$cache->cache($rssContent);

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Front\Exception\LoginBannedException;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -41,6 +42,6 @@ class LoginController extends ShaarliVisitorController
->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli'))
;
return $response->write($this->render('loginform'));
return $response->write($this->render(TemplatePage::LOGIN));
}
}

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Render\TemplatePage;
use Slim\Http\Request;
use Slim\Http\Response;
@ -21,6 +22,6 @@ class OpenSearchController extends ShaarliVisitorController
$this->assignView('serverurl', index_url($this->container->environment));
return $response->write($this->render('opensearch'));
return $response->write($this->render(TemplatePage::OPEN_SEARCH));
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Front\Exception\ThumbnailsDisabledException;
use Shaarli\Render\TemplatePage;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
@ -46,7 +47,7 @@ class PictureWallController extends ShaarliVisitorController
$this->assignView($key, $value);
}
return $response->write($this->render('picwall'));
return $response->write($this->render(TemplatePage::PICTURE_WALL));
}
/**

View file

@ -0,0 +1,130 @@
<?php
declare(strict_types=1);
namespace Shaarli\Legacy;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Front\Controller\Visitor\ShaarliVisitorController;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* We use this to maintain legacy routes, and redirect requests to the corresponding Slim route.
* Only public routes, and both `?addlink` and `?post` were kept here.
* Other routes will just display the linklist.
*
* @deprecated
*/
class LegacyController extends ShaarliVisitorController
{
/** @var string[] Both `?post` and `?addlink` do not use `?do=` format. */
public const LEGACY_GET_ROUTES = [
'post',
'addlink',
];
/**
* This method will call `$action` method, which will redirect to corresponding Slim route.
*/
public function process(Request $request, Response $response, string $action): Response
{
if (!method_exists($this, $action)) {
throw new UnknowLegacyRouteException();
}
return $this->{$action}($request, $response);
}
/** Legacy route: ?post= */
public function post(Request $request, Response $response): Response
{
$parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
if (!$this->container->loginManager->isLoggedIn()) {
return $this->redirect($response, '/login' . $parameters);
}
return $this->redirect($response, '/admin/shaare' . $parameters);
}
/** Legacy route: ?addlink= */
protected function addlink(Request $request, Response $response): Response
{
if (!$this->container->loginManager->isLoggedIn()) {
return $this->redirect($response, '/login');
}
return $this->redirect($response, '/admin/add-shaare');
}
/** Legacy route: ?do=login */
protected function login(Request $request, Response $response): Response
{
return $this->redirect($response, '/login');
}
/** Legacy route: ?do=logout */
protected function logout(Request $request, Response $response): Response
{
return $this->redirect($response, '/logout');
}
/** Legacy route: ?do=picwall */
protected function picwall(Request $request, Response $response): Response
{
return $this->redirect($response, '/picture-wall');
}
/** Legacy route: ?do=tagcloud */
protected function tagcloud(Request $request, Response $response): Response
{
return $this->redirect($response, '/tags/cloud');
}
/** Legacy route: ?do=taglist */
protected function taglist(Request $request, Response $response): Response
{
return $this->redirect($response, '/tags/list');
}
/** Legacy route: ?do=daily */
protected function daily(Request $request, Response $response): Response
{
$dayParam = !empty($request->getParam('day')) ? '?day=' . escape($request->getParam('day')) : '';
return $this->redirect($response, '/daily' . $dayParam);
}
/** Legacy route: ?do=rss */
protected function rss(Request $request, Response $response): Response
{
return $this->feed($request, $response, FeedBuilder::$FEED_RSS);
}
/** Legacy route: ?do=atom */
protected function atom(Request $request, Response $response): Response
{
return $this->feed($request, $response, FeedBuilder::$FEED_ATOM);
}
/** Legacy route: ?do=opensearch */
protected function opensearch(Request $request, Response $response): Response
{
return $this->redirect($response, '/open-search');
}
/** Legacy route: ?do=dailyrss */
protected function dailyrss(Request $request, Response $response): Response
{
return $this->redirect($response, '/daily-rss');
}
/** Legacy route: ?do=feed */
protected function feed(Request $request, Response $response, string $feedType): Response
{
$parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
return $this->redirect($response, '/feed/' . $feedType . $parameters);
}
}

View file

@ -1,12 +1,15 @@
<?php
namespace Shaarli;
namespace Shaarli\Legacy;
/**
* Class Router
*
* (only displayable pages here)
*
* @deprecated
*/
class Router
class LegacyRouter
{
public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';

View file

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Shaarli\Legacy;
class UnknowLegacyRouteException extends \Exception
{
}

View file

@ -69,6 +69,15 @@ class PageBuilder
$this->isLoggedIn = $isLoggedIn;
}
/**
* Reset current state of template rendering.
* Mostly useful for error handling. We remove everything, and display the error template.
*/
public function reset(): void
{
$this->tpl = false;
}
/**
* Initialize all default tpl tags.
*/

View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Shaarli\Render;
interface TemplatePage
{
public const ERROR_404 = '404';
public const ADDLINK = 'addlink';
public const CHANGE_PASSWORD = 'changepassword';
public const CHANGE_TAG = 'changetag';
public const CONFIGURE = 'configure';
public const DAILY = 'daily';
public const DAILY_RSS = 'dailyrss';
public const EDIT_LINK = 'editlink';
public const ERROR = 'error';
public const EXPORT = 'export';
public const NETSCAPE_EXPORT_BOOKMARKS = 'export.bookmarks';
public const FEED_ATOM = 'feed.atom';
public const FEED_RSS = 'feed.rss';
public const IMPORT = 'import';
public const INSTALL = 'install';
public const LINKLIST = 'linklist';
public const LOGIN = 'loginform';
public const OPEN_SEARCH = 'opensearch';
public const PICTURE_WALL = 'picwall';
public const PLUGINS_ADMIN = 'pluginsadmin';
public const TAG_CLOUD = 'tag.cloud';
public const TAG_LIST = 'tag.list';
public const THUMBNAILS = 'thumbnails';
public const TOOLS = 'tools';
}

View file

@ -21,7 +21,7 @@ class Updater
/**
* @var BookmarkServiceInterface instance.
*/
protected $linkServices;
protected $bookmarkService;
/**
* @var ConfigManager $conf Configuration Manager instance.
@ -49,7 +49,7 @@ class Updater
public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
{
$this->doneUpdates = $doneUpdates;
$this->linkServices = $linkDB;
$this->bookmarkService = $linkDB;
$this->conf = $conf;
$this->isLoggedIn = $isLoggedIn;
@ -68,7 +68,7 @@ class Updater
*/
public function update()
{
$updatesRan = array();
$updatesRan = [];
// If the user isn't logged in, exit without updating.
if ($this->isLoggedIn !== true) {
@ -112,6 +112,16 @@ class Updater
return $this->doneUpdates;
}
public function readUpdates(string $updatesFilepath): array
{
return UpdaterUtils::read_updates_file($updatesFilepath);
}
public function writeUpdates(string $updatesFilepath, array $updates): void
{
UpdaterUtils::write_updates_file($updatesFilepath, $updates);
}
/**
* 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 `/`.
@ -127,4 +137,31 @@ class Updater
return true;
}
/**
* With the Slim routing system, note bookmarks URL formatted `?abcdef`
* should be replaced with `/shaare/abcdef`
*/
public function updateMethodMigrateExistingNotesUrl(): bool
{
$updated = false;
foreach ($this->bookmarkService->search() as $bookmark) {
if ($bookmark->isNote()
&& startsWith($bookmark->getUrl(), '?')
&& 1 === preg_match('/^\?([a-zA-Z0-9-_@]{6})($|&|#)/', $bookmark->getUrl(), $match)
) {
$updated = true;
$bookmark = $bookmark->setUrl('/shaare/' . $match[1]);
$this->bookmarkService->set($bookmark, false);
}
}
if ($updated) {
$this->bookmarkService->save();
}
return true;
}
}

View file

@ -131,7 +131,7 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha
| ------------- |:-------------:|
| [render_header](#render_header) | Allow plugin to add content in page headers. |
| [render_includes](#render_includes) | Allow plugin to include their own CSS files. |
| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
| [render_linklist](#render_linklist) | It allows to add content at the begining and end of the page, after every link displayed and to alter link data. |
| [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |
| [render_tools](#render_tools) | Allow to add content at the end of the page. |
@ -515,7 +515,7 @@ Otherwise, you can use your own JS as long as this field is send by the form:
### Placeholder system
In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
It's a RainTPL loop like this:
@ -537,7 +537,7 @@ At the end of the menu:
At the end of file, before clearing floating blocks:
{if="!empty($plugin_errors) && isLoggedIn()"}
{if="!empty($plugin_errors) && $is_logged_in"}
<ul class="errors">
{loop="plugin_errors"}
<li>{$value}</li>

534
index.php
View file

@ -61,30 +61,15 @@ require_once 'application/TimeZone.php';
require_once 'application/Utils.php';
use Shaarli\ApplicationUtils;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\BookmarkFilter;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ContainerBuilder;
use Shaarli\Feed\CachedPage;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Formatter\BookmarkMarkdownFormatter;
use Shaarli\Formatter\FormatterFactory;
use Shaarli\History;
use Shaarli\Languages;
use Shaarli\Netscape\NetscapeBookmarkUtils;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Render\ThemeUtils;
use Shaarli\Router;
use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
use Shaarli\Updater\Updater;
use Shaarli\Updater\UpdaterUtils;
use Slim\App;
// Ensure the PHP version is supported
@ -196,20 +181,6 @@ if (! is_file($conf->getConfigFileExt())) {
$loginManager->checkLoginState($_COOKIE, $clientIpId);
/**
* Adapter function to ensure compatibility with third-party templates
*
* @see https://github.com/shaarli/Shaarli/pull/1086
*
* @return bool true when the user is logged in, false otherwise
*/
function isLoggedIn()
{
global $loginManager;
return $loginManager->isLoggedIn();
}
// ------------------------------------------------------------------------------------------
// Process login form: Check if login/password is correct.
if (isset($_POST['login'])) {
@ -300,473 +271,6 @@ if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens']=array(); // Token are attached to the session.
}
/**
* Renders the linklist
*
* @param pageBuilder $PAGE pageBuilder instance.
* @param BookmarkServiceInterface $linkDb instance.
* @param ConfigManager $conf Configuration Manager instance.
* @param PluginManager $pluginManager Plugin Manager instance.
*/
function showLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
{
buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager);
$PAGE->renderPage('linklist');
}
/**
* Render HTML page (according to URL parameters and user rights)
*
* @param ConfigManager $conf Configuration Manager instance.
* @param PluginManager $pluginManager Plugin Manager instance,
* @param BookmarkServiceInterface $bookmarkService
* @param History $history instance
* @param SessionManager $sessionManager SessionManager instance
* @param LoginManager $loginManager LoginManager instance
*/
function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionManager, $loginManager)
{
$pageCacheManager = new PageCacheManager($conf->get('resource.page_cache'), $loginManager->isLoggedIn());
$updater = new Updater(
UpdaterUtils::read_updates_file($conf->get('resource.updates')),
$bookmarkService,
$conf,
$loginManager->isLoggedIn()
);
try {
$newUpdates = $updater->update();
if (! empty($newUpdates)) {
UpdaterUtils::write_updates_file(
$conf->get('resource.updates'),
$updater->getDoneUpdates()
);
$pageCacheManager->invalidateCaches();
}
} catch (Exception $e) {
die($e->getMessage());
}
$PAGE = new PageBuilder($conf, $_SESSION, $bookmarkService, $sessionManager->generateToken(), $loginManager->isLoggedIn());
$PAGE->assign('linkcount', $bookmarkService->count(BookmarkFilter::$ALL));
$PAGE->assign('privateLinkcount', $bookmarkService->count(BookmarkFilter::$PRIVATE));
$PAGE->assign('plugin_errors', $pluginManager->getErrors());
// Determine which page will be rendered.
$query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
$targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn());
if (// if the user isn't logged in
!$loginManager->isLoggedIn() &&
// and Shaarli doesn't have public content...
$conf->get('privacy.hide_public_links') &&
// and is configured to enforce the login
$conf->get('privacy.force_login') &&
// and the current page isn't already the login page
$targetPage !== Router::$PAGE_LOGIN &&
// and the user is not requesting a feed (which would lead to a different content-type as expected)
$targetPage !== Router::$PAGE_FEED_ATOM &&
$targetPage !== Router::$PAGE_FEED_RSS
) {
// force current page to be the login page
$targetPage = Router::$PAGE_LOGIN;
}
// Call plugin hooks for header, footer and includes, specifying which page will be rendered.
// Then assign generated data to RainTPL.
$common_hooks = array(
'includes',
'header',
'footer',
);
foreach ($common_hooks as $name) {
$plugin_data = array();
$pluginManager->executeHooks(
'render_' . $name,
$plugin_data,
array(
'target' => $targetPage,
'loggedin' => $loginManager->isLoggedIn()
)
);
$PAGE->assign('plugins_' . $name, $plugin_data);
}
// -------- Display login form.
if ($targetPage == Router::$PAGE_LOGIN) {
header('Location: ./login');
exit;
}
// -------- User wants to logout.
if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) {
header('Location: ./logout');
exit;
}
// -------- Picture wall
if ($targetPage == Router::$PAGE_PICWALL) {
header('Location: ./picture-wall');
exit;
}
// -------- Tag cloud
if ($targetPage == Router::$PAGE_TAGCLOUD) {
header('Location: ./tags/cloud');
exit;
}
// -------- Tag list
if ($targetPage == Router::$PAGE_TAGLIST) {
header('Location: ./tags/list');
exit;
}
// Daily page.
if ($targetPage == Router::$PAGE_DAILY) {
$dayParam = !empty($_GET['day']) ? '?day=' . escape($_GET['day']) : '';
header('Location: ./daily'. $dayParam);
exit;
}
// ATOM and RSS feed.
if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) {
$feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
header('Location: ./feed/'. $feedType .'?'. http_build_query($_GET));
exit;
}
// Display opensearch plugin (XML)
if ($targetPage == Router::$PAGE_OPENSEARCH) {
header('Location: ./open-search');
exit;
}
// -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
if (isset($_GET['addtag'])) {
header('Location: ./add-tag/'. $_GET['addtag']);
exit;
}
// -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
if (isset($_GET['removetag'])) {
header('Location: ./remove-tag/'. $_GET['removetag']);
exit;
}
// -------- User wants to change the number of bookmarks per page (linksperpage=...)
if (isset($_GET['linksperpage'])) {
header('Location: ./links-per-page?nb='. $_GET['linksperpage']);
exit;
}
// -------- User wants to see only private bookmarks (toggle)
if (isset($_GET['visibility'])) {
header('Location: ./visibility/'. $_GET['visibility']);
exit;
}
// -------- User wants to see only untagged bookmarks (toggle)
if (isset($_GET['untaggedonly'])) {
header('Location: ./untagged-only');
exit;
}
// -------- Handle other actions allowed for non-logged in users:
if (!$loginManager->isLoggedIn()) {
// User tries to post new link but is not logged in:
// Show login screen, then redirect to ?post=...
if (isset($_GET['post'])) {
header( // Redirect to login page, then back to post link.
'Location: ./login?post='.urlencode($_GET['post']).
(!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').
(!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').
(!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):'').
(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')
);
exit;
}
showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
if (isset($_GET['edit_link'])) {
header('Location: ./login?edit_link='. escape($_GET['edit_link']));
exit;
}
exit; // Never remove this one! All operations below are reserved for logged in user.
}
// -------- All other functions are reserved for the registered user:
// TODO: Remove legacy admin route redirections. We'll only keep public URL.
// -------- Display the Tools menu if requested (import/export/bookmarklet...)
if ($targetPage == Router::$PAGE_TOOLS) {
header('Location: ./admin/tools');
exit;
}
// -------- User wants to change his/her password.
if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
header('Location: ./admin/password');
exit;
}
// -------- User wants to change configuration
if ($targetPage == Router::$PAGE_CONFIGURE) {
header('Location: ./admin/configure');
exit;
}
// -------- User wants to rename a tag or delete it
if ($targetPage == Router::$PAGE_CHANGETAG) {
header('Location: ./admin/tags');
exit;
}
// -------- User wants to add a link without using the bookmarklet: Show form.
if ($targetPage == Router::$PAGE_ADDLINK) {
header('Location: ./admin/shaare');
exit;
}
// -------- User clicked the "Save" button when editing a link: Save link to database.
if (isset($_POST['save_edit'])) {
// This route is no longer supported in legacy mode
header('Location: ./');
exit;
}
// -------- User clicked the "Delete" button when editing a link: Delete link from database.
if ($targetPage == Router::$PAGE_DELETELINK) {
$ids = $_GET['lf_linkdate'] ?? '';
$token = $_GET['token'] ?? '';
header('Location: ./admin/shaare/delete?id=' . $ids . '&token=' . $token);
exit;
}
// -------- User clicked either "Set public" or "Set private" bulk operation
if ($targetPage == Router::$PAGE_CHANGE_VISIBILITY) {
header('Location: ./admin/shaare/visibility?id=' . $_GET['token']);
exit;
}
// -------- User clicked the "EDIT" button on a link: Display link edit form.
if (isset($_GET['edit_link'])) {
$id = (int) escape($_GET['edit_link']);
header('Location: ./admin/shaare/' . $id);
exit;
}
// -------- User want to post a new link: Display link edit form.
if (isset($_GET['post'])) {
header('Location: ./admin/shaare?' . http_build_query($_GET));
exit;
}
if ($targetPage == Router::$PAGE_PINLINK) {
// This route is no longer supported in legacy mode
header('Location: ./');
exit;
}
if ($targetPage == Router::$PAGE_EXPORT) {
header('Location: ./admin/export');
exit;
}
if ($targetPage == Router::$PAGE_IMPORT) {
header('Location: ./admin/import');
exit;
}
// Plugin administration page
if ($targetPage == Router::$PAGE_PLUGINSADMIN) {
header('Location: ./admin/plugins');
exit;
}
// Plugin administration form action
if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
// This route is no longer supported in legacy mode
header('Location: ./admin/plugins');
exit;
}
// Get a fresh token
if ($targetPage == Router::$GET_TOKEN) {
header('Location: ./admin/token');
exit;
}
// -------- Thumbnails Update
if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
header('Location: ./admin/thumbnails');
exit;
}
// -------- Single Thumbnail Update
if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
// This route is no longer supported in legacy mode
http_response_code(404);
exit;
}
// -------- Otherwise, simply display search form and bookmarks:
showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
exit;
}
/**
* Template for the list of bookmarks (<div id="linklist">)
* This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
*
* @param pageBuilder $PAGE pageBuilder instance.
* @param BookmarkServiceInterface $linkDb LinkDB instance.
* @param ConfigManager $conf Configuration Manager instance.
* @param PluginManager $pluginManager Plugin Manager instance.
* @param LoginManager $loginManager LoginManager instance
*/
function buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
{
$factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
$formatter = $factory->getFormatter();
// Used in templates
if (isset($_GET['searchtags'])) {
if (! empty($_GET['searchtags'])) {
$searchtags = escape(normalize_spaces($_GET['searchtags']));
} else {
$searchtags = false;
}
} else {
$searchtags = '';
}
$searchterm = !empty($_GET['searchterm']) ? escape(normalize_spaces($_GET['searchterm'])) : '';
// Smallhash filter
if (! empty($_SERVER['QUERY_STRING'])
&& preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) {
try {
$linksToDisplay = $linkDb->findByHash($_SERVER['QUERY_STRING']);
} catch (BookmarkNotFoundException $e) {
$PAGE->render404($e->getMessage());
exit;
}
} else {
// Filter bookmarks according search parameters.
$visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : null;
$request = [
'searchtags' => $searchtags,
'searchterm' => $searchterm,
];
$linksToDisplay = $linkDb->search($request, $visibility, false, !empty($_SESSION['untaggedonly']));
}
// ---- Handle paging.
$keys = array();
foreach ($linksToDisplay as $key => $value) {
$keys[] = $key;
}
// Select articles according to paging.
$pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
$pagecount = $pagecount == 0 ? 1 : $pagecount;
$page= empty($_GET['page']) ? 1 : intval($_GET['page']);
$page = $page < 1 ? 1 : $page;
$page = $page > $pagecount ? $pagecount : $page;
// Start index.
$i = ($page-1) * $_SESSION['LINKS_PER_PAGE'];
$end = $i + $_SESSION['LINKS_PER_PAGE'];
$thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE;
if ($thumbnailsEnabled) {
$thumbnailer = new Thumbnailer($conf);
}
$linkDisp = array();
while ($i<$end && $i<count($keys)) {
$link = $formatter->format($linksToDisplay[$keys[$i]]);
// Logged in, thumbnails enabled, not a note,
// and (never retrieved yet or no valid cache file)
if ($loginManager->isLoggedIn()
&& $thumbnailsEnabled
&& !$linksToDisplay[$keys[$i]]->isNote()
&& $linksToDisplay[$keys[$i]]->getThumbnail() !== false
&& ! is_file($linksToDisplay[$keys[$i]]->getThumbnail())
) {
$linksToDisplay[$keys[$i]]->setThumbnail($thumbnailer->get($link['url']));
$linkDb->set($linksToDisplay[$keys[$i]], false);
$updateDB = true;
$link['thumbnail'] = $linksToDisplay[$keys[$i]]->getThumbnail();
}
// Check for both signs of a note: starting with ? and 7 chars long.
// if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
// $link['url'] = index_url($_SERVER) . $link['url'];
// }
$linkDisp[$keys[$i]] = $link;
$i++;
}
// If we retrieved new thumbnails, we update the database.
if (!empty($updateDB)) {
$linkDb->save();
}
// Compute paging navigation
$searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags);
$searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm);
$previous_page_url = '';
if ($i != count($keys)) {
$previous_page_url = '?page=' . ($page+1) . $searchtermUrl . $searchtagsUrl;
}
$next_page_url='';
if ($page>1) {
$next_page_url = '?page=' . ($page-1) . $searchtermUrl . $searchtagsUrl;
}
// Fill all template fields.
$data = array(
'previous_page_url' => $previous_page_url,
'next_page_url' => $next_page_url,
'page_current' => $page,
'page_max' => $pagecount,
'result_count' => count($linksToDisplay),
'search_term' => $searchterm,
'search_tags' => $searchtags,
'visibility' => ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '',
'links' => $linkDisp,
);
// If there is only a single link, we change on-the-fly the title of the page.
if (count($linksToDisplay) == 1) {
$data['pagetitle'] = $linksToDisplay[$keys[0]]->getTitle() .' - '. $conf->get('general.title');
} elseif (! empty($searchterm) || ! empty($searchtags)) {
$data['pagetitle'] = t('Search: ');
$data['pagetitle'] .= ! empty($searchterm) ? $searchterm .' ' : '';
$bracketWrap = function ($tag) {
return '['. $tag .']';
};
$data['pagetitle'] .= ! empty($searchtags)
? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchtags))).' '
: '';
$data['pagetitle'] .= '- '. $conf->get('general.title');
}
$pluginManager->executeHooks('render_linklist', $data, array('loggedin' => $loginManager->isLoggedIn()));
foreach ($data as $key => $value) {
$PAGE->assign($key, $value);
}
return;
}
/**
* Installation
* This function should NEVER be called if the file data/config.php exists.
@ -882,19 +386,6 @@ if (!isset($_SESSION['LINKS_PER_PAGE'])) {
$_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
}
try {
$history = new History($conf->get('resource.history'));
} catch (Exception $e) {
die($e->getMessage());
}
$linkDb = new BookmarkFileService($conf, $history, $loginManager->isLoggedIn());
if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) {
header('Location: ./daily-rss');
exit;
}
$containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager);
$container = $containerBuilder->build();
$app = new App($container);
@ -918,13 +409,15 @@ $app->group('/api/v1', function () {
$app->group('', function () {
/* -- PUBLIC --*/
$this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index');
$this->get('/', '\Shaarli\Front\Controller\Visitor\BookmarkListController:index');
$this->get('/shaare/{hash}', '\Shaarli\Front\Controller\Visitor\BookmarkListController:permalink');
$this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index')->setName('login');
$this->get('/picture-wall', '\Shaarli\Front\Controller\Visitor\PictureWallController:index');
$this->get('/tags/cloud', '\Shaarli\Front\Controller\Visitor\TagCloudController:cloud');
$this->get('/tags/list', '\Shaarli\Front\Controller\Visitor\TagCloudController:list');
$this->get('/daily', '\Shaarli\Front\Controller\Visitor\DailyController:index');
$this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss');
$this->get('/feed/atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom');
$this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss')->setName('rss');
$this->get('/feed/atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom')->setName('atom');
$this->get('/feed/rss', '\Shaarli\Front\Controller\Visitor\FeedController:rss');
$this->get('/open-search', '\Shaarli\Front\Controller\Visitor\OpenSearchController:index');
@ -967,19 +460,4 @@ $app->group('', function () {
$response = $app->run(true);
// Hack to make Slim and Shaarli router work together:
// If a Slim route isn't found and NOT API call, we call renderPage().
if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) {
// We use UTF-8 for proper international characters handling.
header('Content-Type: text/html; charset=utf-8');
renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);
} else {
$response = $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader(
'Access-Control-Allow-Headers',
'X-Requested-With, Content-Type, Accept, Origin, Authorization'
)
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$app->respond($response);
}
$app->respond($response);

View file

@ -5,7 +5,7 @@
* Adds the addlink input on the linklist page.
*/
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* When linklist is displayed, add play videos to header's toolbar.
@ -16,7 +16,7 @@ use Shaarli\Router;
*/
function hook_addlink_toolbar_render_header($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST && $data['_LOGGEDIN_'] === true) {
$form = array(
'attr' => array(
'method' => 'GET',

View file

@ -16,7 +16,7 @@
use Shaarli\Config\ConfigManager;
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* In the footer hook, there is a working example of a translation extension for Shaarli.
@ -74,7 +74,7 @@ function demo_plugin_init($conf)
function hook_demo_plugin_render_header($data)
{
// Only execute when linklist is rendered.
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
// If loggedin
if ($data['_LOGGEDIN_'] === true) {
/*
@ -441,9 +441,9 @@ function hook_demo_plugin_delete_link($data)
function hook_demo_plugin_render_feed($data)
{
foreach ($data['links'] as &$link) {
if ($data['_PAGE_'] == Router::$PAGE_FEED_ATOM) {
if ($data['_PAGE_'] == TemplatePage::FEED_ATOM) {
$link['description'] .= ' - ATOM Feed' ;
} elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) {
} elseif ($data['_PAGE_'] == TemplatePage::FEED_RSS) {
$link['description'] .= ' - RSS Feed';
}
}

View file

@ -6,7 +6,7 @@
use Shaarli\Config\ConfigManager;
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* Display an error everywhere if the plugin is enabled without configuration.
@ -76,7 +76,7 @@ function hook_isso_render_linklist($data, $conf)
*/
function hook_isso_render_includes($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
$data['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
}

View file

@ -7,7 +7,7 @@
*/
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* When linklist is displayed, add play videos to header's toolbar.
@ -18,7 +18,7 @@ use Shaarli\Router;
*/
function hook_playvideos_render_header($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
$playvideo = array(
'attr' => array(
'href' => '#',
@ -42,7 +42,7 @@ function hook_playvideos_render_header($data)
*/
function hook_playvideos_render_footer($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/jquery-1.11.2.min.js';
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/youtube_playlist.js';
}

View file

@ -13,7 +13,7 @@ use pubsubhubbub\publisher\Publisher;
use Shaarli\Config\ConfigManager;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* Plugin init function - set the hub to the default appspot one.
@ -41,7 +41,7 @@ function pubsubhubbub_init($conf)
*/
function hook_pubsubhubbub_render_feed($data, $conf)
{
$feedType = $data['_PAGE_'] == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
$feedType = $data['_PAGE_'] == TemplatePage::FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
$template = file_get_contents(PluginManager::$PLUGINS_PATH . '/pubsubhubbub/hub.'. $feedType .'.xml');
$data['feed_plugins_header'][] = sprintf($template, $conf->get('plugins.PUBSUBHUB_URL'));

View file

@ -6,7 +6,7 @@
*/
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
/**
* Add qrcode icon to link_plugin when rendering linklist.
@ -40,7 +40,7 @@ function hook_qrcode_render_linklist($data)
*/
function hook_qrcode_render_footer($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/shaarli-qrcode.js';
}
@ -56,7 +56,7 @@ function hook_qrcode_render_footer($data)
*/
function hook_qrcode_render_includes($data)
{
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
$data['css_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.css';
}

View file

@ -892,35 +892,35 @@ class BookmarkFileServiceTest extends TestCase
public function testFilterHashValid()
{
$request = smallHash('20150310_114651');
$this->assertEquals(
1,
count($this->publicLinkDB->findByHash($request))
$this->assertSame(
$request,
$this->publicLinkDB->findByHash($request)->getShortUrl()
);
$request = smallHash('20150310_114633' . 8);
$this->assertEquals(
1,
count($this->publicLinkDB->findByHash($request))
$this->assertSame(
$request,
$this->publicLinkDB->findByHash($request)->getShortUrl()
);
}
/**
* Test filterHash() with an invalid smallhash.
*
* @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
*/
public function testFilterHashInValid1()
{
$this->expectException(BookmarkNotFoundException::class);
$request = 'blabla';
$this->publicLinkDB->findByHash($request);
}
/**
* Test filterHash() with an empty smallhash.
*
* @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
*/
public function testFilterHashInValid()
{
$this->expectException(BookmarkNotFoundException::class);
$this->publicLinkDB->findByHash('');
}

View file

@ -8,7 +8,11 @@ use PHPUnit\Framework\TestCase;
use Shaarli\Config\ConfigManager;
use Shaarli\Container\ShaarliContainer;
use Shaarli\Front\Exception\LoginBannedException;
use Shaarli\Front\Exception\UnauthorizedException;
use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager;
use Shaarli\Updater\Updater;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Uri;
@ -24,9 +28,16 @@ class ShaarliMiddlewareTest extends TestCase
public function setUp(): void
{
$this->container = $this->createMock(ShaarliContainer::class);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->middleware = new ShaarliMiddleware($this->container);
}
/**
* Test middleware execution with valid controller call
*/
public function testMiddlewareExecution(): void
{
$request = $this->createMock(Request::class);
@ -49,7 +60,10 @@ class ShaarliMiddlewareTest extends TestCase
static::assertSame(418, $result->getStatusCode());
}
public function testMiddlewareExecutionWithException(): void
/**
* Test middleware execution with controller throwing a known front exception
*/
public function testMiddlewareExecutionWithFrontException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
@ -58,7 +72,7 @@ class ShaarliMiddlewareTest extends TestCase
return $uri;
});
$response = new Response();
$controller = function (): void {
$exception = new LoginBannedException();
@ -72,9 +86,6 @@ class ShaarliMiddlewareTest extends TestCase
});
$this->container->pageBuilder = $pageBuilder;
$conf = $this->createMock(ConfigManager::class);
$this->container->conf = $conf;
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
@ -82,4 +93,113 @@ class ShaarliMiddlewareTest extends TestCase
static::assertSame(401, $result->getStatusCode());
static::assertContains('error', (string) $result->getBody());
}
/**
* Test middleware execution with controller throwing a not authorized exception
*/
public function testMiddlewareExecutionWithUnauthorizedException(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (): void {
throw new UnauthorizedException();
};
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
}
/**
* Test middleware execution with controller throwing a not authorized exception
*/
public function testMiddlewareExecutionWithServerExceptionWith(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (): void {
throw new \Exception();
};
$parameters = [];
$this->container->pageBuilder = $this->createMock(PageBuilder::class);
$this->container->pageBuilder->method('render')->willReturnCallback(function (string $message): string {
return $message;
});
$this->container->pageBuilder
->method('assign')
->willReturnCallback(function (string $key, string $value) use (&$parameters): void {
$parameters[$key] = $value;
})
;
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertSame(500, $result->getStatusCode());
static::assertContains('error', (string) $result->getBody());
static::assertSame('An unexpected error occurred.', $parameters['message']);
}
public function testMiddlewareExecutionWithUpdates(): void
{
$request = $this->createMock(Request::class);
$request->method('getUri')->willReturnCallback(function (): Uri {
$uri = $this->createMock(Uri::class);
$uri->method('getBasePath')->willReturn('/subfolder');
return $uri;
});
$response = new Response();
$controller = function (Request $request, Response $response): Response {
return $response->withStatus(418); // I'm a tea pot
};
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf->method('get')->willReturnCallback(function (string $key): string {
return $key;
});
$this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
$this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
$this->container->updater = $this->createMock(Updater::class);
$this->container->updater
->expects(static::once())
->method('update')
->willReturn(['update123'])
;
$this->container->updater->method('getDoneUpdates')->willReturn($updates = ['update123', 'other']);
$this->container->updater
->expects(static::once())
->method('writeUpdates')
->with('resource.updates', $updates)
;
/** @var Response $result */
$result = $this->middleware->__invoke($request, $response, $controller);
static::assertInstanceOf(Response::class, $result);
static::assertSame(418, $result->getStatusCode());
}
}

View file

@ -0,0 +1,448 @@
<?php
declare(strict_types=1);
namespace Shaarli\Front\Controller\Visitor;
use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
use Shaarli\Config\ConfigManager;
use Shaarli\Security\LoginManager;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
use Slim\Http\Response;
class BookmarkListControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var BookmarkListController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new BookmarkListController($this->container);
}
/**
* Test rendering list of bookmarks with default parameters (first page).
*/
public function testIndexDefaultFirstPage(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => '', 'searchterm' => ''],
null,
false,
false
)
->willReturn([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
]
);
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $parameter, $default = null) {
if ('LINKS_PER_PAGE' === $parameter) {
return 2;
}
return $default;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['pagetitle']);
static::assertSame('?page=2', $assignedVariables['previous_page_url']);
static::assertSame('', $assignedVariables['next_page_url']);
static::assertSame(2, $assignedVariables['page_max']);
static::assertSame('', $assignedVariables['search_tags']);
static::assertSame(3, $assignedVariables['result_count']);
static::assertSame(1, $assignedVariables['page_current']);
static::assertSame('', $assignedVariables['search_term']);
static::assertNull($assignedVariables['visibility']);
static::assertCount(2, $assignedVariables['links']);
$link = $assignedVariables['links'][0];
static::assertSame(1, $link['id']);
static::assertSame('http://url1.tld', $link['url']);
static::assertSame('Title 1', $link['title']);
$link = $assignedVariables['links'][1];
static::assertSame(2, $link['id']);
static::assertSame('http://url2.tld', $link['url']);
static::assertSame('Title 2', $link['title']);
}
/**
* Test rendering list of bookmarks with default parameters (second page).
*/
public function testIndexDefaultSecondPage(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) {
if ('page' === $key) {
return '2';
}
return null;
});
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => '', 'searchterm' => ''],
null,
false,
false
)
->willReturn([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
])
;
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $parameter, $default = null) {
if ('LINKS_PER_PAGE' === $parameter) {
return 2;
}
return $default;
})
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['pagetitle']);
static::assertSame('', $assignedVariables['previous_page_url']);
static::assertSame('?page=1', $assignedVariables['next_page_url']);
static::assertSame(2, $assignedVariables['page_max']);
static::assertSame('', $assignedVariables['search_tags']);
static::assertSame(3, $assignedVariables['result_count']);
static::assertSame(2, $assignedVariables['page_current']);
static::assertSame('', $assignedVariables['search_term']);
static::assertNull($assignedVariables['visibility']);
static::assertCount(1, $assignedVariables['links']);
$link = $assignedVariables['links'][2];
static::assertSame(3, $link['id']);
static::assertSame('http://url3.tld', $link['url']);
static::assertSame('Title 3', $link['title']);
}
/**
* Test rendering list of bookmarks with filters.
*/
public function testIndexDefaultWithFilters(): void
{
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$request->method('getParam')->willReturnCallback(function (string $key) {
if ('searchtags' === $key) {
return 'abc def';
}
if ('searchterm' === $key) {
return 'ghi jkl';
}
return null;
});
$response = new Response();
$this->container->sessionManager
->method('getSessionParameter')
->willReturnCallback(function (string $key, $default) {
if ('LINKS_PER_PAGE' === $key) {
return 2;
}
if ('visibility' === $key) {
return 'private';
}
if ('untaggedonly' === $key) {
return true;
}
return $default;
})
;
$this->container->bookmarkService
->expects(static::once())
->method('search')
->with(
['searchtags' => 'abc def', 'searchterm' => 'ghi jkl'],
'private',
false,
true
)
->willReturn([
(new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
(new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
])
;
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Search: ghi jkl [abc] [def] - Shaarli', $assignedVariables['pagetitle']);
static::assertSame('?page=2&searchterm=ghi+jkl&searchtags=abc+def', $assignedVariables['previous_page_url']);
}
/**
* Test displaying a permalink with valid parameters
*/
public function testPermalinkValid(): void
{
$hash = 'abcdef';
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
;
$result = $this->controller->permalink($request, $response, ['hash' => $hash]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
static::assertSame('Title 1 - Shaarli', $assignedVariables['pagetitle']);
static::assertCount(1, $assignedVariables['links']);
$link = $assignedVariables['links'][0];
static::assertSame(123, $link['id']);
static::assertSame('http://url1.tld', $link['url']);
static::assertSame('Title 1', $link['title']);
}
/**
* Test displaying a permalink with an unknown small hash : renders a 404 template error
*/
public function testPermalinkNotFound(): void
{
$hash = 'abcdef';
$assignedVariables = [];
$this->assignTemplateVars($assignedVariables);
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->with($hash)
->willThrowException(new BookmarkNotFoundException())
;
$result = $this->controller->permalink($request, $response, ['hash' => $hash]);
static::assertSame(200, $result->getStatusCode());
static::assertSame('404', (string) $result->getBody());
static::assertSame(
'The link you are trying to reach does not exist or has been deleted.',
$assignedVariables['error_message']
);
}
/**
* Test getting link list with thumbnail updates.
* -> 2 thumbnails update, only 1 datastore write
*/
public function testThumbnailUpdateFromLinkList(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $default) {
return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
})
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer
->expects(static::exactly(2))
->method('get')
->withConsecutive(['https://url2.tld'], ['https://url4.tld'])
;
$this->container->bookmarkService
->expects(static::once())
->method('search')
->willReturn([
(new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false),
$b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'),
(new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false),
$b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'),
(new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'),
])
;
$this->container->bookmarkService
->expects(static::exactly(2))
->method('set')
->withConsecutive([$b1, false], [$b2, false])
;
$this->container->bookmarkService->expects(static::once())->method('save');
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Test getting a permalink with thumbnail update.
*/
public function testThumbnailUpdateFromPermalink(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$this->container->conf = $this->createMock(ConfigManager::class);
$this->container->conf
->method('get')
->willReturnCallback(function (string $key, $default) {
return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
})
;
$this->container->thumbnailer = $this->createMock(Thumbnailer::class);
$this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']);
$this->container->bookmarkService
->expects(static::once())
->method('findByHash')
->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
;
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
$this->container->bookmarkService->expects(static::never())->method('save');
$result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Trigger legacy controller in link list controller: permalink
*/
public function testLegacyControllerPermalink(): void
{
$hash = 'abcdef';
$this->container->environment['QUERY_STRING'] = $hash;
$request = $this->createMock(Request::class);
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]);
}
/**
* Trigger legacy controller in link list controller: ?do= query parameter
*/
public function testLegacyControllerDoPage(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('do')->willReturn('picwall');
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]);
}
/**
* Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route
*/
public function testLegacyControllerUnknownDoPage(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParam')->with('do')->willReturn('nope');
$response = new Response();
$result = $this->controller->index($request, $response);
static::assertSame(200, $result->getStatusCode());
static::assertSame('linklist', (string) $result->getBody());
}
/**
* Trigger legacy controller in link list controller: other GET route (e.g. ?post)
*/
public function testLegacyControllerGetParameter(): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']);
$response = new Response();
$this->container->loginManager = $this->createMock(LoginManager::class);
$this->container->loginManager->method('isLoggedIn')->willReturn(true);
$result = $this->controller->index($request, $response);
static::assertSame(302, $result->getStatusCode());
static::assertSame(
'/subfolder/admin/shaare?post=' . urlencode($url),
$result->getHeader('location')[0]
);
}
}

View file

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Shaarli\Legacy;
use PHPUnit\Framework\TestCase;
use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
use Slim\Http\Request;
use Slim\Http\Response;
class LegacyControllerTest extends TestCase
{
use FrontControllerMockHelper;
/** @var LegacyController */
protected $controller;
public function setUp(): void
{
$this->createContainer();
$this->controller = new LegacyController($this->container);
}
/**
* @dataProvider getProcessProvider
*/
public function testProcess(string $legacyRoute, array $queryParameters, string $slimRoute, bool $isLoggedIn): void
{
$request = $this->createMock(Request::class);
$request->method('getQueryParams')->willReturn($queryParameters);
$request
->method('getParam')
->willReturnCallback(function (string $key) use ($queryParameters): ?string {
return $queryParameters[$key] ?? null;
})
;
$response = new Response();
$this->container->loginManager->method('isLoggedIn')->willReturn($isLoggedIn);
$result = $this->controller->process($request, $response, $legacyRoute);
static::assertSame('/subfolder' . $slimRoute, $result->getHeader('location')[0]);
}
public function testProcessNotFound(): void
{
$request = $this->createMock(Request::class);
$response = new Response();
$this->expectException(UnknowLegacyRouteException::class);
$this->controller->process($request, $response, 'nope');
}
/**
* @return array[] Parameters:
* - string legacyRoute
* - array queryParameters
* - string slimRoute
* - bool isLoggedIn
*/
public function getProcessProvider(): array
{
return [
['post', [], '/admin/shaare', true],
['post', [], '/login', false],
['post', ['title' => 'test'], '/admin/shaare?title=test', true],
['post', ['title' => 'test'], '/login?title=test', false],
['addlink', [], '/admin/add-shaare', true],
['addlink', [], '/login', false],
['login', [], '/login', true],
['login', [], '/login', false],
['logout', [], '/logout', true],
['logout', [], '/logout', false],
['picwall', [], '/picture-wall', false],
['picwall', [], '/picture-wall', true],
['tagcloud', [], '/tags/cloud', false],
['tagcloud', [], '/tags/cloud', true],
['taglist', [], '/tags/list', false],
['taglist', [], '/tags/list', true],
['daily', [], '/daily', false],
['daily', [], '/daily', true],
['daily', ['day' => '123456789', 'discard' => '1'], '/daily?day=123456789', false],
['rss', [], '/feed/rss', false],
['rss', [], '/feed/rss', true],
['rss', ['search' => 'filter123', 'other' => 'param'], '/feed/rss?search=filter123&other=param', false],
['atom', [], '/feed/atom', false],
['atom', [], '/feed/atom', true],
['atom', ['search' => 'filter123', 'other' => 'param'], '/feed/atom?search=filter123&other=param', false],
['opensearch', [], '/open-search', false],
['opensearch', [], '/open-search', true],
['dailyrss', [], '/daily-rss', false],
['dailyrss', [], '/daily-rss', true],
];
}
}

View file

@ -1,10 +1,13 @@
<?php
namespace Shaarli;
namespace Shaarli\Legacy;
use PHPUnit\Framework\TestCase;
/**
* Unit tests for Router
*/
class RouterTest extends \PHPUnit\Framework\TestCase
class LegacyRouterTest extends TestCase
{
/**
* Test findPage: login page output.
@ -15,18 +18,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageLoginValid()
{
$this->assertEquals(
Router::$PAGE_LOGIN,
Router::findPage('do=login', array(), false)
LegacyRouter::$PAGE_LOGIN,
LegacyRouter::findPage('do=login', array(), false)
);
$this->assertEquals(
Router::$PAGE_LOGIN,
Router::findPage('do=login', array(), 1)
LegacyRouter::$PAGE_LOGIN,
LegacyRouter::findPage('do=login', array(), 1)
);
$this->assertEquals(
Router::$PAGE_LOGIN,
Router::findPage('do=login&stuff', array(), false)
LegacyRouter::$PAGE_LOGIN,
LegacyRouter::findPage('do=login&stuff', array(), false)
);
}
@ -39,13 +42,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageLoginInvalid()
{
$this->assertNotEquals(
Router::$PAGE_LOGIN,
Router::findPage('do=login', array(), true)
LegacyRouter::$PAGE_LOGIN,
LegacyRouter::findPage('do=login', array(), true)
);
$this->assertNotEquals(
Router::$PAGE_LOGIN,
Router::findPage('do=other', array(), false)
LegacyRouter::$PAGE_LOGIN,
LegacyRouter::findPage('do=other', array(), false)
);
}
@ -58,13 +61,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPagePicwallValid()
{
$this->assertEquals(
Router::$PAGE_PICWALL,
Router::findPage('do=picwall', array(), false)
LegacyRouter::$PAGE_PICWALL,
LegacyRouter::findPage('do=picwall', array(), false)
);
$this->assertEquals(
Router::$PAGE_PICWALL,
Router::findPage('do=picwall', array(), true)
LegacyRouter::$PAGE_PICWALL,
LegacyRouter::findPage('do=picwall', array(), true)
);
}
@ -77,13 +80,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPagePicwallInvalid()
{
$this->assertEquals(
Router::$PAGE_PICWALL,
Router::findPage('do=picwall&stuff', array(), false)
LegacyRouter::$PAGE_PICWALL,
LegacyRouter::findPage('do=picwall&stuff', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_PICWALL,
Router::findPage('do=other', array(), false)
LegacyRouter::$PAGE_PICWALL,
LegacyRouter::findPage('do=other', array(), false)
);
}
@ -96,18 +99,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageTagcloudValid()
{
$this->assertEquals(
Router::$PAGE_TAGCLOUD,
Router::findPage('do=tagcloud', array(), false)
LegacyRouter::$PAGE_TAGCLOUD,
LegacyRouter::findPage('do=tagcloud', array(), false)
);
$this->assertEquals(
Router::$PAGE_TAGCLOUD,
Router::findPage('do=tagcloud', array(), true)
LegacyRouter::$PAGE_TAGCLOUD,
LegacyRouter::findPage('do=tagcloud', array(), true)
);
$this->assertEquals(
Router::$PAGE_TAGCLOUD,
Router::findPage('do=tagcloud&stuff', array(), false)
LegacyRouter::$PAGE_TAGCLOUD,
LegacyRouter::findPage('do=tagcloud&stuff', array(), false)
);
}
@ -120,8 +123,8 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageTagcloudInvalid()
{
$this->assertNotEquals(
Router::$PAGE_TAGCLOUD,
Router::findPage('do=other', array(), false)
LegacyRouter::$PAGE_TAGCLOUD,
LegacyRouter::findPage('do=other', array(), false)
);
}
@ -134,23 +137,23 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageLinklistValid()
{
$this->assertEquals(
Router::$PAGE_LINKLIST,
Router::findPage('', array(), true)
LegacyRouter::$PAGE_LINKLIST,
LegacyRouter::findPage('', array(), true)
);
$this->assertEquals(
Router::$PAGE_LINKLIST,
Router::findPage('whatever', array(), true)
LegacyRouter::$PAGE_LINKLIST,
LegacyRouter::findPage('whatever', array(), true)
);
$this->assertEquals(
Router::$PAGE_LINKLIST,
Router::findPage('whatever', array(), false)
LegacyRouter::$PAGE_LINKLIST,
LegacyRouter::findPage('whatever', array(), false)
);
$this->assertEquals(
Router::$PAGE_LINKLIST,
Router::findPage('do=tools', array(), false)
LegacyRouter::$PAGE_LINKLIST,
LegacyRouter::findPage('do=tools', array(), false)
);
}
@ -163,13 +166,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageToolsValid()
{
$this->assertEquals(
Router::$PAGE_TOOLS,
Router::findPage('do=tools', array(), true)
LegacyRouter::$PAGE_TOOLS,
LegacyRouter::findPage('do=tools', array(), true)
);
$this->assertEquals(
Router::$PAGE_TOOLS,
Router::findPage('do=tools&stuff', array(), true)
LegacyRouter::$PAGE_TOOLS,
LegacyRouter::findPage('do=tools&stuff', array(), true)
);
}
@ -182,18 +185,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageToolsInvalid()
{
$this->assertNotEquals(
Router::$PAGE_TOOLS,
Router::findPage('do=tools', array(), 1)
LegacyRouter::$PAGE_TOOLS,
LegacyRouter::findPage('do=tools', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_TOOLS,
Router::findPage('do=tools', array(), false)
LegacyRouter::$PAGE_TOOLS,
LegacyRouter::findPage('do=tools', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_TOOLS,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_TOOLS,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -206,12 +209,12 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageChangepasswdValid()
{
$this->assertEquals(
Router::$PAGE_CHANGEPASSWORD,
Router::findPage('do=changepasswd', array(), true)
LegacyRouter::$PAGE_CHANGEPASSWORD,
LegacyRouter::findPage('do=changepasswd', array(), true)
);
$this->assertEquals(
Router::$PAGE_CHANGEPASSWORD,
Router::findPage('do=changepasswd&stuff', array(), true)
LegacyRouter::$PAGE_CHANGEPASSWORD,
LegacyRouter::findPage('do=changepasswd&stuff', array(), true)
);
}
@ -224,18 +227,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageChangepasswdInvalid()
{
$this->assertNotEquals(
Router::$PAGE_CHANGEPASSWORD,
Router::findPage('do=changepasswd', array(), 1)
LegacyRouter::$PAGE_CHANGEPASSWORD,
LegacyRouter::findPage('do=changepasswd', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_CHANGEPASSWORD,
Router::findPage('do=changepasswd', array(), false)
LegacyRouter::$PAGE_CHANGEPASSWORD,
LegacyRouter::findPage('do=changepasswd', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_CHANGEPASSWORD,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_CHANGEPASSWORD,
LegacyRouter::findPage('do=other', array(), true)
);
}
/**
@ -247,13 +250,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageConfigureValid()
{
$this->assertEquals(
Router::$PAGE_CONFIGURE,
Router::findPage('do=configure', array(), true)
LegacyRouter::$PAGE_CONFIGURE,
LegacyRouter::findPage('do=configure', array(), true)
);
$this->assertEquals(
Router::$PAGE_CONFIGURE,
Router::findPage('do=configure&stuff', array(), true)
LegacyRouter::$PAGE_CONFIGURE,
LegacyRouter::findPage('do=configure&stuff', array(), true)
);
}
@ -266,18 +269,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageConfigureInvalid()
{
$this->assertNotEquals(
Router::$PAGE_CONFIGURE,
Router::findPage('do=configure', array(), 1)
LegacyRouter::$PAGE_CONFIGURE,
LegacyRouter::findPage('do=configure', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_CONFIGURE,
Router::findPage('do=configure', array(), false)
LegacyRouter::$PAGE_CONFIGURE,
LegacyRouter::findPage('do=configure', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_CONFIGURE,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_CONFIGURE,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -290,13 +293,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageChangetagValid()
{
$this->assertEquals(
Router::$PAGE_CHANGETAG,
Router::findPage('do=changetag', array(), true)
LegacyRouter::$PAGE_CHANGETAG,
LegacyRouter::findPage('do=changetag', array(), true)
);
$this->assertEquals(
Router::$PAGE_CHANGETAG,
Router::findPage('do=changetag&stuff', array(), true)
LegacyRouter::$PAGE_CHANGETAG,
LegacyRouter::findPage('do=changetag&stuff', array(), true)
);
}
@ -309,18 +312,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageChangetagInvalid()
{
$this->assertNotEquals(
Router::$PAGE_CHANGETAG,
Router::findPage('do=changetag', array(), 1)
LegacyRouter::$PAGE_CHANGETAG,
LegacyRouter::findPage('do=changetag', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_CHANGETAG,
Router::findPage('do=changetag', array(), false)
LegacyRouter::$PAGE_CHANGETAG,
LegacyRouter::findPage('do=changetag', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_CHANGETAG,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_CHANGETAG,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -333,13 +336,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageAddlinkValid()
{
$this->assertEquals(
Router::$PAGE_ADDLINK,
Router::findPage('do=addlink', array(), true)
LegacyRouter::$PAGE_ADDLINK,
LegacyRouter::findPage('do=addlink', array(), true)
);
$this->assertEquals(
Router::$PAGE_ADDLINK,
Router::findPage('do=addlink&stuff', array(), true)
LegacyRouter::$PAGE_ADDLINK,
LegacyRouter::findPage('do=addlink&stuff', array(), true)
);
}
@ -352,18 +355,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageAddlinkInvalid()
{
$this->assertNotEquals(
Router::$PAGE_ADDLINK,
Router::findPage('do=addlink', array(), 1)
LegacyRouter::$PAGE_ADDLINK,
LegacyRouter::findPage('do=addlink', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_ADDLINK,
Router::findPage('do=addlink', array(), false)
LegacyRouter::$PAGE_ADDLINK,
LegacyRouter::findPage('do=addlink', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_ADDLINK,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_ADDLINK,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -376,13 +379,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageExportValid()
{
$this->assertEquals(
Router::$PAGE_EXPORT,
Router::findPage('do=export', array(), true)
LegacyRouter::$PAGE_EXPORT,
LegacyRouter::findPage('do=export', array(), true)
);
$this->assertEquals(
Router::$PAGE_EXPORT,
Router::findPage('do=export&stuff', array(), true)
LegacyRouter::$PAGE_EXPORT,
LegacyRouter::findPage('do=export&stuff', array(), true)
);
}
@ -395,18 +398,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageExportInvalid()
{
$this->assertNotEquals(
Router::$PAGE_EXPORT,
Router::findPage('do=export', array(), 1)
LegacyRouter::$PAGE_EXPORT,
LegacyRouter::findPage('do=export', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_EXPORT,
Router::findPage('do=export', array(), false)
LegacyRouter::$PAGE_EXPORT,
LegacyRouter::findPage('do=export', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_EXPORT,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_EXPORT,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -419,13 +422,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageImportValid()
{
$this->assertEquals(
Router::$PAGE_IMPORT,
Router::findPage('do=import', array(), true)
LegacyRouter::$PAGE_IMPORT,
LegacyRouter::findPage('do=import', array(), true)
);
$this->assertEquals(
Router::$PAGE_IMPORT,
Router::findPage('do=import&stuff', array(), true)
LegacyRouter::$PAGE_IMPORT,
LegacyRouter::findPage('do=import&stuff', array(), true)
);
}
@ -438,18 +441,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageImportInvalid()
{
$this->assertNotEquals(
Router::$PAGE_IMPORT,
Router::findPage('do=import', array(), 1)
LegacyRouter::$PAGE_IMPORT,
LegacyRouter::findPage('do=import', array(), 1)
);
$this->assertNotEquals(
Router::$PAGE_IMPORT,
Router::findPage('do=import', array(), false)
LegacyRouter::$PAGE_IMPORT,
LegacyRouter::findPage('do=import', array(), false)
);
$this->assertNotEquals(
Router::$PAGE_IMPORT,
Router::findPage('do=other', array(), true)
LegacyRouter::$PAGE_IMPORT,
LegacyRouter::findPage('do=other', array(), true)
);
}
@ -462,24 +465,24 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageEditlinkValid()
{
$this->assertEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array('edit_link' => 1), true)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array('edit_link' => 1), true)
);
$this->assertEquals(
Router::$PAGE_EDITLINK,
Router::findPage('', array('edit_link' => 1), true)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('', array('edit_link' => 1), true)
);
$this->assertEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array('post' => 1), true)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array('post' => 1), true)
);
$this->assertEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
);
}
@ -492,18 +495,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
public function testFindPageEditlinkInvalid()
{
$this->assertNotEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array('edit_link' => 1), false)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array('edit_link' => 1), false)
);
$this->assertNotEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array('edit_link' => 1), 1)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array('edit_link' => 1), 1)
);
$this->assertNotEquals(
Router::$PAGE_EDITLINK,
Router::findPage('whatever', array(), true)
LegacyRouter::$PAGE_EDITLINK,
LegacyRouter::findPage('whatever', array(), true)
);
}
}

View file

@ -2,7 +2,7 @@
namespace Shaarli\Plugin\Addlink;
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
require_once 'plugins/addlink_toolbar/addlink_toolbar.php';
@ -26,7 +26,7 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
{
$str = 'stuff';
$data = array($str => $str);
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
$data['_PAGE_'] = TemplatePage::LINKLIST;
$data['_LOGGEDIN_'] = true;
$data = hook_addlink_toolbar_render_header($data);
@ -48,7 +48,7 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
{
$str = 'stuff';
$data = array($str => $str);
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
$data['_PAGE_'] = TemplatePage::LINKLIST;
$data['_LOGGEDIN_'] = false;
$data = hook_addlink_toolbar_render_header($data);

View file

@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Playvideos;
*/
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
require_once 'plugins/playvideos/playvideos.php';
@ -31,7 +31,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
{
$str = 'stuff';
$data = array($str => $str);
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
$data['_PAGE_'] = TemplatePage::LINKLIST;
$data = hook_playvideos_render_header($data);
$this->assertEquals($str, $data[$str]);
@ -50,7 +50,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
{
$str = 'stuff';
$data = array($str => $str);
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
$data['_PAGE_'] = TemplatePage::LINKLIST;
$data = hook_playvideos_render_footer($data);
$this->assertEquals($str, $data[$str]);

View file

@ -3,7 +3,7 @@ namespace Shaarli\Plugin\Pubsubhubbub;
use Shaarli\Config\ConfigManager;
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
@ -34,7 +34,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
$hub = 'http://domain.hub';
$conf = new ConfigManager(self::$configFile);
$conf->set('plugins.PUBSUBHUB_URL', $hub);
$data['_PAGE_'] = Router::$PAGE_FEED_RSS;
$data['_PAGE_'] = TemplatePage::FEED_RSS;
$data = hook_pubsubhubbub_render_feed($data, $conf);
$expected = '<atom:link rel="hub" href="'. $hub .'" />';
@ -49,7 +49,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
$hub = 'http://domain.hub';
$conf = new ConfigManager(self::$configFile);
$conf->set('plugins.PUBSUBHUB_URL', $hub);
$data['_PAGE_'] = Router::$PAGE_FEED_ATOM;
$data['_PAGE_'] = TemplatePage::FEED_ATOM;
$data = hook_pubsubhubbub_render_feed($data, $conf);
$expected = '<link rel="hub" href="'. $hub .'" />';

View file

@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Qrcode;
*/
use Shaarli\Plugin\PluginManager;
use Shaarli\Router;
use Shaarli\Render\TemplatePage;
require_once 'plugins/qrcode/qrcode.php';
@ -57,7 +57,7 @@ class PluginQrcodeTest extends \PHPUnit\Framework\TestCase
{
$str = 'stuff';
$data = array($str => $str);
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
$data['_PAGE_'] = TemplatePage::LINKLIST;
$data = hook_qrcode_render_footer($data);
$this->assertEquals($str, $data[$str]);

View file

@ -2,7 +2,10 @@
namespace Shaarli\Updater;
use Exception;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
require_once 'tests/updater/DummyUpdater.php';
require_once 'tests/utils/ReferenceLinkDB.php';
@ -29,6 +32,12 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
*/
protected $conf;
/** @var BookmarkServiceInterface */
protected $bookmarkService;
/** @var Updater */
protected $updater;
/**
* Executed before each test.
*/
@ -36,6 +45,8 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
{
copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
$this->conf = new ConfigManager(self::$configFile);
$this->bookmarkService = new BookmarkFileService($this->conf, $this->createMock(History::class), true);
$this->updater = new Updater([], $this->bookmarkService, $this->conf, true);
}
/**
@ -167,4 +178,12 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
$updater = new DummyUpdater($updates, array(), $this->conf, true);
$updater->update();
}
public function testUpdateMethodRelativeHomeLinkRename(): void
{
$this->conf->set('general.header_link', '?');
$this->updater->updateMethodRelativeHomeLink();
static::assertSame();
}
}

View file

@ -224,7 +224,7 @@
</div>
{/if}
{/if}
<a href="{$base_path}/?{$value.shorturl}" title="{$strPermalink}">
<a href="{$base_path}/shaare/{$value.shorturl}" title="{$strPermalink}">
{if="!$hide_timestamps || $is_logged_in"}
{$updated=$value.updated_timestamp ? $strEdited. format_date($value.updated) : $strPermalink}
<span class="linkdate" title="{$updated}">