aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-07-06 08:04:35 +0200
committerArthurHoaro <arthur@hoa.ro>2020-07-23 21:19:21 +0200
commit1a8ac737e52cb25a5c346232ee398f5908cee7d7 (patch)
tree31954c4e106b5743e2005d72c2d548a0be8d6dce
parent6132d64748dfc6806ed25f71d2e078a5ed29d071 (diff)
downloadShaarli-1a8ac737e52cb25a5c346232ee398f5908cee7d7.tar.gz
Shaarli-1a8ac737e52cb25a5c346232ee398f5908cee7d7.tar.zst
Shaarli-1a8ac737e52cb25a5c346232ee398f5908cee7d7.zip
Process main page (linklist) through Slim controller
Including a bunch of improvements on the container, and helper used across new controllers.
-rw-r--r--application/api/ApiMiddleware.php9
-rw-r--r--application/bookmark/BookmarkFileService.php2
-rw-r--r--application/container/ContainerBuilder.php11
-rw-r--r--application/container/ShaarliContainer.php7
-rw-r--r--application/front/ShaarliMiddleware.php73
-rw-r--r--application/front/controller/admin/ConfigureController.php3
-rw-r--r--application/front/controller/admin/ExportController.php5
-rw-r--r--application/front/controller/admin/ImportController.php3
-rw-r--r--application/front/controller/admin/ManageShaareController.php5
-rw-r--r--application/front/controller/admin/ManageTagController.php3
-rw-r--r--application/front/controller/admin/PasswordController.php9
-rw-r--r--application/front/controller/admin/PluginsController.php3
-rw-r--r--application/front/controller/admin/ThumbnailsController.php18
-rw-r--r--application/front/controller/admin/ToolsController.php3
-rw-r--r--application/front/controller/visitor/BookmarkListController.php248
-rw-r--r--application/front/controller/visitor/DailyController.php5
-rw-r--r--application/front/controller/visitor/LoginController.php3
-rw-r--r--application/front/controller/visitor/OpenSearchController.php3
-rw-r--r--application/front/controller/visitor/PictureWallController.php3
-rw-r--r--application/legacy/LegacyController.php130
-rw-r--r--application/legacy/LegacyRouter.php (renamed from application/Router.php)7
-rw-r--r--application/legacy/UnknowLegacyRouteException.php9
-rw-r--r--application/render/PageBuilder.php9
-rw-r--r--application/render/TemplatePage.php33
-rw-r--r--application/updater/Updater.php43
-rw-r--r--doc/md/Plugin-System.md6
-rw-r--r--index.php534
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.php4
-rw-r--r--plugins/demo_plugin/demo_plugin.php8
-rw-r--r--plugins/isso/isso.php4
-rw-r--r--plugins/playvideos/playvideos.php6
-rw-r--r--plugins/pubsubhubbub/pubsubhubbub.php4
-rw-r--r--plugins/qrcode/qrcode.php6
-rw-r--r--tests/bookmark/BookmarkFileServiceTest.php20
-rw-r--r--tests/front/ShaarliMiddlewareTest.php130
-rw-r--r--tests/front/controller/visitor/BookmarkListControllerTest.php448
-rw-r--r--tests/legacy/LegacyControllerTest.php99
-rw-r--r--tests/legacy/LegacyRouterTest.php (renamed from tests/RouterTest.php)243
-rw-r--r--tests/plugins/PluginAddlinkTest.php6
-rw-r--r--tests/plugins/PluginPlayvideosTest.php6
-rw-r--r--tests/plugins/PluginPubsubhubbubTest.php6
-rw-r--r--tests/plugins/PluginQrcodeTest.php4
-rw-r--r--tests/updater/UpdaterTest.php19
-rw-r--r--tpl/default/linklist.html2
44 files changed, 1457 insertions, 745 deletions
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php
index 4745ac94..09ce6445 100644
--- a/application/api/ApiMiddleware.php
+++ b/application/api/ApiMiddleware.php
@@ -71,7 +71,14 @@ class ApiMiddleware
71 $response = $e->getApiResponse(); 71 $response = $e->getApiResponse();
72 } 72 }
73 73
74 return $response; 74 return $response
75 ->withHeader('Access-Control-Allow-Origin', '*')
76 ->withHeader(
77 'Access-Control-Allow-Headers',
78 'X-Requested-With, Content-Type, Accept, Origin, Authorization'
79 )
80 ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
81 ;
75 } 82 }
76 83
77 /** 84 /**
diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php
index 7439d8d8..3d15d4c9 100644
--- a/application/bookmark/BookmarkFileService.php
+++ b/application/bookmark/BookmarkFileService.php
@@ -93,7 +93,7 @@ class BookmarkFileService implements BookmarkServiceInterface
93 throw new Exception('Not authorized'); 93 throw new Exception('Not authorized');
94 } 94 }
95 95
96 return $bookmark; 96 return $first;
97 } 97 }
98 98
99 /** 99 /**
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php
index ba91fe8b..ccb87c3a 100644
--- a/application/container/ContainerBuilder.php
+++ b/application/container/ContainerBuilder.php
@@ -18,6 +18,8 @@ use Shaarli\Render\PageCacheManager;
18use Shaarli\Security\LoginManager; 18use Shaarli\Security\LoginManager;
19use Shaarli\Security\SessionManager; 19use Shaarli\Security\SessionManager;
20use Shaarli\Thumbnailer; 20use Shaarli\Thumbnailer;
21use Shaarli\Updater\Updater;
22use Shaarli\Updater\UpdaterUtils;
21 23
22/** 24/**
23 * Class ContainerBuilder 25 * Class ContainerBuilder
@@ -128,6 +130,15 @@ class ContainerBuilder
128 return new NetscapeBookmarkUtils($container->bookmarkService, $container->conf, $container->history); 130 return new NetscapeBookmarkUtils($container->bookmarkService, $container->conf, $container->history);
129 }; 131 };
130 132
133 $container['updater'] = function (ShaarliContainer $container): Updater {
134 return new Updater(
135 UpdaterUtils::read_updates_file($container->conf->get('resource.updates')),
136 $container->bookmarkService,
137 $container->conf,
138 $container->loginManager->isLoggedIn()
139 );
140 };
141
131 return $container; 142 return $container;
132 } 143 }
133} 144}
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php
index b08fa4cb..09e7d5b1 100644
--- a/application/container/ShaarliContainer.php
+++ b/application/container/ShaarliContainer.php
@@ -17,15 +17,17 @@ use Shaarli\Render\PageCacheManager;
17use Shaarli\Security\LoginManager; 17use Shaarli\Security\LoginManager;
18use Shaarli\Security\SessionManager; 18use Shaarli\Security\SessionManager;
19use Shaarli\Thumbnailer; 19use Shaarli\Thumbnailer;
20use Shaarli\Updater\Updater;
20use Slim\Container; 21use Slim\Container;
21 22
22/** 23/**
23 * Extension of Slim container to document the injected objects. 24 * Extension of Slim container to document the injected objects.
24 * 25 *
25 * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) 26 * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
26 * @property BookmarkServiceInterface $bookmarkService 27 * @property BookmarkServiceInterface $bookmarkService
27 * @property ConfigManager $conf 28 * @property ConfigManager $conf
28 * @property mixed[] $environment $_SERVER automatically injected by Slim 29 * @property mixed[] $environment $_SERVER automatically injected by Slim
30 * @property callable $errorHandler Overrides default Slim error display
29 * @property FeedBuilder $feedBuilder 31 * @property FeedBuilder $feedBuilder
30 * @property FormatterFactory $formatterFactory 32 * @property FormatterFactory $formatterFactory
31 * @property History $history 33 * @property History $history
@@ -37,6 +39,7 @@ use Slim\Container;
37 * @property PluginManager $pluginManager 39 * @property PluginManager $pluginManager
38 * @property SessionManager $sessionManager 40 * @property SessionManager $sessionManager
39 * @property Thumbnailer $thumbnailer 41 * @property Thumbnailer $thumbnailer
42 * @property Updater $updater
40 */ 43 */
41class ShaarliContainer extends Container 44class ShaarliContainer extends Container
42{ 45{
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php
index 7ad610c7..baea6ef2 100644
--- a/application/front/ShaarliMiddleware.php
+++ b/application/front/ShaarliMiddleware.php
@@ -25,6 +25,8 @@ class ShaarliMiddleware
25 25
26 /** 26 /**
27 * Middleware execution: 27 * Middleware execution:
28 * - run updates
29 * - if not logged in open shaarli, redirect to login
28 * - execute the controller 30 * - execute the controller
29 * - return the response 31 * - return the response
30 * 32 *
@@ -36,27 +38,82 @@ class ShaarliMiddleware
36 * 38 *
37 * @return Response response. 39 * @return Response response.
38 */ 40 */
39 public function __invoke(Request $request, Response $response, callable $next) 41 public function __invoke(Request $request, Response $response, callable $next): Response
40 { 42 {
41 $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/'); 43 $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
42 44
43 try { 45 try {
44 $response = $next($request, $response); 46 $this->runUpdates();
47 $this->checkOpenShaarli($request, $response, $next);
48
49 return $next($request, $response);
45 } catch (ShaarliFrontException $e) { 50 } catch (ShaarliFrontException $e) {
51 // Possible functional error
52 $this->container->pageBuilder->reset();
46 $this->container->pageBuilder->assign('message', $e->getMessage()); 53 $this->container->pageBuilder->assign('message', $e->getMessage());
54
55 $response = $response->withStatus($e->getCode());
56
57 return $response->write($this->container->pageBuilder->render('error'));
58 } catch (UnauthorizedException $e) {
59 return $response->withRedirect($this->container->basePath . '/login');
60 } catch (\Throwable $e) {
61 // Unknown error encountered
62 $this->container->pageBuilder->reset();
47 if ($this->container->conf->get('dev.debug', false)) { 63 if ($this->container->conf->get('dev.debug', false)) {
64 $this->container->pageBuilder->assign('message', $e->getMessage());
48 $this->container->pageBuilder->assign( 65 $this->container->pageBuilder->assign(
49 'stacktrace', 66 'stacktrace',
50 nl2br(get_class($this) .': '. $e->getTraceAsString()) 67 nl2br(get_class($e) .': '. PHP_EOL . $e->getTraceAsString())
51 ); 68 );
69 } else {
70 $this->container->pageBuilder->assign('message', t('An unexpected error occurred.'));
52 } 71 }
53 72
54 $response = $response->withStatus($e->getCode()); 73 $response = $response->withStatus(500);
55 $response = $response->write($this->container->pageBuilder->render('error')); 74
56 } catch (UnauthorizedException $e) { 75 return $response->write($this->container->pageBuilder->render('error'));
57 return $response->withRedirect($this->container->basePath . '/login'); 76 }
77 }
78
79 /**
80 * Run the updater for every requests processed while logged in.
81 */
82 protected function runUpdates(): void
83 {
84 if ($this->container->loginManager->isLoggedIn() !== true) {
85 return;
86 }
87
88 $newUpdates = $this->container->updater->update();
89 if (!empty($newUpdates)) {
90 $this->container->updater->writeUpdates(
91 $this->container->conf->get('resource.updates'),
92 $this->container->updater->getDoneUpdates()
93 );
94
95 $this->container->pageCacheManager->invalidateCaches();
96 }
97 }
98
99 /**
100 * Access is denied to most pages with `hide_public_links` + `force_login` settings.
101 */
102 protected function checkOpenShaarli(Request $request, Response $response, callable $next): bool
103 {
104 if (// if the user isn't logged in
105 !$this->container->loginManager->isLoggedIn()
106 // and Shaarli doesn't have public content...
107 && $this->container->conf->get('privacy.hide_public_links')
108 // and is configured to enforce the login
109 && $this->container->conf->get('privacy.force_login')
110 // and the current page isn't already the login page
111 // and the user is not requesting a feed (which would lead to a different content-type as expected)
112 && !in_array($next->getName(), ['login', 'atom', 'rss'], true)
113 ) {
114 throw new UnauthorizedException();
58 } 115 }
59 116
60 return $response; 117 return true;
61 } 118 }
62} 119}
diff --git a/application/front/controller/admin/ConfigureController.php b/application/front/controller/admin/ConfigureController.php
index 201a859b..865fc2b0 100644
--- a/application/front/controller/admin/ConfigureController.php
+++ b/application/front/controller/admin/ConfigureController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Shaarli\Languages; 7use Shaarli\Languages;
8use Shaarli\Render\TemplatePage;
8use Shaarli\Render\ThemeUtils; 9use Shaarli\Render\ThemeUtils;
9use Shaarli\Thumbnailer; 10use Shaarli\Thumbnailer;
10use Slim\Http\Request; 11use Slim\Http\Request;
@@ -52,7 +53,7 @@ class ConfigureController extends ShaarliAdminController
52 $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); 53 $this->assignView('thumbnails_mode', $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE));
53 $this->assignView('pagetitle', t('Configure') .' - '. $this->container->conf->get('general.title', 'Shaarli')); 54 $this->assignView('pagetitle', t('Configure') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
54 55
55 return $response->write($this->render('configure')); 56 return $response->write($this->render(TemplatePage::CONFIGURE));
56 } 57 }
57 58
58 /** 59 /**
diff --git a/application/front/controller/admin/ExportController.php b/application/front/controller/admin/ExportController.php
index 7afbfc23..2be957fa 100644
--- a/application/front/controller/admin/ExportController.php
+++ b/application/front/controller/admin/ExportController.php
@@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin;
6 6
7use DateTime; 7use DateTime;
8use Shaarli\Bookmark\Bookmark; 8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Render\TemplatePage;
9use Slim\Http\Request; 10use Slim\Http\Request;
10use Slim\Http\Response; 11use Slim\Http\Response;
11 12
@@ -24,7 +25,7 @@ class ExportController extends ShaarliAdminController
24 { 25 {
25 $this->assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli')); 26 $this->assignView('pagetitle', t('Export') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
26 27
27 return $response->write($this->render('export')); 28 return $response->write($this->render(TemplatePage::EXPORT));
28 } 29 }
29 30
30 /** 31 /**
@@ -74,6 +75,6 @@ class ExportController extends ShaarliAdminController
74 $this->assignView('eol', PHP_EOL); 75 $this->assignView('eol', PHP_EOL);
75 $this->assignView('selection', $selection); 76 $this->assignView('selection', $selection);
76 77
77 return $response->write($this->render('export.bookmarks')); 78 return $response->write($this->render(TemplatePage::NETSCAPE_EXPORT_BOOKMARKS));
78 } 79 }
79} 80}
diff --git a/application/front/controller/admin/ImportController.php b/application/front/controller/admin/ImportController.php
index 8c5305b9..758d5ef9 100644
--- a/application/front/controller/admin/ImportController.php
+++ b/application/front/controller/admin/ImportController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Psr\Http\Message\UploadedFileInterface; 7use Psr\Http\Message\UploadedFileInterface;
8use Shaarli\Render\TemplatePage;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
10 11
@@ -39,7 +40,7 @@ class ImportController extends ShaarliAdminController
39 ); 40 );
40 $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli')); 41 $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
41 42
42 return $response->write($this->render('import')); 43 return $response->write($this->render(TemplatePage::IMPORT));
43 } 44 }
44 45
45 /** 46 /**
diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php
index bdfc5ca7..3aa48423 100644
--- a/application/front/controller/admin/ManageShaareController.php
+++ b/application/front/controller/admin/ManageShaareController.php
@@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin;
7use Shaarli\Bookmark\Bookmark; 7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException; 8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Formatter\BookmarkMarkdownFormatter; 9use Shaarli\Formatter\BookmarkMarkdownFormatter;
10use Shaarli\Render\TemplatePage;
10use Shaarli\Thumbnailer; 11use Shaarli\Thumbnailer;
11use Slim\Http\Request; 12use Slim\Http\Request;
12use Slim\Http\Response; 13use Slim\Http\Response;
@@ -28,7 +29,7 @@ class ManageShaareController extends ShaarliAdminController
28 t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') 29 t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli')
29 ); 30 );
30 31
31 return $response->write($this->render('addlink')); 32 return $response->write($this->render(TemplatePage::ADDLINK));
32 } 33 }
33 34
34 /** 35 /**
@@ -365,7 +366,7 @@ class ManageShaareController extends ShaarliAdminController
365 $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') 366 $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli')
366 ); 367 );
367 368
368 return $response->write($this->render('editlink')); 369 return $response->write($this->render(TemplatePage::EDIT_LINK));
369 } 370 }
370 371
371 /** 372 /**
diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php
index 7dab288a..0380ef1f 100644
--- a/application/front/controller/admin/ManageTagController.php
+++ b/application/front/controller/admin/ManageTagController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Shaarli\Bookmark\BookmarkFilter; 7use Shaarli\Bookmark\BookmarkFilter;
8use Shaarli\Render\TemplatePage;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
10 11
@@ -28,7 +29,7 @@ class ManageTagController extends ShaarliAdminController
28 t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli') 29 t('Manage tags') .' - '. $this->container->conf->get('general.title', 'Shaarli')
29 ); 30 );
30 31
31 return $response->write($this->render('changetag')); 32 return $response->write($this->render(TemplatePage::CHANGE_TAG));
32 } 33 }
33 34
34 /** 35 /**
diff --git a/application/front/controller/admin/PasswordController.php b/application/front/controller/admin/PasswordController.php
index bcce01a6..5ec0d24b 100644
--- a/application/front/controller/admin/PasswordController.php
+++ b/application/front/controller/admin/PasswordController.php
@@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Admin;
7use Shaarli\Container\ShaarliContainer; 7use Shaarli\Container\ShaarliContainer;
8use Shaarli\Front\Exception\OpenShaarliPasswordException; 8use Shaarli\Front\Exception\OpenShaarliPasswordException;
9use Shaarli\Front\Exception\ShaarliFrontException; 9use Shaarli\Front\Exception\ShaarliFrontException;
10use Shaarli\Render\TemplatePage;
10use Slim\Http\Request; 11use Slim\Http\Request;
11use Slim\Http\Response; 12use Slim\Http\Response;
12use Throwable; 13use Throwable;
@@ -33,7 +34,7 @@ class PasswordController extends ShaarliAdminController
33 */ 34 */
34 public function index(Request $request, Response $response): Response 35 public function index(Request $request, Response $response): Response
35 { 36 {
36 return $response->write($this->render('changepassword')); 37 return $response->write($this->render(TemplatePage::CHANGE_PASSWORD));
37 } 38 }
38 39
39 /** 40 /**
@@ -55,7 +56,7 @@ class PasswordController extends ShaarliAdminController
55 56
56 return $response 57 return $response
57 ->withStatus(400) 58 ->withStatus(400)
58 ->write($this->render('changepassword')) 59 ->write($this->render(TemplatePage::CHANGE_PASSWORD))
59 ; 60 ;
60 } 61 }
61 62
@@ -71,7 +72,7 @@ class PasswordController extends ShaarliAdminController
71 72
72 return $response 73 return $response
73 ->withStatus(400) 74 ->withStatus(400)
74 ->write($this->render('changepassword')) 75 ->write($this->render(TemplatePage::CHANGE_PASSWORD))
75 ; 76 ;
76 } 77 }
77 78
@@ -95,6 +96,6 @@ class PasswordController extends ShaarliAdminController
95 96
96 $this->saveSuccessMessage(t('Your password has been changed')); 97 $this->saveSuccessMessage(t('Your password has been changed'));
97 98
98 return $response->write($this->render('changepassword')); 99 return $response->write($this->render(TemplatePage::CHANGE_PASSWORD));
99 } 100 }
100} 101}
diff --git a/application/front/controller/admin/PluginsController.php b/application/front/controller/admin/PluginsController.php
index d5ec91f0..44025395 100644
--- a/application/front/controller/admin/PluginsController.php
+++ b/application/front/controller/admin/PluginsController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Exception; 7use Exception;
8use Shaarli\Render\TemplatePage;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
10 11
@@ -44,7 +45,7 @@ class PluginsController extends ShaarliAdminController
44 t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli') 45 t('Plugin Administration') .' - '. $this->container->conf->get('general.title', 'Shaarli')
45 ); 46 );
46 47
47 return $response->write($this->render('pluginsadmin')); 48 return $response->write($this->render(TemplatePage::PLUGINS_ADMIN));
48 } 49 }
49 50
50 /** 51 /**
diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php
index e5308510..81c87ed0 100644
--- a/application/front/controller/admin/ThumbnailsController.php
+++ b/application/front/controller/admin/ThumbnailsController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Shaarli\Bookmark\Exception\BookmarkNotFoundException; 7use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
8use Shaarli\Render\TemplatePage;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
10 11
@@ -36,7 +37,7 @@ class ThumbnailsController extends ShaarliAdminController
36 t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli') 37 t('Thumbnails update') .' - '. $this->container->conf->get('general.title', 'Shaarli')
37 ); 38 );
38 39
39 return $response->write($this->render('thumbnails')); 40 return $response->write($this->render(TemplatePage::THUMBNAILS));
40 } 41 }
41 42
42 /** 43 /**
@@ -61,19 +62,4 @@ class ThumbnailsController extends ShaarliAdminController
61 62
62 return $response->withJson($this->container->formatterFactory->getFormatter('raw')->format($bookmark)); 63 return $response->withJson($this->container->formatterFactory->getFormatter('raw')->format($bookmark));
63 } 64 }
64
65 /**
66 * @param mixed[] $data Variables passed to the template engine
67 *
68 * @return mixed[] Template data after active plugins render_picwall hook execution.
69 */
70 protected function executeHooks(array $data): array
71 {
72 $this->container->pluginManager->executeHooks(
73 'render_tools',
74 $data
75 );
76
77 return $data;
78 }
79} 65}
diff --git a/application/front/controller/admin/ToolsController.php b/application/front/controller/admin/ToolsController.php
index d087f2cd..a476e898 100644
--- a/application/front/controller/admin/ToolsController.php
+++ b/application/front/controller/admin/ToolsController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
4 4
5namespace Shaarli\Front\Controller\Admin; 5namespace Shaarli\Front\Controller\Admin;
6 6
7use Shaarli\Render\TemplatePage;
7use Slim\Http\Request; 8use Slim\Http\Request;
8use Slim\Http\Response; 9use Slim\Http\Response;
9 10
@@ -29,7 +30,7 @@ class ToolsController extends ShaarliAdminController
29 30
30 $this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli')); 31 $this->assignView('pagetitle', t('Tools') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
31 32
32 return $response->write($this->render('tools')); 33 return $response->write($this->render(TemplatePage::TOOLS));
33 } 34 }
34 35
35 /** 36 /**
diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php
new file mode 100644
index 00000000..a37a7f6b
--- /dev/null
+++ b/application/front/controller/visitor/BookmarkListController.php
@@ -0,0 +1,248 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Legacy\LegacyController;
10use Shaarli\Legacy\UnknowLegacyRouteException;
11use Shaarli\Render\TemplatePage;
12use Shaarli\Thumbnailer;
13use Slim\Http\Request;
14use Slim\Http\Response;
15
16/**
17 * Class BookmarkListController
18 *
19 * Slim controller used to render the bookmark list, the home page of Shaarli.
20 * It also displays permalinks, and process legacy routes based on GET parameters.
21 */
22class BookmarkListController extends ShaarliVisitorController
23{
24 /**
25 * GET / - Displays the bookmark list, with optional filter parameters.
26 */
27 public function index(Request $request, Response $response): Response
28 {
29 $legacyResponse = $this->processLegacyController($request, $response);
30 if (null !== $legacyResponse) {
31 return $legacyResponse;
32 }
33
34 $formatter = $this->container->formatterFactory->getFormatter();
35
36 $searchTags = escape(normalize_spaces($request->getParam('searchtags') ?? ''));
37 $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));;
38
39 // Filter bookmarks according search parameters.
40 $visibility = $this->container->sessionManager->getSessionParameter('visibility');
41 $search = [
42 'searchtags' => $searchTags,
43 'searchterm' => $searchTerm,
44 ];
45 $linksToDisplay = $this->container->bookmarkService->search(
46 $search,
47 $visibility,
48 false,
49 !!$this->container->sessionManager->getSessionParameter('untaggedonly')
50 ) ?? [];
51
52 // ---- Handle paging.
53 $keys = [];
54 foreach ($linksToDisplay as $key => $value) {
55 $keys[] = $key;
56 }
57
58 $linksPerPage = $this->container->sessionManager->getSessionParameter('LINKS_PER_PAGE', 20) ?: 20;
59
60 // Select articles according to paging.
61 $pageCount = (int) ceil(count($keys) / $linksPerPage) ?: 1;
62 $page = (int) $request->getParam('page') ?? 1;
63 $page = $page < 1 ? 1 : $page;
64 $page = $page > $pageCount ? $pageCount : $page;
65
66 // Start index.
67 $i = ($page - 1) * $linksPerPage;
68 $end = $i + $linksPerPage;
69
70 $linkDisp = [];
71 $save = false;
72 while ($i < $end && $i < count($keys)) {
73 $save = $this->updateThumbnail($linksToDisplay[$keys[$i]], false) || $save;
74 $link = $formatter->format($linksToDisplay[$keys[$i]]);
75
76 $linkDisp[$keys[$i]] = $link;
77 $i++;
78 }
79
80 if ($save) {
81 $this->container->bookmarkService->save();
82 }
83
84 // Compute paging navigation
85 $searchtagsUrl = $searchTags === '' ? '' : '&searchtags=' . urlencode($searchTags);
86 $searchtermUrl = $searchTerm === '' ? '' : '&searchterm=' . urlencode($searchTerm);
87
88 $previous_page_url = '';
89 if ($i !== count($keys)) {
90 $previous_page_url = '?page=' . ($page + 1) . $searchtermUrl . $searchtagsUrl;
91 }
92 $next_page_url = '';
93 if ($page > 1) {
94 $next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl;
95 }
96
97 // Fill all template fields.
98 $data = array_merge(
99 $this->initializeTemplateVars(),
100 [
101 'previous_page_url' => $previous_page_url,
102 'next_page_url' => $next_page_url,
103 'page_current' => $page,
104 'page_max' => $pageCount,
105 'result_count' => count($linksToDisplay),
106 'search_term' => $searchTerm,
107 'search_tags' => $searchTags,
108 'visibility' => $visibility,
109 'links' => $linkDisp,
110 ]
111 );
112
113 if (!empty($searchTerm) || !empty($searchTags)) {
114 $data['pagetitle'] = t('Search: ');
115 $data['pagetitle'] .= ! empty($searchTerm) ? $searchTerm . ' ' : '';
116 $bracketWrap = function ($tag) {
117 return '[' . $tag . ']';
118 };
119 $data['pagetitle'] .= ! empty($searchTags)
120 ? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchTags))) . ' '
121 : '';
122 $data['pagetitle'] .= '- ';
123 }
124
125 $data['pagetitle'] = ($data['pagetitle'] ?? '') . $this->container->conf->get('general.title', 'Shaarli');
126
127 $this->executeHooks($data);
128 $this->assignAllView($data);
129
130 return $response->write($this->render(TemplatePage::LINKLIST));
131 }
132
133 /**
134 * GET /shaare/{hash} - Display a single shaare
135 */
136 public function permalink(Request $request, Response $response, array $args): Response
137 {
138 try {
139 $bookmark = $this->container->bookmarkService->findByHash($args['hash']);
140 } catch (BookmarkNotFoundException $e) {
141 $this->assignView('error_message', $e->getMessage());
142
143 return $response->write($this->render(TemplatePage::ERROR_404));
144 }
145
146 $this->updateThumbnail($bookmark);
147
148 $data = array_merge(
149 $this->initializeTemplateVars(),
150 [
151 'pagetitle' => $bookmark->getTitle() .' - '. $this->container->conf->get('general.title', 'Shaarli'),
152 'links' => [$this->container->formatterFactory->getFormatter()->format($bookmark)],
153 ]
154 );
155
156 $this->executeHooks($data);
157 $this->assignAllView($data);
158
159 return $response->write($this->render(TemplatePage::LINKLIST));
160 }
161
162 /**
163 * Update the thumbnail of a single bookmark if necessary.
164 */
165 protected function updateThumbnail(Bookmark $bookmark, bool $writeDatastore = true): bool
166 {
167 // Logged in, thumbnails enabled, not a note, is HTTP
168 // and (never retrieved yet or no valid cache file)
169 if ($this->container->loginManager->isLoggedIn()
170 && $this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
171 && false !== $bookmark->getThumbnail()
172 && !$bookmark->isNote()
173 && (null === $bookmark->getThumbnail() || !is_file($bookmark->getThumbnail()))
174 && startsWith(strtolower($bookmark->getUrl()), 'http')
175 ) {
176 $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl()));
177 $this->container->bookmarkService->set($bookmark, $writeDatastore);
178
179 return true;
180 }
181
182 return false;
183 }
184
185 /**
186 * @param mixed[] $data Template vars to process in plugins, passed as reference.
187 */
188 protected function executeHooks(array &$data): void
189 {
190 $this->container->pluginManager->executeHooks(
191 'render_linklist',
192 $data,
193 ['loggedin' => $this->container->loginManager->isLoggedIn()]
194 );
195 }
196
197 /**
198 * @return string[] Default template variables without values.
199 */
200 protected function initializeTemplateVars(): array
201 {
202 return [
203 'previous_page_url' => '',
204 'next_page_url' => '',
205 'page_max' => '',
206 'search_tags' => '',
207 'result_count' => '',
208 ];
209 }
210
211 /**
212 * Process legacy routes if necessary. They used query parameters.
213 * If no legacy routes is passed, return null.
214 */
215 protected function processLegacyController(Request $request, Response $response): ?Response
216 {
217 // Legacy smallhash filter
218 $queryString = $this->container->environment['QUERY_STRING'] ?? null;
219 if (null !== $queryString && 1 === preg_match('/^([a-zA-Z0-9-_@]{6})($|&|#)/', $queryString, $match)) {
220 return $this->redirect($response, '/shaare/' . $match[1]);
221 }
222
223 // Legacy controllers (mostly used for redirections)
224 if (null !== $request->getQueryParam('do')) {
225 $legacyController = new LegacyController($this->container);
226
227 try {
228 return $legacyController->process($request, $response, $request->getQueryParam('do'));
229 } catch (UnknowLegacyRouteException $e) {
230 // We ignore legacy 404
231 return null;
232 }
233 }
234
235 // Legacy GET admin routes
236 $legacyGetRoutes = array_intersect(
237 LegacyController::LEGACY_GET_ROUTES,
238 array_keys($request->getQueryParams() ?? [])
239 );
240 if (1 === count($legacyGetRoutes)) {
241 $legacyController = new LegacyController($this->container);
242
243 return $legacyController->process($request, $response, $legacyGetRoutes[0]);
244 }
245
246 return null;
247 }
248}
diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php
index e5c9ddac..05b4f095 100644
--- a/application/front/controller/visitor/DailyController.php
+++ b/application/front/controller/visitor/DailyController.php
@@ -7,6 +7,7 @@ namespace Shaarli\Front\Controller\Visitor;
7use DateTime; 7use DateTime;
8use DateTimeImmutable; 8use DateTimeImmutable;
9use Shaarli\Bookmark\Bookmark; 9use Shaarli\Bookmark\Bookmark;
10use Shaarli\Render\TemplatePage;
10use Slim\Http\Request; 11use Slim\Http\Request;
11use Slim\Http\Response; 12use Slim\Http\Response;
12 13
@@ -85,7 +86,7 @@ class DailyController extends ShaarliVisitorController
85 t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle 86 t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle
86 ); 87 );
87 88
88 return $response->write($this->render('daily')); 89 return $response->write($this->render(TemplatePage::DAILY));
89 } 90 }
90 91
91 /** 92 /**
@@ -152,7 +153,7 @@ class DailyController extends ShaarliVisitorController
152 $this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false)); 153 $this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false));
153 $this->assignView('days', $dataPerDay); 154 $this->assignView('days', $dataPerDay);
154 155
155 $rssContent = $this->render('dailyrss'); 156 $rssContent = $this->render(TemplatePage::DAILY_RSS);
156 157
157 $cache->cache($rssContent); 158 $cache->cache($rssContent);
158 159
diff --git a/application/front/controller/visitor/LoginController.php b/application/front/controller/visitor/LoginController.php
index 0db1f463..a257766f 100644
--- a/application/front/controller/visitor/LoginController.php
+++ b/application/front/controller/visitor/LoginController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Visitor; 5namespace Shaarli\Front\Controller\Visitor;
6 6
7use Shaarli\Front\Exception\LoginBannedException; 7use Shaarli\Front\Exception\LoginBannedException;
8use Shaarli\Render\TemplatePage;
8use Slim\Http\Request; 9use Slim\Http\Request;
9use Slim\Http\Response; 10use Slim\Http\Response;
10 11
@@ -41,6 +42,6 @@ class LoginController extends ShaarliVisitorController
41 ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli')) 42 ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli'))
42 ; 43 ;
43 44
44 return $response->write($this->render('loginform')); 45 return $response->write($this->render(TemplatePage::LOGIN));
45 } 46 }
46} 47}
diff --git a/application/front/controller/visitor/OpenSearchController.php b/application/front/controller/visitor/OpenSearchController.php
index 0fd68db6..36d60acf 100644
--- a/application/front/controller/visitor/OpenSearchController.php
+++ b/application/front/controller/visitor/OpenSearchController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
4 4
5namespace Shaarli\Front\Controller\Visitor; 5namespace Shaarli\Front\Controller\Visitor;
6 6
7use Shaarli\Render\TemplatePage;
7use Slim\Http\Request; 8use Slim\Http\Request;
8use Slim\Http\Response; 9use Slim\Http\Response;
9 10
@@ -21,6 +22,6 @@ class OpenSearchController extends ShaarliVisitorController
21 22
22 $this->assignView('serverurl', index_url($this->container->environment)); 23 $this->assignView('serverurl', index_url($this->container->environment));
23 24
24 return $response->write($this->render('opensearch')); 25 return $response->write($this->render(TemplatePage::OPEN_SEARCH));
25 } 26 }
26} 27}
diff --git a/application/front/controller/visitor/PictureWallController.php b/application/front/controller/visitor/PictureWallController.php
index 4e1dce8c..5ef2cb17 100644
--- a/application/front/controller/visitor/PictureWallController.php
+++ b/application/front/controller/visitor/PictureWallController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Visitor; 5namespace Shaarli\Front\Controller\Visitor;
6 6
7use Shaarli\Front\Exception\ThumbnailsDisabledException; 7use Shaarli\Front\Exception\ThumbnailsDisabledException;
8use Shaarli\Render\TemplatePage;
8use Shaarli\Thumbnailer; 9use Shaarli\Thumbnailer;
9use Slim\Http\Request; 10use Slim\Http\Request;
10use Slim\Http\Response; 11use Slim\Http\Response;
@@ -46,7 +47,7 @@ class PictureWallController extends ShaarliVisitorController
46 $this->assignView($key, $value); 47 $this->assignView($key, $value);
47 } 48 }
48 49
49 return $response->write($this->render('picwall')); 50 return $response->write($this->render(TemplatePage::PICTURE_WALL));
50 } 51 }
51 52
52 /** 53 /**
diff --git a/application/legacy/LegacyController.php b/application/legacy/LegacyController.php
new file mode 100644
index 00000000..a97b07b1
--- /dev/null
+++ b/application/legacy/LegacyController.php
@@ -0,0 +1,130 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Legacy;
6
7use Shaarli\Feed\FeedBuilder;
8use Shaarli\Front\Controller\Visitor\ShaarliVisitorController;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * We use this to maintain legacy routes, and redirect requests to the corresponding Slim route.
14 * Only public routes, and both `?addlink` and `?post` were kept here.
15 * Other routes will just display the linklist.
16 *
17 * @deprecated
18 */
19class LegacyController extends ShaarliVisitorController
20{
21 /** @var string[] Both `?post` and `?addlink` do not use `?do=` format. */
22 public const LEGACY_GET_ROUTES = [
23 'post',
24 'addlink',
25 ];
26
27 /**
28 * This method will call `$action` method, which will redirect to corresponding Slim route.
29 */
30 public function process(Request $request, Response $response, string $action): Response
31 {
32 if (!method_exists($this, $action)) {
33 throw new UnknowLegacyRouteException();
34 }
35
36 return $this->{$action}($request, $response);
37 }
38
39 /** Legacy route: ?post= */
40 public function post(Request $request, Response $response): Response
41 {
42 $parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
43
44 if (!$this->container->loginManager->isLoggedIn()) {
45 return $this->redirect($response, '/login' . $parameters);
46 }
47
48 return $this->redirect($response, '/admin/shaare' . $parameters);
49 }
50
51 /** Legacy route: ?addlink= */
52 protected function addlink(Request $request, Response $response): Response
53 {
54 if (!$this->container->loginManager->isLoggedIn()) {
55 return $this->redirect($response, '/login');
56 }
57
58 return $this->redirect($response, '/admin/add-shaare');
59 }
60
61 /** Legacy route: ?do=login */
62 protected function login(Request $request, Response $response): Response
63 {
64 return $this->redirect($response, '/login');
65 }
66
67 /** Legacy route: ?do=logout */
68 protected function logout(Request $request, Response $response): Response
69 {
70 return $this->redirect($response, '/logout');
71 }
72
73 /** Legacy route: ?do=picwall */
74 protected function picwall(Request $request, Response $response): Response
75 {
76 return $this->redirect($response, '/picture-wall');
77 }
78
79 /** Legacy route: ?do=tagcloud */
80 protected function tagcloud(Request $request, Response $response): Response
81 {
82 return $this->redirect($response, '/tags/cloud');
83 }
84
85 /** Legacy route: ?do=taglist */
86 protected function taglist(Request $request, Response $response): Response
87 {
88 return $this->redirect($response, '/tags/list');
89 }
90
91 /** Legacy route: ?do=daily */
92 protected function daily(Request $request, Response $response): Response
93 {
94 $dayParam = !empty($request->getParam('day')) ? '?day=' . escape($request->getParam('day')) : '';
95
96 return $this->redirect($response, '/daily' . $dayParam);
97 }
98
99 /** Legacy route: ?do=rss */
100 protected function rss(Request $request, Response $response): Response
101 {
102 return $this->feed($request, $response, FeedBuilder::$FEED_RSS);
103 }
104
105 /** Legacy route: ?do=atom */
106 protected function atom(Request $request, Response $response): Response
107 {
108 return $this->feed($request, $response, FeedBuilder::$FEED_ATOM);
109 }
110
111 /** Legacy route: ?do=opensearch */
112 protected function opensearch(Request $request, Response $response): Response
113 {
114 return $this->redirect($response, '/open-search');
115 }
116
117 /** Legacy route: ?do=dailyrss */
118 protected function dailyrss(Request $request, Response $response): Response
119 {
120 return $this->redirect($response, '/daily-rss');
121 }
122
123 /** Legacy route: ?do=feed */
124 protected function feed(Request $request, Response $response, string $feedType): Response
125 {
126 $parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
127
128 return $this->redirect($response, '/feed/' . $feedType . $parameters);
129 }
130}
diff --git a/application/Router.php b/application/legacy/LegacyRouter.php
index d7187487..cea99154 100644
--- a/application/Router.php
+++ b/application/legacy/LegacyRouter.php
@@ -1,12 +1,15 @@
1<?php 1<?php
2namespace Shaarli; 2
3namespace Shaarli\Legacy;
3 4
4/** 5/**
5 * Class Router 6 * Class Router
6 * 7 *
7 * (only displayable pages here) 8 * (only displayable pages here)
9 *
10 * @deprecated
8 */ 11 */
9class Router 12class LegacyRouter
10{ 13{
11 public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update'; 14 public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';
12 15
diff --git a/application/legacy/UnknowLegacyRouteException.php b/application/legacy/UnknowLegacyRouteException.php
new file mode 100644
index 00000000..ae1518ad
--- /dev/null
+++ b/application/legacy/UnknowLegacyRouteException.php
@@ -0,0 +1,9 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Legacy;
6
7class UnknowLegacyRouteException extends \Exception
8{
9}
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php
index 2779eb90..85e1d59d 100644
--- a/application/render/PageBuilder.php
+++ b/application/render/PageBuilder.php
@@ -70,6 +70,15 @@ class PageBuilder
70 } 70 }
71 71
72 /** 72 /**
73 * Reset current state of template rendering.
74 * Mostly useful for error handling. We remove everything, and display the error template.
75 */
76 public function reset(): void
77 {
78 $this->tpl = false;
79 }
80
81 /**
73 * Initialize all default tpl tags. 82 * Initialize all default tpl tags.
74 */ 83 */
75 private function initialize() 84 private function initialize()
diff --git a/application/render/TemplatePage.php b/application/render/TemplatePage.php
new file mode 100644
index 00000000..8af8228a
--- /dev/null
+++ b/application/render/TemplatePage.php
@@ -0,0 +1,33 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Render;
6
7interface TemplatePage
8{
9 public const ERROR_404 = '404';
10 public const ADDLINK = 'addlink';
11 public const CHANGE_PASSWORD = 'changepassword';
12 public const CHANGE_TAG = 'changetag';
13 public const CONFIGURE = 'configure';
14 public const DAILY = 'daily';
15 public const DAILY_RSS = 'dailyrss';
16 public const EDIT_LINK = 'editlink';
17 public const ERROR = 'error';
18 public const EXPORT = 'export';
19 public const NETSCAPE_EXPORT_BOOKMARKS = 'export.bookmarks';
20 public const FEED_ATOM = 'feed.atom';
21 public const FEED_RSS = 'feed.rss';
22 public const IMPORT = 'import';
23 public const INSTALL = 'install';
24 public const LINKLIST = 'linklist';
25 public const LOGIN = 'loginform';
26 public const OPEN_SEARCH = 'opensearch';
27 public const PICTURE_WALL = 'picwall';
28 public const PLUGINS_ADMIN = 'pluginsadmin';
29 public const TAG_CLOUD = 'tag.cloud';
30 public const TAG_LIST = 'tag.list';
31 public const THUMBNAILS = 'thumbnails';
32 public const TOOLS = 'tools';
33}
diff --git a/application/updater/Updater.php b/application/updater/Updater.php
index 4bbcb9f2..f73a7452 100644
--- a/application/updater/Updater.php
+++ b/application/updater/Updater.php
@@ -21,7 +21,7 @@ class Updater
21 /** 21 /**
22 * @var BookmarkServiceInterface instance. 22 * @var BookmarkServiceInterface instance.
23 */ 23 */
24 protected $linkServices; 24 protected $bookmarkService;
25 25
26 /** 26 /**
27 * @var ConfigManager $conf Configuration Manager instance. 27 * @var ConfigManager $conf Configuration Manager instance.
@@ -49,7 +49,7 @@ class Updater
49 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) 49 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
50 { 50 {
51 $this->doneUpdates = $doneUpdates; 51 $this->doneUpdates = $doneUpdates;
52 $this->linkServices = $linkDB; 52 $this->bookmarkService = $linkDB;
53 $this->conf = $conf; 53 $this->conf = $conf;
54 $this->isLoggedIn = $isLoggedIn; 54 $this->isLoggedIn = $isLoggedIn;
55 55
@@ -68,7 +68,7 @@ class Updater
68 */ 68 */
69 public function update() 69 public function update()
70 { 70 {
71 $updatesRan = array(); 71 $updatesRan = [];
72 72
73 // If the user isn't logged in, exit without updating. 73 // If the user isn't logged in, exit without updating.
74 if ($this->isLoggedIn !== true) { 74 if ($this->isLoggedIn !== true) {
@@ -112,6 +112,16 @@ class Updater
112 return $this->doneUpdates; 112 return $this->doneUpdates;
113 } 113 }
114 114
115 public function readUpdates(string $updatesFilepath): array
116 {
117 return UpdaterUtils::read_updates_file($updatesFilepath);
118 }
119
120 public function writeUpdates(string $updatesFilepath, array $updates): void
121 {
122 UpdaterUtils::write_updates_file($updatesFilepath, $updates);
123 }
124
115 /** 125 /**
116 * With the Slim routing system, default header link should be `./` instead of `?`. 126 * With the Slim routing system, default header link should be `./` instead of `?`.
117 * Otherwise you can not go back to the home page. Example: `/picture-wall` -> `/picture-wall?` instead of `/`. 127 * Otherwise you can not go back to the home page. Example: `/picture-wall` -> `/picture-wall?` instead of `/`.
@@ -127,4 +137,31 @@ class Updater
127 137
128 return true; 138 return true;
129 } 139 }
140
141 /**
142 * With the Slim routing system, note bookmarks URL formatted `?abcdef`
143 * should be replaced with `/shaare/abcdef`
144 */
145 public function updateMethodMigrateExistingNotesUrl(): bool
146 {
147 $updated = false;
148
149 foreach ($this->bookmarkService->search() as $bookmark) {
150 if ($bookmark->isNote()
151 && startsWith($bookmark->getUrl(), '?')
152 && 1 === preg_match('/^\?([a-zA-Z0-9-_@]{6})($|&|#)/', $bookmark->getUrl(), $match)
153 ) {
154 $updated = true;
155 $bookmark = $bookmark->setUrl('/shaare/' . $match[1]);
156
157 $this->bookmarkService->set($bookmark, false);
158 }
159 }
160
161 if ($updated) {
162 $this->bookmarkService->save();
163 }
164
165 return true;
166 }
130} 167}
diff --git a/doc/md/Plugin-System.md b/doc/md/Plugin-System.md
index d5b16e2d..f264e873 100644
--- a/doc/md/Plugin-System.md
+++ b/doc/md/Plugin-System.md
@@ -131,7 +131,7 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha
131| ------------- |:-------------:| 131| ------------- |:-------------:|
132| [render_header](#render_header) | Allow plugin to add content in page headers. | 132| [render_header](#render_header) | Allow plugin to add content in page headers. |
133| [render_includes](#render_includes) | Allow plugin to include their own CSS files. | 133| [render_includes](#render_includes) | Allow plugin to include their own CSS files. |
134| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. | 134| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
135| [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. | 135| [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. |
136| [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. | 136| [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |
137| [render_tools](#render_tools) | Allow to add content at the end of the page. | 137| [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:
515 515
516### Placeholder system 516### Placeholder system
517 517
518In order to make plugins work with every custom themes, you need to add variable placeholder in your templates. 518In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
519 519
520It's a RainTPL loop like this: 520It's a RainTPL loop like this:
521 521
@@ -537,7 +537,7 @@ At the end of the menu:
537 537
538At the end of file, before clearing floating blocks: 538At the end of file, before clearing floating blocks:
539 539
540 {if="!empty($plugin_errors) && isLoggedIn()"} 540 {if="!empty($plugin_errors) && $is_logged_in"}
541 <ul class="errors"> 541 <ul class="errors">
542 {loop="plugin_errors"} 542 {loop="plugin_errors"}
543 <li>{$value}</li> 543 <li>{$value}</li>
diff --git a/index.php b/index.php
index a07de74d..2737c22c 100644
--- a/index.php
+++ b/index.php
@@ -61,30 +61,15 @@ require_once 'application/TimeZone.php';
61require_once 'application/Utils.php'; 61require_once 'application/Utils.php';
62 62
63use Shaarli\ApplicationUtils; 63use Shaarli\ApplicationUtils;
64use Shaarli\Bookmark\Bookmark;
65use Shaarli\Bookmark\BookmarkFileService; 64use Shaarli\Bookmark\BookmarkFileService;
66use Shaarli\Bookmark\BookmarkFilter;
67use Shaarli\Bookmark\BookmarkServiceInterface;
68use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
69use Shaarli\Config\ConfigManager; 65use Shaarli\Config\ConfigManager;
70use Shaarli\Container\ContainerBuilder; 66use Shaarli\Container\ContainerBuilder;
71use Shaarli\Feed\CachedPage;
72use Shaarli\Feed\FeedBuilder;
73use Shaarli\Formatter\BookmarkMarkdownFormatter;
74use Shaarli\Formatter\FormatterFactory;
75use Shaarli\History; 67use Shaarli\History;
76use Shaarli\Languages; 68use Shaarli\Languages;
77use Shaarli\Netscape\NetscapeBookmarkUtils;
78use Shaarli\Plugin\PluginManager; 69use Shaarli\Plugin\PluginManager;
79use Shaarli\Render\PageBuilder; 70use Shaarli\Render\PageBuilder;
80use Shaarli\Render\PageCacheManager;
81use Shaarli\Render\ThemeUtils;
82use Shaarli\Router;
83use Shaarli\Security\LoginManager; 71use Shaarli\Security\LoginManager;
84use Shaarli\Security\SessionManager; 72use Shaarli\Security\SessionManager;
85use Shaarli\Thumbnailer;
86use Shaarli\Updater\Updater;
87use Shaarli\Updater\UpdaterUtils;
88use Slim\App; 73use Slim\App;
89 74
90// Ensure the PHP version is supported 75// Ensure the PHP version is supported
@@ -196,20 +181,6 @@ if (! is_file($conf->getConfigFileExt())) {
196 181
197$loginManager->checkLoginState($_COOKIE, $clientIpId); 182$loginManager->checkLoginState($_COOKIE, $clientIpId);
198 183
199/**
200 * Adapter function to ensure compatibility with third-party templates
201 *
202 * @see https://github.com/shaarli/Shaarli/pull/1086
203 *
204 * @return bool true when the user is logged in, false otherwise
205 */
206function isLoggedIn()
207{
208 global $loginManager;
209 return $loginManager->isLoggedIn();
210}
211
212
213// ------------------------------------------------------------------------------------------ 184// ------------------------------------------------------------------------------------------
214// Process login form: Check if login/password is correct. 185// Process login form: Check if login/password is correct.
215if (isset($_POST['login'])) { 186if (isset($_POST['login'])) {
@@ -301,473 +272,6 @@ if (!isset($_SESSION['tokens'])) {
301} 272}
302 273
303/** 274/**
304 * Renders the linklist
305 *
306 * @param pageBuilder $PAGE pageBuilder instance.
307 * @param BookmarkServiceInterface $linkDb instance.
308 * @param ConfigManager $conf Configuration Manager instance.
309 * @param PluginManager $pluginManager Plugin Manager instance.
310 */
311function showLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
312{
313 buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager);
314 $PAGE->renderPage('linklist');
315}
316
317/**
318 * Render HTML page (according to URL parameters and user rights)
319 *
320 * @param ConfigManager $conf Configuration Manager instance.
321 * @param PluginManager $pluginManager Plugin Manager instance,
322 * @param BookmarkServiceInterface $bookmarkService
323 * @param History $history instance
324 * @param SessionManager $sessionManager SessionManager instance
325 * @param LoginManager $loginManager LoginManager instance
326 */
327function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionManager, $loginManager)
328{
329 $pageCacheManager = new PageCacheManager($conf->get('resource.page_cache'), $loginManager->isLoggedIn());
330 $updater = new Updater(
331 UpdaterUtils::read_updates_file($conf->get('resource.updates')),
332 $bookmarkService,
333 $conf,
334 $loginManager->isLoggedIn()
335 );
336 try {
337 $newUpdates = $updater->update();
338 if (! empty($newUpdates)) {
339 UpdaterUtils::write_updates_file(
340 $conf->get('resource.updates'),
341 $updater->getDoneUpdates()
342 );
343
344 $pageCacheManager->invalidateCaches();
345 }
346 } catch (Exception $e) {
347 die($e->getMessage());
348 }
349
350 $PAGE = new PageBuilder($conf, $_SESSION, $bookmarkService, $sessionManager->generateToken(), $loginManager->isLoggedIn());
351 $PAGE->assign('linkcount', $bookmarkService->count(BookmarkFilter::$ALL));
352 $PAGE->assign('privateLinkcount', $bookmarkService->count(BookmarkFilter::$PRIVATE));
353 $PAGE->assign('plugin_errors', $pluginManager->getErrors());
354
355 // Determine which page will be rendered.
356 $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
357 $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn());
358
359 if (// if the user isn't logged in
360 !$loginManager->isLoggedIn() &&
361 // and Shaarli doesn't have public content...
362 $conf->get('privacy.hide_public_links') &&
363 // and is configured to enforce the login
364 $conf->get('privacy.force_login') &&
365 // and the current page isn't already the login page
366 $targetPage !== Router::$PAGE_LOGIN &&
367 // and the user is not requesting a feed (which would lead to a different content-type as expected)
368 $targetPage !== Router::$PAGE_FEED_ATOM &&
369 $targetPage !== Router::$PAGE_FEED_RSS
370 ) {
371 // force current page to be the login page
372 $targetPage = Router::$PAGE_LOGIN;
373 }
374
375 // Call plugin hooks for header, footer and includes, specifying which page will be rendered.
376 // Then assign generated data to RainTPL.
377 $common_hooks = array(
378 'includes',
379 'header',
380 'footer',
381 );
382
383 foreach ($common_hooks as $name) {
384 $plugin_data = array();
385 $pluginManager->executeHooks(
386 'render_' . $name,
387 $plugin_data,
388 array(
389 'target' => $targetPage,
390 'loggedin' => $loginManager->isLoggedIn()
391 )
392 );
393 $PAGE->assign('plugins_' . $name, $plugin_data);
394 }
395
396 // -------- Display login form.
397 if ($targetPage == Router::$PAGE_LOGIN) {
398 header('Location: ./login');
399 exit;
400 }
401 // -------- User wants to logout.
402 if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) {
403 header('Location: ./logout');
404 exit;
405 }
406
407 // -------- Picture wall
408 if ($targetPage == Router::$PAGE_PICWALL) {
409 header('Location: ./picture-wall');
410 exit;
411 }
412
413 // -------- Tag cloud
414 if ($targetPage == Router::$PAGE_TAGCLOUD) {
415 header('Location: ./tags/cloud');
416 exit;
417 }
418
419 // -------- Tag list
420 if ($targetPage == Router::$PAGE_TAGLIST) {
421 header('Location: ./tags/list');
422 exit;
423 }
424
425 // Daily page.
426 if ($targetPage == Router::$PAGE_DAILY) {
427 $dayParam = !empty($_GET['day']) ? '?day=' . escape($_GET['day']) : '';
428 header('Location: ./daily'. $dayParam);
429 exit;
430 }
431
432 // ATOM and RSS feed.
433 if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) {
434 $feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
435
436 header('Location: ./feed/'. $feedType .'?'. http_build_query($_GET));
437 exit;
438 }
439
440 // Display opensearch plugin (XML)
441 if ($targetPage == Router::$PAGE_OPENSEARCH) {
442 header('Location: ./open-search');
443 exit;
444 }
445
446 // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
447 if (isset($_GET['addtag'])) {
448 header('Location: ./add-tag/'. $_GET['addtag']);
449 exit;
450 }
451
452 // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
453 if (isset($_GET['removetag'])) {
454 header('Location: ./remove-tag/'. $_GET['removetag']);
455 exit;
456 }
457
458 // -------- User wants to change the number of bookmarks per page (linksperpage=...)
459 if (isset($_GET['linksperpage'])) {
460 header('Location: ./links-per-page?nb='. $_GET['linksperpage']);
461 exit;
462 }
463
464 // -------- User wants to see only private bookmarks (toggle)
465 if (isset($_GET['visibility'])) {
466 header('Location: ./visibility/'. $_GET['visibility']);
467 exit;
468 }
469
470 // -------- User wants to see only untagged bookmarks (toggle)
471 if (isset($_GET['untaggedonly'])) {
472 header('Location: ./untagged-only');
473 exit;
474 }
475
476 // -------- Handle other actions allowed for non-logged in users:
477 if (!$loginManager->isLoggedIn()) {
478 // User tries to post new link but is not logged in:
479 // Show login screen, then redirect to ?post=...
480 if (isset($_GET['post'])) {
481 header( // Redirect to login page, then back to post link.
482 'Location: ./login?post='.urlencode($_GET['post']).
483 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').
484 (!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').
485 (!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):'').
486 (!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')
487 );
488 exit;
489 }
490
491 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
492 if (isset($_GET['edit_link'])) {
493 header('Location: ./login?edit_link='. escape($_GET['edit_link']));
494 exit;
495 }
496
497 exit; // Never remove this one! All operations below are reserved for logged in user.
498 }
499
500 // -------- All other functions are reserved for the registered user:
501
502 // TODO: Remove legacy admin route redirections. We'll only keep public URL.
503
504 // -------- Display the Tools menu if requested (import/export/bookmarklet...)
505 if ($targetPage == Router::$PAGE_TOOLS) {
506 header('Location: ./admin/tools');
507 exit;
508 }
509
510 // -------- User wants to change his/her password.
511 if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
512 header('Location: ./admin/password');
513 exit;
514 }
515
516 // -------- User wants to change configuration
517 if ($targetPage == Router::$PAGE_CONFIGURE) {
518 header('Location: ./admin/configure');
519 exit;
520 }
521
522 // -------- User wants to rename a tag or delete it
523 if ($targetPage == Router::$PAGE_CHANGETAG) {
524 header('Location: ./admin/tags');
525 exit;
526 }
527
528 // -------- User wants to add a link without using the bookmarklet: Show form.
529 if ($targetPage == Router::$PAGE_ADDLINK) {
530 header('Location: ./admin/shaare');
531 exit;
532 }
533
534 // -------- User clicked the "Save" button when editing a link: Save link to database.
535 if (isset($_POST['save_edit'])) {
536 // This route is no longer supported in legacy mode
537 header('Location: ./');
538 exit;
539 }
540
541 // -------- User clicked the "Delete" button when editing a link: Delete link from database.
542 if ($targetPage == Router::$PAGE_DELETELINK) {
543 $ids = $_GET['lf_linkdate'] ?? '';
544 $token = $_GET['token'] ?? '';
545
546 header('Location: ./admin/shaare/delete?id=' . $ids . '&token=' . $token);
547 exit;
548 }
549
550 // -------- User clicked either "Set public" or "Set private" bulk operation
551 if ($targetPage == Router::$PAGE_CHANGE_VISIBILITY) {
552 header('Location: ./admin/shaare/visibility?id=' . $_GET['token']);
553 exit;
554 }
555
556 // -------- User clicked the "EDIT" button on a link: Display link edit form.
557 if (isset($_GET['edit_link'])) {
558 $id = (int) escape($_GET['edit_link']);
559 header('Location: ./admin/shaare/' . $id);
560 exit;
561 }
562
563 // -------- User want to post a new link: Display link edit form.
564 if (isset($_GET['post'])) {
565 header('Location: ./admin/shaare?' . http_build_query($_GET));
566 exit;
567 }
568
569 if ($targetPage == Router::$PAGE_PINLINK) {
570 // This route is no longer supported in legacy mode
571 header('Location: ./');
572 exit;
573 }
574
575 if ($targetPage == Router::$PAGE_EXPORT) {
576 header('Location: ./admin/export');
577 exit;
578 }
579
580 if ($targetPage == Router::$PAGE_IMPORT) {
581 header('Location: ./admin/import');
582 exit;
583 }
584
585 // Plugin administration page
586 if ($targetPage == Router::$PAGE_PLUGINSADMIN) {
587 header('Location: ./admin/plugins');
588 exit;
589 }
590
591 // Plugin administration form action
592 if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
593 // This route is no longer supported in legacy mode
594 header('Location: ./admin/plugins');
595 exit;
596 }
597
598 // Get a fresh token
599 if ($targetPage == Router::$GET_TOKEN) {
600 header('Location: ./admin/token');
601 exit;
602 }
603
604 // -------- Thumbnails Update
605 if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
606 header('Location: ./admin/thumbnails');
607 exit;
608 }
609
610 // -------- Single Thumbnail Update
611 if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
612 // This route is no longer supported in legacy mode
613 http_response_code(404);
614 exit;
615 }
616
617 // -------- Otherwise, simply display search form and bookmarks:
618 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
619 exit;
620}
621
622/**
623 * Template for the list of bookmarks (<div id="linklist">)
624 * This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
625 *
626 * @param pageBuilder $PAGE pageBuilder instance.
627 * @param BookmarkServiceInterface $linkDb LinkDB instance.
628 * @param ConfigManager $conf Configuration Manager instance.
629 * @param PluginManager $pluginManager Plugin Manager instance.
630 * @param LoginManager $loginManager LoginManager instance
631 */
632function buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
633{
634 $factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
635 $formatter = $factory->getFormatter();
636
637 // Used in templates
638 if (isset($_GET['searchtags'])) {
639 if (! empty($_GET['searchtags'])) {
640 $searchtags = escape(normalize_spaces($_GET['searchtags']));
641 } else {
642 $searchtags = false;
643 }
644 } else {
645 $searchtags = '';
646 }
647 $searchterm = !empty($_GET['searchterm']) ? escape(normalize_spaces($_GET['searchterm'])) : '';
648
649 // Smallhash filter
650 if (! empty($_SERVER['QUERY_STRING'])
651 && preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) {
652 try {
653 $linksToDisplay = $linkDb->findByHash($_SERVER['QUERY_STRING']);
654 } catch (BookmarkNotFoundException $e) {
655 $PAGE->render404($e->getMessage());
656 exit;
657 }
658 } else {
659 // Filter bookmarks according search parameters.
660 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : null;
661 $request = [
662 'searchtags' => $searchtags,
663 'searchterm' => $searchterm,
664 ];
665 $linksToDisplay = $linkDb->search($request, $visibility, false, !empty($_SESSION['untaggedonly']));
666 }
667
668 // ---- Handle paging.
669 $keys = array();
670 foreach ($linksToDisplay as $key => $value) {
671 $keys[] = $key;
672 }
673
674 // Select articles according to paging.
675 $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
676 $pagecount = $pagecount == 0 ? 1 : $pagecount;
677 $page= empty($_GET['page']) ? 1 : intval($_GET['page']);
678 $page = $page < 1 ? 1 : $page;
679 $page = $page > $pagecount ? $pagecount : $page;
680 // Start index.
681 $i = ($page-1) * $_SESSION['LINKS_PER_PAGE'];
682 $end = $i + $_SESSION['LINKS_PER_PAGE'];
683
684 $thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE;
685 if ($thumbnailsEnabled) {
686 $thumbnailer = new Thumbnailer($conf);
687 }
688
689 $linkDisp = array();
690 while ($i<$end && $i<count($keys)) {
691 $link = $formatter->format($linksToDisplay[$keys[$i]]);
692
693 // Logged in, thumbnails enabled, not a note,
694 // and (never retrieved yet or no valid cache file)
695 if ($loginManager->isLoggedIn()
696 && $thumbnailsEnabled
697 && !$linksToDisplay[$keys[$i]]->isNote()
698 && $linksToDisplay[$keys[$i]]->getThumbnail() !== false
699 && ! is_file($linksToDisplay[$keys[$i]]->getThumbnail())
700 ) {
701 $linksToDisplay[$keys[$i]]->setThumbnail($thumbnailer->get($link['url']));
702 $linkDb->set($linksToDisplay[$keys[$i]], false);
703 $updateDB = true;
704 $link['thumbnail'] = $linksToDisplay[$keys[$i]]->getThumbnail();
705 }
706
707 // Check for both signs of a note: starting with ? and 7 chars long.
708// if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
709// $link['url'] = index_url($_SERVER) . $link['url'];
710// }
711
712 $linkDisp[$keys[$i]] = $link;
713 $i++;
714 }
715
716 // If we retrieved new thumbnails, we update the database.
717 if (!empty($updateDB)) {
718 $linkDb->save();
719 }
720
721 // Compute paging navigation
722 $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags);
723 $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm);
724 $previous_page_url = '';
725 if ($i != count($keys)) {
726 $previous_page_url = '?page=' . ($page+1) . $searchtermUrl . $searchtagsUrl;
727 }
728 $next_page_url='';
729 if ($page>1) {
730 $next_page_url = '?page=' . ($page-1) . $searchtermUrl . $searchtagsUrl;
731 }
732
733 // Fill all template fields.
734 $data = array(
735 'previous_page_url' => $previous_page_url,
736 'next_page_url' => $next_page_url,
737 'page_current' => $page,
738 'page_max' => $pagecount,
739 'result_count' => count($linksToDisplay),
740 'search_term' => $searchterm,
741 'search_tags' => $searchtags,
742 'visibility' => ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '',
743 'links' => $linkDisp,
744 );
745
746 // If there is only a single link, we change on-the-fly the title of the page.
747 if (count($linksToDisplay) == 1) {
748 $data['pagetitle'] = $linksToDisplay[$keys[0]]->getTitle() .' - '. $conf->get('general.title');
749 } elseif (! empty($searchterm) || ! empty($searchtags)) {
750 $data['pagetitle'] = t('Search: ');
751 $data['pagetitle'] .= ! empty($searchterm) ? $searchterm .' ' : '';
752 $bracketWrap = function ($tag) {
753 return '['. $tag .']';
754 };
755 $data['pagetitle'] .= ! empty($searchtags)
756 ? implode(' ', array_map($bracketWrap, preg_split('/\s+/', $searchtags))).' '
757 : '';
758 $data['pagetitle'] .= '- '. $conf->get('general.title');
759 }
760
761 $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => $loginManager->isLoggedIn()));
762
763 foreach ($data as $key => $value) {
764 $PAGE->assign($key, $value);
765 }
766
767 return;
768}
769
770/**
771 * Installation 275 * Installation
772 * This function should NEVER be called if the file data/config.php exists. 276 * This function should NEVER be called if the file data/config.php exists.
773 * 277 *
@@ -882,19 +386,6 @@ if (!isset($_SESSION['LINKS_PER_PAGE'])) {
882 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); 386 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
883} 387}
884 388
885try {
886 $history = new History($conf->get('resource.history'));
887} catch (Exception $e) {
888 die($e->getMessage());
889}
890
891$linkDb = new BookmarkFileService($conf, $history, $loginManager->isLoggedIn());
892
893if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) {
894 header('Location: ./daily-rss');
895 exit;
896}
897
898$containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager); 389$containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager);
899$container = $containerBuilder->build(); 390$container = $containerBuilder->build();
900$app = new App($container); 391$app = new App($container);
@@ -918,13 +409,15 @@ $app->group('/api/v1', function () {
918 409
919$app->group('', function () { 410$app->group('', function () {
920 /* -- PUBLIC --*/ 411 /* -- PUBLIC --*/
921 $this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index'); 412 $this->get('/', '\Shaarli\Front\Controller\Visitor\BookmarkListController:index');
413 $this->get('/shaare/{hash}', '\Shaarli\Front\Controller\Visitor\BookmarkListController:permalink');
414 $this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index')->setName('login');
922 $this->get('/picture-wall', '\Shaarli\Front\Controller\Visitor\PictureWallController:index'); 415 $this->get('/picture-wall', '\Shaarli\Front\Controller\Visitor\PictureWallController:index');
923 $this->get('/tags/cloud', '\Shaarli\Front\Controller\Visitor\TagCloudController:cloud'); 416 $this->get('/tags/cloud', '\Shaarli\Front\Controller\Visitor\TagCloudController:cloud');
924 $this->get('/tags/list', '\Shaarli\Front\Controller\Visitor\TagCloudController:list'); 417 $this->get('/tags/list', '\Shaarli\Front\Controller\Visitor\TagCloudController:list');
925 $this->get('/daily', '\Shaarli\Front\Controller\Visitor\DailyController:index'); 418 $this->get('/daily', '\Shaarli\Front\Controller\Visitor\DailyController:index');
926 $this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss'); 419 $this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss')->setName('rss');
927 $this->get('/feed/atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom'); 420 $this->get('/feed/atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom')->setName('atom');
928 $this->get('/feed/rss', '\Shaarli\Front\Controller\Visitor\FeedController:rss'); 421 $this->get('/feed/rss', '\Shaarli\Front\Controller\Visitor\FeedController:rss');
929 $this->get('/open-search', '\Shaarli\Front\Controller\Visitor\OpenSearchController:index'); 422 $this->get('/open-search', '\Shaarli\Front\Controller\Visitor\OpenSearchController:index');
930 423
@@ -967,19 +460,4 @@ $app->group('', function () {
967 460
968$response = $app->run(true); 461$response = $app->run(true);
969 462
970// Hack to make Slim and Shaarli router work together: 463$app->respond($response);
971// If a Slim route isn't found and NOT API call, we call renderPage().
972if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) {
973 // We use UTF-8 for proper international characters handling.
974 header('Content-Type: text/html; charset=utf-8');
975 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);
976} else {
977 $response = $response
978 ->withHeader('Access-Control-Allow-Origin', '*')
979 ->withHeader(
980 'Access-Control-Allow-Headers',
981 'X-Requested-With, Content-Type, Accept, Origin, Authorization'
982 )
983 ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
984 $app->respond($response);
985}
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php
index 8bf4ed46..c3e7abaf 100644
--- a/plugins/addlink_toolbar/addlink_toolbar.php
+++ b/plugins/addlink_toolbar/addlink_toolbar.php
@@ -5,7 +5,7 @@
5 * Adds the addlink input on the linklist page. 5 * Adds the addlink input on the linklist page.
6 */ 6 */
7 7
8use Shaarli\Router; 8use Shaarli\Render\TemplatePage;
9 9
10/** 10/**
11 * When linklist is displayed, add play videos to header's toolbar. 11 * When linklist is displayed, add play videos to header's toolbar.
@@ -16,7 +16,7 @@ use Shaarli\Router;
16 */ 16 */
17function hook_addlink_toolbar_render_header($data) 17function hook_addlink_toolbar_render_header($data)
18{ 18{
19 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) { 19 if ($data['_PAGE_'] == TemplatePage::LINKLIST && $data['_LOGGEDIN_'] === true) {
20 $form = array( 20 $form = array(
21 'attr' => array( 21 'attr' => array(
22 'method' => 'GET', 22 'method' => 'GET',
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php
index 8ae1b479..dd73d6a2 100644
--- a/plugins/demo_plugin/demo_plugin.php
+++ b/plugins/demo_plugin/demo_plugin.php
@@ -16,7 +16,7 @@
16 16
17use Shaarli\Config\ConfigManager; 17use Shaarli\Config\ConfigManager;
18use Shaarli\Plugin\PluginManager; 18use Shaarli\Plugin\PluginManager;
19use Shaarli\Router; 19use Shaarli\Render\TemplatePage;
20 20
21/** 21/**
22 * In the footer hook, there is a working example of a translation extension for Shaarli. 22 * In the footer hook, there is a working example of a translation extension for Shaarli.
@@ -74,7 +74,7 @@ function demo_plugin_init($conf)
74function hook_demo_plugin_render_header($data) 74function hook_demo_plugin_render_header($data)
75{ 75{
76 // Only execute when linklist is rendered. 76 // Only execute when linklist is rendered.
77 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 77 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
78 // If loggedin 78 // If loggedin
79 if ($data['_LOGGEDIN_'] === true) { 79 if ($data['_LOGGEDIN_'] === true) {
80 /* 80 /*
@@ -441,9 +441,9 @@ function hook_demo_plugin_delete_link($data)
441function hook_demo_plugin_render_feed($data) 441function hook_demo_plugin_render_feed($data)
442{ 442{
443 foreach ($data['links'] as &$link) { 443 foreach ($data['links'] as &$link) {
444 if ($data['_PAGE_'] == Router::$PAGE_FEED_ATOM) { 444 if ($data['_PAGE_'] == TemplatePage::FEED_ATOM) {
445 $link['description'] .= ' - ATOM Feed' ; 445 $link['description'] .= ' - ATOM Feed' ;
446 } elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) { 446 } elseif ($data['_PAGE_'] == TemplatePage::FEED_RSS) {
447 $link['description'] .= ' - RSS Feed'; 447 $link['description'] .= ' - RSS Feed';
448 } 448 }
449 } 449 }
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php
index dab75dd5..16edd9a6 100644
--- a/plugins/isso/isso.php
+++ b/plugins/isso/isso.php
@@ -6,7 +6,7 @@
6 6
7use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11/** 11/**
12 * Display an error everywhere if the plugin is enabled without configuration. 12 * Display an error everywhere if the plugin is enabled without configuration.
@@ -76,7 +76,7 @@ function hook_isso_render_linklist($data, $conf)
76 */ 76 */
77function hook_isso_render_includes($data) 77function hook_isso_render_includes($data)
78{ 78{
79 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 79 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
80 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css'; 80 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
81 } 81 }
82 82
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php
index 0341ed59..91a9c1e5 100644
--- a/plugins/playvideos/playvideos.php
+++ b/plugins/playvideos/playvideos.php
@@ -7,7 +7,7 @@
7 */ 7 */
8 8
9use Shaarli\Plugin\PluginManager; 9use Shaarli\Plugin\PluginManager;
10use Shaarli\Router; 10use Shaarli\Render\TemplatePage;
11 11
12/** 12/**
13 * When linklist is displayed, add play videos to header's toolbar. 13 * When linklist is displayed, add play videos to header's toolbar.
@@ -18,7 +18,7 @@ use Shaarli\Router;
18 */ 18 */
19function hook_playvideos_render_header($data) 19function hook_playvideos_render_header($data)
20{ 20{
21 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 21 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
22 $playvideo = array( 22 $playvideo = array(
23 'attr' => array( 23 'attr' => array(
24 'href' => '#', 24 'href' => '#',
@@ -42,7 +42,7 @@ function hook_playvideos_render_header($data)
42 */ 42 */
43function hook_playvideos_render_footer($data) 43function hook_playvideos_render_footer($data)
44{ 44{
45 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 45 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
46 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/jquery-1.11.2.min.js'; 46 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/jquery-1.11.2.min.js';
47 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/youtube_playlist.js'; 47 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/youtube_playlist.js';
48 } 48 }
diff --git a/plugins/pubsubhubbub/pubsubhubbub.php b/plugins/pubsubhubbub/pubsubhubbub.php
index 170f3494..8fe6799c 100644
--- a/plugins/pubsubhubbub/pubsubhubbub.php
+++ b/plugins/pubsubhubbub/pubsubhubbub.php
@@ -13,7 +13,7 @@ use pubsubhubbub\publisher\Publisher;
13use Shaarli\Config\ConfigManager; 13use Shaarli\Config\ConfigManager;
14use Shaarli\Feed\FeedBuilder; 14use Shaarli\Feed\FeedBuilder;
15use Shaarli\Plugin\PluginManager; 15use Shaarli\Plugin\PluginManager;
16use Shaarli\Router; 16use Shaarli\Render\TemplatePage;
17 17
18/** 18/**
19 * Plugin init function - set the hub to the default appspot one. 19 * Plugin init function - set the hub to the default appspot one.
@@ -41,7 +41,7 @@ function pubsubhubbub_init($conf)
41 */ 41 */
42function hook_pubsubhubbub_render_feed($data, $conf) 42function hook_pubsubhubbub_render_feed($data, $conf)
43{ 43{
44 $feedType = $data['_PAGE_'] == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM; 44 $feedType = $data['_PAGE_'] == TemplatePage::FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
45 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/pubsubhubbub/hub.'. $feedType .'.xml'); 45 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/pubsubhubbub/hub.'. $feedType .'.xml');
46 $data['feed_plugins_header'][] = sprintf($template, $conf->get('plugins.PUBSUBHUB_URL')); 46 $data['feed_plugins_header'][] = sprintf($template, $conf->get('plugins.PUBSUBHUB_URL'));
47 47
diff --git a/plugins/qrcode/qrcode.php b/plugins/qrcode/qrcode.php
index c1d237d5..2ec0cb65 100644
--- a/plugins/qrcode/qrcode.php
+++ b/plugins/qrcode/qrcode.php
@@ -6,7 +6,7 @@
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11/** 11/**
12 * Add qrcode icon to link_plugin when rendering linklist. 12 * Add qrcode icon to link_plugin when rendering linklist.
@@ -40,7 +40,7 @@ function hook_qrcode_render_linklist($data)
40 */ 40 */
41function hook_qrcode_render_footer($data) 41function hook_qrcode_render_footer($data)
42{ 42{
43 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 43 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
44 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/shaarli-qrcode.js'; 44 $data['js_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/shaarli-qrcode.js';
45 } 45 }
46 46
@@ -56,7 +56,7 @@ function hook_qrcode_render_footer($data)
56 */ 56 */
57function hook_qrcode_render_includes($data) 57function hook_qrcode_render_includes($data)
58{ 58{
59 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 59 if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
60 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.css'; 60 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.css';
61 } 61 }
62 62
diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php
index 5adc26aa..b19c8250 100644
--- a/tests/bookmark/BookmarkFileServiceTest.php
+++ b/tests/bookmark/BookmarkFileServiceTest.php
@@ -892,35 +892,35 @@ class BookmarkFileServiceTest extends TestCase
892 public function testFilterHashValid() 892 public function testFilterHashValid()
893 { 893 {
894 $request = smallHash('20150310_114651'); 894 $request = smallHash('20150310_114651');
895 $this->assertEquals( 895 $this->assertSame(
896 1, 896 $request,
897 count($this->publicLinkDB->findByHash($request)) 897 $this->publicLinkDB->findByHash($request)->getShortUrl()
898 ); 898 );
899 $request = smallHash('20150310_114633' . 8); 899 $request = smallHash('20150310_114633' . 8);
900 $this->assertEquals( 900 $this->assertSame(
901 1, 901 $request,
902 count($this->publicLinkDB->findByHash($request)) 902 $this->publicLinkDB->findByHash($request)->getShortUrl()
903 ); 903 );
904 } 904 }
905 905
906 /** 906 /**
907 * Test filterHash() with an invalid smallhash. 907 * Test filterHash() with an invalid smallhash.
908 *
909 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
910 */ 908 */
911 public function testFilterHashInValid1() 909 public function testFilterHashInValid1()
912 { 910 {
911 $this->expectException(BookmarkNotFoundException::class);
912
913 $request = 'blabla'; 913 $request = 'blabla';
914 $this->publicLinkDB->findByHash($request); 914 $this->publicLinkDB->findByHash($request);
915 } 915 }
916 916
917 /** 917 /**
918 * Test filterHash() with an empty smallhash. 918 * Test filterHash() with an empty smallhash.
919 *
920 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
921 */ 919 */
922 public function testFilterHashInValid() 920 public function testFilterHashInValid()
923 { 921 {
922 $this->expectException(BookmarkNotFoundException::class);
923
924 $this->publicLinkDB->findByHash(''); 924 $this->publicLinkDB->findByHash('');
925 } 925 }
926 926
diff --git a/tests/front/ShaarliMiddlewareTest.php b/tests/front/ShaarliMiddlewareTest.php
index 57be1002..81ea1344 100644
--- a/tests/front/ShaarliMiddlewareTest.php
+++ b/tests/front/ShaarliMiddlewareTest.php
@@ -8,7 +8,11 @@ use PHPUnit\Framework\TestCase;
8use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
9use Shaarli\Container\ShaarliContainer; 9use Shaarli\Container\ShaarliContainer;
10use Shaarli\Front\Exception\LoginBannedException; 10use Shaarli\Front\Exception\LoginBannedException;
11use Shaarli\Front\Exception\UnauthorizedException;
11use Shaarli\Render\PageBuilder; 12use Shaarli\Render\PageBuilder;
13use Shaarli\Render\PageCacheManager;
14use Shaarli\Security\LoginManager;
15use Shaarli\Updater\Updater;
12use Slim\Http\Request; 16use Slim\Http\Request;
13use Slim\Http\Response; 17use Slim\Http\Response;
14use Slim\Http\Uri; 18use Slim\Http\Uri;
@@ -24,9 +28,16 @@ class ShaarliMiddlewareTest extends TestCase
24 public function setUp(): void 28 public function setUp(): void
25 { 29 {
26 $this->container = $this->createMock(ShaarliContainer::class); 30 $this->container = $this->createMock(ShaarliContainer::class);
31
32 $this->container->conf = $this->createMock(ConfigManager::class);
33 $this->container->loginManager = $this->createMock(LoginManager::class);
34
27 $this->middleware = new ShaarliMiddleware($this->container); 35 $this->middleware = new ShaarliMiddleware($this->container);
28 } 36 }
29 37
38 /**
39 * Test middleware execution with valid controller call
40 */
30 public function testMiddlewareExecution(): void 41 public function testMiddlewareExecution(): void
31 { 42 {
32 $request = $this->createMock(Request::class); 43 $request = $this->createMock(Request::class);
@@ -49,7 +60,10 @@ class ShaarliMiddlewareTest extends TestCase
49 static::assertSame(418, $result->getStatusCode()); 60 static::assertSame(418, $result->getStatusCode());
50 } 61 }
51 62
52 public function testMiddlewareExecutionWithException(): void 63 /**
64 * Test middleware execution with controller throwing a known front exception
65 */
66 public function testMiddlewareExecutionWithFrontException(): void
53 { 67 {
54 $request = $this->createMock(Request::class); 68 $request = $this->createMock(Request::class);
55 $request->method('getUri')->willReturnCallback(function (): Uri { 69 $request->method('getUri')->willReturnCallback(function (): Uri {
@@ -58,7 +72,7 @@ class ShaarliMiddlewareTest extends TestCase
58 72
59 return $uri; 73 return $uri;
60 }); 74 });
61 75
62 $response = new Response(); 76 $response = new Response();
63 $controller = function (): void { 77 $controller = function (): void {
64 $exception = new LoginBannedException(); 78 $exception = new LoginBannedException();
@@ -72,9 +86,6 @@ class ShaarliMiddlewareTest extends TestCase
72 }); 86 });
73 $this->container->pageBuilder = $pageBuilder; 87 $this->container->pageBuilder = $pageBuilder;
74 88
75 $conf = $this->createMock(ConfigManager::class);
76 $this->container->conf = $conf;
77
78 /** @var Response $result */ 89 /** @var Response $result */
79 $result = $this->middleware->__invoke($request, $response, $controller); 90 $result = $this->middleware->__invoke($request, $response, $controller);
80 91
@@ -82,4 +93,113 @@ class ShaarliMiddlewareTest extends TestCase
82 static::assertSame(401, $result->getStatusCode()); 93 static::assertSame(401, $result->getStatusCode());
83 static::assertContains('error', (string) $result->getBody()); 94 static::assertContains('error', (string) $result->getBody());
84 } 95 }
96
97 /**
98 * Test middleware execution with controller throwing a not authorized exception
99 */
100 public function testMiddlewareExecutionWithUnauthorizedException(): void
101 {
102 $request = $this->createMock(Request::class);
103 $request->method('getUri')->willReturnCallback(function (): Uri {
104 $uri = $this->createMock(Uri::class);
105 $uri->method('getBasePath')->willReturn('/subfolder');
106
107 return $uri;
108 });
109
110 $response = new Response();
111 $controller = function (): void {
112 throw new UnauthorizedException();
113 };
114
115 /** @var Response $result */
116 $result = $this->middleware->__invoke($request, $response, $controller);
117
118 static::assertSame(302, $result->getStatusCode());
119 static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
120 }
121
122 /**
123 * Test middleware execution with controller throwing a not authorized exception
124 */
125 public function testMiddlewareExecutionWithServerExceptionWith(): void
126 {
127 $request = $this->createMock(Request::class);
128 $request->method('getUri')->willReturnCallback(function (): Uri {
129 $uri = $this->createMock(Uri::class);
130 $uri->method('getBasePath')->willReturn('/subfolder');
131
132 return $uri;
133 });
134
135 $response = new Response();
136 $controller = function (): void {
137 throw new \Exception();
138 };
139
140 $parameters = [];
141 $this->container->pageBuilder = $this->createMock(PageBuilder::class);
142 $this->container->pageBuilder->method('render')->willReturnCallback(function (string $message): string {
143 return $message;
144 });
145 $this->container->pageBuilder
146 ->method('assign')
147 ->willReturnCallback(function (string $key, string $value) use (&$parameters): void {
148 $parameters[$key] = $value;
149 })
150 ;
151
152 /** @var Response $result */
153 $result = $this->middleware->__invoke($request, $response, $controller);
154
155 static::assertSame(500, $result->getStatusCode());
156 static::assertContains('error', (string) $result->getBody());
157 static::assertSame('An unexpected error occurred.', $parameters['message']);
158 }
159
160 public function testMiddlewareExecutionWithUpdates(): void
161 {
162 $request = $this->createMock(Request::class);
163 $request->method('getUri')->willReturnCallback(function (): Uri {
164 $uri = $this->createMock(Uri::class);
165 $uri->method('getBasePath')->willReturn('/subfolder');
166
167 return $uri;
168 });
169
170 $response = new Response();
171 $controller = function (Request $request, Response $response): Response {
172 return $response->withStatus(418); // I'm a tea pot
173 };
174
175 $this->container->loginManager = $this->createMock(LoginManager::class);
176 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
177
178 $this->container->conf = $this->createMock(ConfigManager::class);
179 $this->container->conf->method('get')->willReturnCallback(function (string $key): string {
180 return $key;
181 });
182
183 $this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
184 $this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
185
186 $this->container->updater = $this->createMock(Updater::class);
187 $this->container->updater
188 ->expects(static::once())
189 ->method('update')
190 ->willReturn(['update123'])
191 ;
192 $this->container->updater->method('getDoneUpdates')->willReturn($updates = ['update123', 'other']);
193 $this->container->updater
194 ->expects(static::once())
195 ->method('writeUpdates')
196 ->with('resource.updates', $updates)
197 ;
198
199 /** @var Response $result */
200 $result = $this->middleware->__invoke($request, $response, $controller);
201
202 static::assertInstanceOf(Response::class, $result);
203 static::assertSame(418, $result->getStatusCode());
204 }
85} 205}
diff --git a/tests/front/controller/visitor/BookmarkListControllerTest.php b/tests/front/controller/visitor/BookmarkListControllerTest.php
new file mode 100644
index 00000000..5daaa2c4
--- /dev/null
+++ b/tests/front/controller/visitor/BookmarkListControllerTest.php
@@ -0,0 +1,448 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
10use Shaarli\Config\ConfigManager;
11use Shaarli\Security\LoginManager;
12use Shaarli\Thumbnailer;
13use Slim\Http\Request;
14use Slim\Http\Response;
15
16class BookmarkListControllerTest extends TestCase
17{
18 use FrontControllerMockHelper;
19
20 /** @var BookmarkListController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->controller = new BookmarkListController($this->container);
28 }
29
30 /**
31 * Test rendering list of bookmarks with default parameters (first page).
32 */
33 public function testIndexDefaultFirstPage(): void
34 {
35 $assignedVariables = [];
36 $this->assignTemplateVars($assignedVariables);
37
38 $request = $this->createMock(Request::class);
39 $response = new Response();
40
41 $this->container->bookmarkService
42 ->expects(static::once())
43 ->method('search')
44 ->with(
45 ['searchtags' => '', 'searchterm' => ''],
46 null,
47 false,
48 false
49 )
50 ->willReturn([
51 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
52 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
53 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
54 ]
55 );
56
57 $this->container->sessionManager
58 ->method('getSessionParameter')
59 ->willReturnCallback(function (string $parameter, $default = null) {
60 if ('LINKS_PER_PAGE' === $parameter) {
61 return 2;
62 }
63
64 return $default;
65 })
66 ;
67
68 $result = $this->controller->index($request, $response);
69
70 static::assertSame(200, $result->getStatusCode());
71 static::assertSame('linklist', (string) $result->getBody());
72
73 static::assertSame('Shaarli', $assignedVariables['pagetitle']);
74 static::assertSame('?page=2', $assignedVariables['previous_page_url']);
75 static::assertSame('', $assignedVariables['next_page_url']);
76 static::assertSame(2, $assignedVariables['page_max']);
77 static::assertSame('', $assignedVariables['search_tags']);
78 static::assertSame(3, $assignedVariables['result_count']);
79 static::assertSame(1, $assignedVariables['page_current']);
80 static::assertSame('', $assignedVariables['search_term']);
81 static::assertNull($assignedVariables['visibility']);
82 static::assertCount(2, $assignedVariables['links']);
83
84 $link = $assignedVariables['links'][0];
85
86 static::assertSame(1, $link['id']);
87 static::assertSame('http://url1.tld', $link['url']);
88 static::assertSame('Title 1', $link['title']);
89
90 $link = $assignedVariables['links'][1];
91
92 static::assertSame(2, $link['id']);
93 static::assertSame('http://url2.tld', $link['url']);
94 static::assertSame('Title 2', $link['title']);
95 }
96
97 /**
98 * Test rendering list of bookmarks with default parameters (second page).
99 */
100 public function testIndexDefaultSecondPage(): void
101 {
102 $assignedVariables = [];
103 $this->assignTemplateVars($assignedVariables);
104
105 $request = $this->createMock(Request::class);
106 $request->method('getParam')->willReturnCallback(function (string $key) {
107 if ('page' === $key) {
108 return '2';
109 }
110
111 return null;
112 });
113 $response = new Response();
114
115 $this->container->bookmarkService
116 ->expects(static::once())
117 ->method('search')
118 ->with(
119 ['searchtags' => '', 'searchterm' => ''],
120 null,
121 false,
122 false
123 )
124 ->willReturn([
125 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
126 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
127 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
128 ])
129 ;
130
131 $this->container->sessionManager
132 ->method('getSessionParameter')
133 ->willReturnCallback(function (string $parameter, $default = null) {
134 if ('LINKS_PER_PAGE' === $parameter) {
135 return 2;
136 }
137
138 return $default;
139 })
140 ;
141
142 $result = $this->controller->index($request, $response);
143
144 static::assertSame(200, $result->getStatusCode());
145 static::assertSame('linklist', (string) $result->getBody());
146
147 static::assertSame('Shaarli', $assignedVariables['pagetitle']);
148 static::assertSame('', $assignedVariables['previous_page_url']);
149 static::assertSame('?page=1', $assignedVariables['next_page_url']);
150 static::assertSame(2, $assignedVariables['page_max']);
151 static::assertSame('', $assignedVariables['search_tags']);
152 static::assertSame(3, $assignedVariables['result_count']);
153 static::assertSame(2, $assignedVariables['page_current']);
154 static::assertSame('', $assignedVariables['search_term']);
155 static::assertNull($assignedVariables['visibility']);
156 static::assertCount(1, $assignedVariables['links']);
157
158 $link = $assignedVariables['links'][2];
159
160 static::assertSame(3, $link['id']);
161 static::assertSame('http://url3.tld', $link['url']);
162 static::assertSame('Title 3', $link['title']);
163 }
164
165 /**
166 * Test rendering list of bookmarks with filters.
167 */
168 public function testIndexDefaultWithFilters(): void
169 {
170 $assignedVariables = [];
171 $this->assignTemplateVars($assignedVariables);
172
173 $request = $this->createMock(Request::class);
174 $request->method('getParam')->willReturnCallback(function (string $key) {
175 if ('searchtags' === $key) {
176 return 'abc def';
177 }
178 if ('searchterm' === $key) {
179 return 'ghi jkl';
180 }
181
182 return null;
183 });
184 $response = new Response();
185
186 $this->container->sessionManager
187 ->method('getSessionParameter')
188 ->willReturnCallback(function (string $key, $default) {
189 if ('LINKS_PER_PAGE' === $key) {
190 return 2;
191 }
192 if ('visibility' === $key) {
193 return 'private';
194 }
195 if ('untaggedonly' === $key) {
196 return true;
197 }
198
199 return $default;
200 })
201 ;
202
203 $this->container->bookmarkService
204 ->expects(static::once())
205 ->method('search')
206 ->with(
207 ['searchtags' => 'abc def', 'searchterm' => 'ghi jkl'],
208 'private',
209 false,
210 true
211 )
212 ->willReturn([
213 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
214 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
215 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
216 ])
217 ;
218
219 $result = $this->controller->index($request, $response);
220
221 static::assertSame(200, $result->getStatusCode());
222 static::assertSame('linklist', (string) $result->getBody());
223
224 static::assertSame('Search: ghi jkl [abc] [def] - Shaarli', $assignedVariables['pagetitle']);
225 static::assertSame('?page=2&searchterm=ghi+jkl&searchtags=abc+def', $assignedVariables['previous_page_url']);
226 }
227
228 /**
229 * Test displaying a permalink with valid parameters
230 */
231 public function testPermalinkValid(): void
232 {
233 $hash = 'abcdef';
234
235 $assignedVariables = [];
236 $this->assignTemplateVars($assignedVariables);
237
238 $request = $this->createMock(Request::class);
239 $response = new Response();
240
241 $this->container->bookmarkService
242 ->expects(static::once())
243 ->method('findByHash')
244 ->with($hash)
245 ->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
246 ;
247
248 $result = $this->controller->permalink($request, $response, ['hash' => $hash]);
249
250 static::assertSame(200, $result->getStatusCode());
251 static::assertSame('linklist', (string) $result->getBody());
252
253 static::assertSame('Title 1 - Shaarli', $assignedVariables['pagetitle']);
254 static::assertCount(1, $assignedVariables['links']);
255
256 $link = $assignedVariables['links'][0];
257
258 static::assertSame(123, $link['id']);
259 static::assertSame('http://url1.tld', $link['url']);
260 static::assertSame('Title 1', $link['title']);
261 }
262
263 /**
264 * Test displaying a permalink with an unknown small hash : renders a 404 template error
265 */
266 public function testPermalinkNotFound(): void
267 {
268 $hash = 'abcdef';
269
270 $assignedVariables = [];
271 $this->assignTemplateVars($assignedVariables);
272
273 $request = $this->createMock(Request::class);
274 $response = new Response();
275
276 $this->container->bookmarkService
277 ->expects(static::once())
278 ->method('findByHash')
279 ->with($hash)
280 ->willThrowException(new BookmarkNotFoundException())
281 ;
282
283 $result = $this->controller->permalink($request, $response, ['hash' => $hash]);
284
285 static::assertSame(200, $result->getStatusCode());
286 static::assertSame('404', (string) $result->getBody());
287
288 static::assertSame(
289 'The link you are trying to reach does not exist or has been deleted.',
290 $assignedVariables['error_message']
291 );
292 }
293
294 /**
295 * Test getting link list with thumbnail updates.
296 * -> 2 thumbnails update, only 1 datastore write
297 */
298 public function testThumbnailUpdateFromLinkList(): void
299 {
300 $request = $this->createMock(Request::class);
301 $response = new Response();
302
303 $this->container->loginManager = $this->createMock(LoginManager::class);
304 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
305
306 $this->container->conf = $this->createMock(ConfigManager::class);
307 $this->container->conf
308 ->method('get')
309 ->willReturnCallback(function (string $key, $default) {
310 return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
311 })
312 ;
313
314 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
315 $this->container->thumbnailer
316 ->expects(static::exactly(2))
317 ->method('get')
318 ->withConsecutive(['https://url2.tld'], ['https://url4.tld'])
319 ;
320
321 $this->container->bookmarkService
322 ->expects(static::once())
323 ->method('search')
324 ->willReturn([
325 (new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false),
326 $b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'),
327 (new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false),
328 $b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'),
329 (new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'),
330 ])
331 ;
332 $this->container->bookmarkService
333 ->expects(static::exactly(2))
334 ->method('set')
335 ->withConsecutive([$b1, false], [$b2, false])
336 ;
337 $this->container->bookmarkService->expects(static::once())->method('save');
338
339 $result = $this->controller->index($request, $response);
340
341 static::assertSame(200, $result->getStatusCode());
342 static::assertSame('linklist', (string) $result->getBody());
343 }
344
345 /**
346 * Test getting a permalink with thumbnail update.
347 */
348 public function testThumbnailUpdateFromPermalink(): void
349 {
350 $request = $this->createMock(Request::class);
351 $response = new Response();
352
353 $this->container->loginManager = $this->createMock(LoginManager::class);
354 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
355
356 $this->container->conf = $this->createMock(ConfigManager::class);
357 $this->container->conf
358 ->method('get')
359 ->willReturnCallback(function (string $key, $default) {
360 return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
361 })
362 ;
363
364 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
365 $this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']);
366
367 $this->container->bookmarkService
368 ->expects(static::once())
369 ->method('findByHash')
370 ->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
371 ;
372 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
373 $this->container->bookmarkService->expects(static::never())->method('save');
374
375 $result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
376
377 static::assertSame(200, $result->getStatusCode());
378 static::assertSame('linklist', (string) $result->getBody());
379 }
380
381 /**
382 * Trigger legacy controller in link list controller: permalink
383 */
384 public function testLegacyControllerPermalink(): void
385 {
386 $hash = 'abcdef';
387 $this->container->environment['QUERY_STRING'] = $hash;
388
389 $request = $this->createMock(Request::class);
390 $response = new Response();
391
392 $result = $this->controller->index($request, $response);
393
394 static::assertSame(302, $result->getStatusCode());
395 static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]);
396 }
397
398 /**
399 * Trigger legacy controller in link list controller: ?do= query parameter
400 */
401 public function testLegacyControllerDoPage(): void
402 {
403 $request = $this->createMock(Request::class);
404 $request->method('getQueryParam')->with('do')->willReturn('picwall');
405 $response = new Response();
406
407 $result = $this->controller->index($request, $response);
408
409 static::assertSame(302, $result->getStatusCode());
410 static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]);
411 }
412
413 /**
414 * Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route
415 */
416 public function testLegacyControllerUnknownDoPage(): void
417 {
418 $request = $this->createMock(Request::class);
419 $request->method('getQueryParam')->with('do')->willReturn('nope');
420 $response = new Response();
421
422 $result = $this->controller->index($request, $response);
423
424 static::assertSame(200, $result->getStatusCode());
425 static::assertSame('linklist', (string) $result->getBody());
426 }
427
428 /**
429 * Trigger legacy controller in link list controller: other GET route (e.g. ?post)
430 */
431 public function testLegacyControllerGetParameter(): void
432 {
433 $request = $this->createMock(Request::class);
434 $request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']);
435 $response = new Response();
436
437 $this->container->loginManager = $this->createMock(LoginManager::class);
438 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
439
440 $result = $this->controller->index($request, $response);
441
442 static::assertSame(302, $result->getStatusCode());
443 static::assertSame(
444 '/subfolder/admin/shaare?post=' . urlencode($url),
445 $result->getHeader('location')[0]
446 );
447 }
448}
diff --git a/tests/legacy/LegacyControllerTest.php b/tests/legacy/LegacyControllerTest.php
new file mode 100644
index 00000000..ff4520a3
--- /dev/null
+++ b/tests/legacy/LegacyControllerTest.php
@@ -0,0 +1,99 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Legacy;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class LegacyControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var LegacyController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new LegacyController($this->container);
24 }
25
26 /**
27 * @dataProvider getProcessProvider
28 */
29 public function testProcess(string $legacyRoute, array $queryParameters, string $slimRoute, bool $isLoggedIn): void
30 {
31 $request = $this->createMock(Request::class);
32 $request->method('getQueryParams')->willReturn($queryParameters);
33 $request
34 ->method('getParam')
35 ->willReturnCallback(function (string $key) use ($queryParameters): ?string {
36 return $queryParameters[$key] ?? null;
37 })
38 ;
39 $response = new Response();
40
41 $this->container->loginManager->method('isLoggedIn')->willReturn($isLoggedIn);
42
43 $result = $this->controller->process($request, $response, $legacyRoute);
44
45 static::assertSame('/subfolder' . $slimRoute, $result->getHeader('location')[0]);
46 }
47
48 public function testProcessNotFound(): void
49 {
50 $request = $this->createMock(Request::class);
51 $response = new Response();
52
53 $this->expectException(UnknowLegacyRouteException::class);
54
55 $this->controller->process($request, $response, 'nope');
56 }
57
58 /**
59 * @return array[] Parameters:
60 * - string legacyRoute
61 * - array queryParameters
62 * - string slimRoute
63 * - bool isLoggedIn
64 */
65 public function getProcessProvider(): array
66 {
67 return [
68 ['post', [], '/admin/shaare', true],
69 ['post', [], '/login', false],
70 ['post', ['title' => 'test'], '/admin/shaare?title=test', true],
71 ['post', ['title' => 'test'], '/login?title=test', false],
72 ['addlink', [], '/admin/add-shaare', true],
73 ['addlink', [], '/login', false],
74 ['login', [], '/login', true],
75 ['login', [], '/login', false],
76 ['logout', [], '/logout', true],
77 ['logout', [], '/logout', false],
78 ['picwall', [], '/picture-wall', false],
79 ['picwall', [], '/picture-wall', true],
80 ['tagcloud', [], '/tags/cloud', false],
81 ['tagcloud', [], '/tags/cloud', true],
82 ['taglist', [], '/tags/list', false],
83 ['taglist', [], '/tags/list', true],
84 ['daily', [], '/daily', false],
85 ['daily', [], '/daily', true],
86 ['daily', ['day' => '123456789', 'discard' => '1'], '/daily?day=123456789', false],
87 ['rss', [], '/feed/rss', false],
88 ['rss', [], '/feed/rss', true],
89 ['rss', ['search' => 'filter123', 'other' => 'param'], '/feed/rss?search=filter123&other=param', false],
90 ['atom', [], '/feed/atom', false],
91 ['atom', [], '/feed/atom', true],
92 ['atom', ['search' => 'filter123', 'other' => 'param'], '/feed/atom?search=filter123&other=param', false],
93 ['opensearch', [], '/open-search', false],
94 ['opensearch', [], '/open-search', true],
95 ['dailyrss', [], '/daily-rss', false],
96 ['dailyrss', [], '/daily-rss', true],
97 ];
98 }
99}
diff --git a/tests/RouterTest.php b/tests/legacy/LegacyRouterTest.php
index 0cd49bb8..c2019ca7 100644
--- a/tests/RouterTest.php
+++ b/tests/legacy/LegacyRouterTest.php
@@ -1,10 +1,13 @@
1<?php 1<?php
2namespace Shaarli; 2
3namespace Shaarli\Legacy;
4
5use PHPUnit\Framework\TestCase;
3 6
4/** 7/**
5 * Unit tests for Router 8 * Unit tests for Router
6 */ 9 */
7class RouterTest extends \PHPUnit\Framework\TestCase 10class LegacyRouterTest extends TestCase
8{ 11{
9 /** 12 /**
10 * Test findPage: login page output. 13 * Test findPage: login page output.
@@ -15,18 +18,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
15 public function testFindPageLoginValid() 18 public function testFindPageLoginValid()
16 { 19 {
17 $this->assertEquals( 20 $this->assertEquals(
18 Router::$PAGE_LOGIN, 21 LegacyRouter::$PAGE_LOGIN,
19 Router::findPage('do=login', array(), false) 22 LegacyRouter::findPage('do=login', array(), false)
20 ); 23 );
21 24
22 $this->assertEquals( 25 $this->assertEquals(
23 Router::$PAGE_LOGIN, 26 LegacyRouter::$PAGE_LOGIN,
24 Router::findPage('do=login', array(), 1) 27 LegacyRouter::findPage('do=login', array(), 1)
25 ); 28 );
26 29
27 $this->assertEquals( 30 $this->assertEquals(
28 Router::$PAGE_LOGIN, 31 LegacyRouter::$PAGE_LOGIN,
29 Router::findPage('do=login&stuff', array(), false) 32 LegacyRouter::findPage('do=login&stuff', array(), false)
30 ); 33 );
31 } 34 }
32 35
@@ -39,13 +42,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
39 public function testFindPageLoginInvalid() 42 public function testFindPageLoginInvalid()
40 { 43 {
41 $this->assertNotEquals( 44 $this->assertNotEquals(
42 Router::$PAGE_LOGIN, 45 LegacyRouter::$PAGE_LOGIN,
43 Router::findPage('do=login', array(), true) 46 LegacyRouter::findPage('do=login', array(), true)
44 ); 47 );
45 48
46 $this->assertNotEquals( 49 $this->assertNotEquals(
47 Router::$PAGE_LOGIN, 50 LegacyRouter::$PAGE_LOGIN,
48 Router::findPage('do=other', array(), false) 51 LegacyRouter::findPage('do=other', array(), false)
49 ); 52 );
50 } 53 }
51 54
@@ -58,13 +61,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
58 public function testFindPagePicwallValid() 61 public function testFindPagePicwallValid()
59 { 62 {
60 $this->assertEquals( 63 $this->assertEquals(
61 Router::$PAGE_PICWALL, 64 LegacyRouter::$PAGE_PICWALL,
62 Router::findPage('do=picwall', array(), false) 65 LegacyRouter::findPage('do=picwall', array(), false)
63 ); 66 );
64 67
65 $this->assertEquals( 68 $this->assertEquals(
66 Router::$PAGE_PICWALL, 69 LegacyRouter::$PAGE_PICWALL,
67 Router::findPage('do=picwall', array(), true) 70 LegacyRouter::findPage('do=picwall', array(), true)
68 ); 71 );
69 } 72 }
70 73
@@ -77,13 +80,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
77 public function testFindPagePicwallInvalid() 80 public function testFindPagePicwallInvalid()
78 { 81 {
79 $this->assertEquals( 82 $this->assertEquals(
80 Router::$PAGE_PICWALL, 83 LegacyRouter::$PAGE_PICWALL,
81 Router::findPage('do=picwall&stuff', array(), false) 84 LegacyRouter::findPage('do=picwall&stuff', array(), false)
82 ); 85 );
83 86
84 $this->assertNotEquals( 87 $this->assertNotEquals(
85 Router::$PAGE_PICWALL, 88 LegacyRouter::$PAGE_PICWALL,
86 Router::findPage('do=other', array(), false) 89 LegacyRouter::findPage('do=other', array(), false)
87 ); 90 );
88 } 91 }
89 92
@@ -96,18 +99,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
96 public function testFindPageTagcloudValid() 99 public function testFindPageTagcloudValid()
97 { 100 {
98 $this->assertEquals( 101 $this->assertEquals(
99 Router::$PAGE_TAGCLOUD, 102 LegacyRouter::$PAGE_TAGCLOUD,
100 Router::findPage('do=tagcloud', array(), false) 103 LegacyRouter::findPage('do=tagcloud', array(), false)
101 ); 104 );
102 105
103 $this->assertEquals( 106 $this->assertEquals(
104 Router::$PAGE_TAGCLOUD, 107 LegacyRouter::$PAGE_TAGCLOUD,
105 Router::findPage('do=tagcloud', array(), true) 108 LegacyRouter::findPage('do=tagcloud', array(), true)
106 ); 109 );
107 110
108 $this->assertEquals( 111 $this->assertEquals(
109 Router::$PAGE_TAGCLOUD, 112 LegacyRouter::$PAGE_TAGCLOUD,
110 Router::findPage('do=tagcloud&stuff', array(), false) 113 LegacyRouter::findPage('do=tagcloud&stuff', array(), false)
111 ); 114 );
112 } 115 }
113 116
@@ -120,8 +123,8 @@ class RouterTest extends \PHPUnit\Framework\TestCase
120 public function testFindPageTagcloudInvalid() 123 public function testFindPageTagcloudInvalid()
121 { 124 {
122 $this->assertNotEquals( 125 $this->assertNotEquals(
123 Router::$PAGE_TAGCLOUD, 126 LegacyRouter::$PAGE_TAGCLOUD,
124 Router::findPage('do=other', array(), false) 127 LegacyRouter::findPage('do=other', array(), false)
125 ); 128 );
126 } 129 }
127 130
@@ -134,23 +137,23 @@ class RouterTest extends \PHPUnit\Framework\TestCase
134 public function testFindPageLinklistValid() 137 public function testFindPageLinklistValid()
135 { 138 {
136 $this->assertEquals( 139 $this->assertEquals(
137 Router::$PAGE_LINKLIST, 140 LegacyRouter::$PAGE_LINKLIST,
138 Router::findPage('', array(), true) 141 LegacyRouter::findPage('', array(), true)
139 ); 142 );
140 143
141 $this->assertEquals( 144 $this->assertEquals(
142 Router::$PAGE_LINKLIST, 145 LegacyRouter::$PAGE_LINKLIST,
143 Router::findPage('whatever', array(), true) 146 LegacyRouter::findPage('whatever', array(), true)
144 ); 147 );
145 148
146 $this->assertEquals( 149 $this->assertEquals(
147 Router::$PAGE_LINKLIST, 150 LegacyRouter::$PAGE_LINKLIST,
148 Router::findPage('whatever', array(), false) 151 LegacyRouter::findPage('whatever', array(), false)
149 ); 152 );
150 153
151 $this->assertEquals( 154 $this->assertEquals(
152 Router::$PAGE_LINKLIST, 155 LegacyRouter::$PAGE_LINKLIST,
153 Router::findPage('do=tools', array(), false) 156 LegacyRouter::findPage('do=tools', array(), false)
154 ); 157 );
155 } 158 }
156 159
@@ -163,13 +166,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
163 public function testFindPageToolsValid() 166 public function testFindPageToolsValid()
164 { 167 {
165 $this->assertEquals( 168 $this->assertEquals(
166 Router::$PAGE_TOOLS, 169 LegacyRouter::$PAGE_TOOLS,
167 Router::findPage('do=tools', array(), true) 170 LegacyRouter::findPage('do=tools', array(), true)
168 ); 171 );
169 172
170 $this->assertEquals( 173 $this->assertEquals(
171 Router::$PAGE_TOOLS, 174 LegacyRouter::$PAGE_TOOLS,
172 Router::findPage('do=tools&stuff', array(), true) 175 LegacyRouter::findPage('do=tools&stuff', array(), true)
173 ); 176 );
174 } 177 }
175 178
@@ -182,18 +185,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
182 public function testFindPageToolsInvalid() 185 public function testFindPageToolsInvalid()
183 { 186 {
184 $this->assertNotEquals( 187 $this->assertNotEquals(
185 Router::$PAGE_TOOLS, 188 LegacyRouter::$PAGE_TOOLS,
186 Router::findPage('do=tools', array(), 1) 189 LegacyRouter::findPage('do=tools', array(), 1)
187 ); 190 );
188 191
189 $this->assertNotEquals( 192 $this->assertNotEquals(
190 Router::$PAGE_TOOLS, 193 LegacyRouter::$PAGE_TOOLS,
191 Router::findPage('do=tools', array(), false) 194 LegacyRouter::findPage('do=tools', array(), false)
192 ); 195 );
193 196
194 $this->assertNotEquals( 197 $this->assertNotEquals(
195 Router::$PAGE_TOOLS, 198 LegacyRouter::$PAGE_TOOLS,
196 Router::findPage('do=other', array(), true) 199 LegacyRouter::findPage('do=other', array(), true)
197 ); 200 );
198 } 201 }
199 202
@@ -206,12 +209,12 @@ class RouterTest extends \PHPUnit\Framework\TestCase
206 public function testFindPageChangepasswdValid() 209 public function testFindPageChangepasswdValid()
207 { 210 {
208 $this->assertEquals( 211 $this->assertEquals(
209 Router::$PAGE_CHANGEPASSWORD, 212 LegacyRouter::$PAGE_CHANGEPASSWORD,
210 Router::findPage('do=changepasswd', array(), true) 213 LegacyRouter::findPage('do=changepasswd', array(), true)
211 ); 214 );
212 $this->assertEquals( 215 $this->assertEquals(
213 Router::$PAGE_CHANGEPASSWORD, 216 LegacyRouter::$PAGE_CHANGEPASSWORD,
214 Router::findPage('do=changepasswd&stuff', array(), true) 217 LegacyRouter::findPage('do=changepasswd&stuff', array(), true)
215 ); 218 );
216 } 219 }
217 220
@@ -224,18 +227,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
224 public function testFindPageChangepasswdInvalid() 227 public function testFindPageChangepasswdInvalid()
225 { 228 {
226 $this->assertNotEquals( 229 $this->assertNotEquals(
227 Router::$PAGE_CHANGEPASSWORD, 230 LegacyRouter::$PAGE_CHANGEPASSWORD,
228 Router::findPage('do=changepasswd', array(), 1) 231 LegacyRouter::findPage('do=changepasswd', array(), 1)
229 ); 232 );
230 233
231 $this->assertNotEquals( 234 $this->assertNotEquals(
232 Router::$PAGE_CHANGEPASSWORD, 235 LegacyRouter::$PAGE_CHANGEPASSWORD,
233 Router::findPage('do=changepasswd', array(), false) 236 LegacyRouter::findPage('do=changepasswd', array(), false)
234 ); 237 );
235 238
236 $this->assertNotEquals( 239 $this->assertNotEquals(
237 Router::$PAGE_CHANGEPASSWORD, 240 LegacyRouter::$PAGE_CHANGEPASSWORD,
238 Router::findPage('do=other', array(), true) 241 LegacyRouter::findPage('do=other', array(), true)
239 ); 242 );
240 } 243 }
241 /** 244 /**
@@ -247,13 +250,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
247 public function testFindPageConfigureValid() 250 public function testFindPageConfigureValid()
248 { 251 {
249 $this->assertEquals( 252 $this->assertEquals(
250 Router::$PAGE_CONFIGURE, 253 LegacyRouter::$PAGE_CONFIGURE,
251 Router::findPage('do=configure', array(), true) 254 LegacyRouter::findPage('do=configure', array(), true)
252 ); 255 );
253 256
254 $this->assertEquals( 257 $this->assertEquals(
255 Router::$PAGE_CONFIGURE, 258 LegacyRouter::$PAGE_CONFIGURE,
256 Router::findPage('do=configure&stuff', array(), true) 259 LegacyRouter::findPage('do=configure&stuff', array(), true)
257 ); 260 );
258 } 261 }
259 262
@@ -266,18 +269,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
266 public function testFindPageConfigureInvalid() 269 public function testFindPageConfigureInvalid()
267 { 270 {
268 $this->assertNotEquals( 271 $this->assertNotEquals(
269 Router::$PAGE_CONFIGURE, 272 LegacyRouter::$PAGE_CONFIGURE,
270 Router::findPage('do=configure', array(), 1) 273 LegacyRouter::findPage('do=configure', array(), 1)
271 ); 274 );
272 275
273 $this->assertNotEquals( 276 $this->assertNotEquals(
274 Router::$PAGE_CONFIGURE, 277 LegacyRouter::$PAGE_CONFIGURE,
275 Router::findPage('do=configure', array(), false) 278 LegacyRouter::findPage('do=configure', array(), false)
276 ); 279 );
277 280
278 $this->assertNotEquals( 281 $this->assertNotEquals(
279 Router::$PAGE_CONFIGURE, 282 LegacyRouter::$PAGE_CONFIGURE,
280 Router::findPage('do=other', array(), true) 283 LegacyRouter::findPage('do=other', array(), true)
281 ); 284 );
282 } 285 }
283 286
@@ -290,13 +293,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
290 public function testFindPageChangetagValid() 293 public function testFindPageChangetagValid()
291 { 294 {
292 $this->assertEquals( 295 $this->assertEquals(
293 Router::$PAGE_CHANGETAG, 296 LegacyRouter::$PAGE_CHANGETAG,
294 Router::findPage('do=changetag', array(), true) 297 LegacyRouter::findPage('do=changetag', array(), true)
295 ); 298 );
296 299
297 $this->assertEquals( 300 $this->assertEquals(
298 Router::$PAGE_CHANGETAG, 301 LegacyRouter::$PAGE_CHANGETAG,
299 Router::findPage('do=changetag&stuff', array(), true) 302 LegacyRouter::findPage('do=changetag&stuff', array(), true)
300 ); 303 );
301 } 304 }
302 305
@@ -309,18 +312,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
309 public function testFindPageChangetagInvalid() 312 public function testFindPageChangetagInvalid()
310 { 313 {
311 $this->assertNotEquals( 314 $this->assertNotEquals(
312 Router::$PAGE_CHANGETAG, 315 LegacyRouter::$PAGE_CHANGETAG,
313 Router::findPage('do=changetag', array(), 1) 316 LegacyRouter::findPage('do=changetag', array(), 1)
314 ); 317 );
315 318
316 $this->assertNotEquals( 319 $this->assertNotEquals(
317 Router::$PAGE_CHANGETAG, 320 LegacyRouter::$PAGE_CHANGETAG,
318 Router::findPage('do=changetag', array(), false) 321 LegacyRouter::findPage('do=changetag', array(), false)
319 ); 322 );
320 323
321 $this->assertNotEquals( 324 $this->assertNotEquals(
322 Router::$PAGE_CHANGETAG, 325 LegacyRouter::$PAGE_CHANGETAG,
323 Router::findPage('do=other', array(), true) 326 LegacyRouter::findPage('do=other', array(), true)
324 ); 327 );
325 } 328 }
326 329
@@ -333,13 +336,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
333 public function testFindPageAddlinkValid() 336 public function testFindPageAddlinkValid()
334 { 337 {
335 $this->assertEquals( 338 $this->assertEquals(
336 Router::$PAGE_ADDLINK, 339 LegacyRouter::$PAGE_ADDLINK,
337 Router::findPage('do=addlink', array(), true) 340 LegacyRouter::findPage('do=addlink', array(), true)
338 ); 341 );
339 342
340 $this->assertEquals( 343 $this->assertEquals(
341 Router::$PAGE_ADDLINK, 344 LegacyRouter::$PAGE_ADDLINK,
342 Router::findPage('do=addlink&stuff', array(), true) 345 LegacyRouter::findPage('do=addlink&stuff', array(), true)
343 ); 346 );
344 } 347 }
345 348
@@ -352,18 +355,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
352 public function testFindPageAddlinkInvalid() 355 public function testFindPageAddlinkInvalid()
353 { 356 {
354 $this->assertNotEquals( 357 $this->assertNotEquals(
355 Router::$PAGE_ADDLINK, 358 LegacyRouter::$PAGE_ADDLINK,
356 Router::findPage('do=addlink', array(), 1) 359 LegacyRouter::findPage('do=addlink', array(), 1)
357 ); 360 );
358 361
359 $this->assertNotEquals( 362 $this->assertNotEquals(
360 Router::$PAGE_ADDLINK, 363 LegacyRouter::$PAGE_ADDLINK,
361 Router::findPage('do=addlink', array(), false) 364 LegacyRouter::findPage('do=addlink', array(), false)
362 ); 365 );
363 366
364 $this->assertNotEquals( 367 $this->assertNotEquals(
365 Router::$PAGE_ADDLINK, 368 LegacyRouter::$PAGE_ADDLINK,
366 Router::findPage('do=other', array(), true) 369 LegacyRouter::findPage('do=other', array(), true)
367 ); 370 );
368 } 371 }
369 372
@@ -376,13 +379,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
376 public function testFindPageExportValid() 379 public function testFindPageExportValid()
377 { 380 {
378 $this->assertEquals( 381 $this->assertEquals(
379 Router::$PAGE_EXPORT, 382 LegacyRouter::$PAGE_EXPORT,
380 Router::findPage('do=export', array(), true) 383 LegacyRouter::findPage('do=export', array(), true)
381 ); 384 );
382 385
383 $this->assertEquals( 386 $this->assertEquals(
384 Router::$PAGE_EXPORT, 387 LegacyRouter::$PAGE_EXPORT,
385 Router::findPage('do=export&stuff', array(), true) 388 LegacyRouter::findPage('do=export&stuff', array(), true)
386 ); 389 );
387 } 390 }
388 391
@@ -395,18 +398,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
395 public function testFindPageExportInvalid() 398 public function testFindPageExportInvalid()
396 { 399 {
397 $this->assertNotEquals( 400 $this->assertNotEquals(
398 Router::$PAGE_EXPORT, 401 LegacyRouter::$PAGE_EXPORT,
399 Router::findPage('do=export', array(), 1) 402 LegacyRouter::findPage('do=export', array(), 1)
400 ); 403 );
401 404
402 $this->assertNotEquals( 405 $this->assertNotEquals(
403 Router::$PAGE_EXPORT, 406 LegacyRouter::$PAGE_EXPORT,
404 Router::findPage('do=export', array(), false) 407 LegacyRouter::findPage('do=export', array(), false)
405 ); 408 );
406 409
407 $this->assertNotEquals( 410 $this->assertNotEquals(
408 Router::$PAGE_EXPORT, 411 LegacyRouter::$PAGE_EXPORT,
409 Router::findPage('do=other', array(), true) 412 LegacyRouter::findPage('do=other', array(), true)
410 ); 413 );
411 } 414 }
412 415
@@ -419,13 +422,13 @@ class RouterTest extends \PHPUnit\Framework\TestCase
419 public function testFindPageImportValid() 422 public function testFindPageImportValid()
420 { 423 {
421 $this->assertEquals( 424 $this->assertEquals(
422 Router::$PAGE_IMPORT, 425 LegacyRouter::$PAGE_IMPORT,
423 Router::findPage('do=import', array(), true) 426 LegacyRouter::findPage('do=import', array(), true)
424 ); 427 );
425 428
426 $this->assertEquals( 429 $this->assertEquals(
427 Router::$PAGE_IMPORT, 430 LegacyRouter::$PAGE_IMPORT,
428 Router::findPage('do=import&stuff', array(), true) 431 LegacyRouter::findPage('do=import&stuff', array(), true)
429 ); 432 );
430 } 433 }
431 434
@@ -438,18 +441,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
438 public function testFindPageImportInvalid() 441 public function testFindPageImportInvalid()
439 { 442 {
440 $this->assertNotEquals( 443 $this->assertNotEquals(
441 Router::$PAGE_IMPORT, 444 LegacyRouter::$PAGE_IMPORT,
442 Router::findPage('do=import', array(), 1) 445 LegacyRouter::findPage('do=import', array(), 1)
443 ); 446 );
444 447
445 $this->assertNotEquals( 448 $this->assertNotEquals(
446 Router::$PAGE_IMPORT, 449 LegacyRouter::$PAGE_IMPORT,
447 Router::findPage('do=import', array(), false) 450 LegacyRouter::findPage('do=import', array(), false)
448 ); 451 );
449 452
450 $this->assertNotEquals( 453 $this->assertNotEquals(
451 Router::$PAGE_IMPORT, 454 LegacyRouter::$PAGE_IMPORT,
452 Router::findPage('do=other', array(), true) 455 LegacyRouter::findPage('do=other', array(), true)
453 ); 456 );
454 } 457 }
455 458
@@ -462,24 +465,24 @@ class RouterTest extends \PHPUnit\Framework\TestCase
462 public function testFindPageEditlinkValid() 465 public function testFindPageEditlinkValid()
463 { 466 {
464 $this->assertEquals( 467 $this->assertEquals(
465 Router::$PAGE_EDITLINK, 468 LegacyRouter::$PAGE_EDITLINK,
466 Router::findPage('whatever', array('edit_link' => 1), true) 469 LegacyRouter::findPage('whatever', array('edit_link' => 1), true)
467 ); 470 );
468 471
469 $this->assertEquals( 472 $this->assertEquals(
470 Router::$PAGE_EDITLINK, 473 LegacyRouter::$PAGE_EDITLINK,
471 Router::findPage('', array('edit_link' => 1), true) 474 LegacyRouter::findPage('', array('edit_link' => 1), true)
472 ); 475 );
473 476
474 477
475 $this->assertEquals( 478 $this->assertEquals(
476 Router::$PAGE_EDITLINK, 479 LegacyRouter::$PAGE_EDITLINK,
477 Router::findPage('whatever', array('post' => 1), true) 480 LegacyRouter::findPage('whatever', array('post' => 1), true)
478 ); 481 );
479 482
480 $this->assertEquals( 483 $this->assertEquals(
481 Router::$PAGE_EDITLINK, 484 LegacyRouter::$PAGE_EDITLINK,
482 Router::findPage('whatever', array('post' => 1, 'edit_link' => 1), true) 485 LegacyRouter::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
483 ); 486 );
484 } 487 }
485 488
@@ -492,18 +495,18 @@ class RouterTest extends \PHPUnit\Framework\TestCase
492 public function testFindPageEditlinkInvalid() 495 public function testFindPageEditlinkInvalid()
493 { 496 {
494 $this->assertNotEquals( 497 $this->assertNotEquals(
495 Router::$PAGE_EDITLINK, 498 LegacyRouter::$PAGE_EDITLINK,
496 Router::findPage('whatever', array('edit_link' => 1), false) 499 LegacyRouter::findPage('whatever', array('edit_link' => 1), false)
497 ); 500 );
498 501
499 $this->assertNotEquals( 502 $this->assertNotEquals(
500 Router::$PAGE_EDITLINK, 503 LegacyRouter::$PAGE_EDITLINK,
501 Router::findPage('whatever', array('edit_link' => 1), 1) 504 LegacyRouter::findPage('whatever', array('edit_link' => 1), 1)
502 ); 505 );
503 506
504 $this->assertNotEquals( 507 $this->assertNotEquals(
505 Router::$PAGE_EDITLINK, 508 LegacyRouter::$PAGE_EDITLINK,
506 Router::findPage('whatever', array(), true) 509 LegacyRouter::findPage('whatever', array(), true)
507 ); 510 );
508 } 511 }
509} 512}
diff --git a/tests/plugins/PluginAddlinkTest.php b/tests/plugins/PluginAddlinkTest.php
index d052f8b9..4018c1a8 100644
--- a/tests/plugins/PluginAddlinkTest.php
+++ b/tests/plugins/PluginAddlinkTest.php
@@ -2,7 +2,7 @@
2namespace Shaarli\Plugin\Addlink; 2namespace Shaarli\Plugin\Addlink;
3 3
4use Shaarli\Plugin\PluginManager; 4use Shaarli\Plugin\PluginManager;
5use Shaarli\Router; 5use Shaarli\Render\TemplatePage;
6 6
7require_once 'plugins/addlink_toolbar/addlink_toolbar.php'; 7require_once 'plugins/addlink_toolbar/addlink_toolbar.php';
8 8
@@ -26,7 +26,7 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
26 { 26 {
27 $str = 'stuff'; 27 $str = 'stuff';
28 $data = array($str => $str); 28 $data = array($str => $str);
29 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 29 $data['_PAGE_'] = TemplatePage::LINKLIST;
30 $data['_LOGGEDIN_'] = true; 30 $data['_LOGGEDIN_'] = true;
31 31
32 $data = hook_addlink_toolbar_render_header($data); 32 $data = hook_addlink_toolbar_render_header($data);
@@ -48,7 +48,7 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
48 { 48 {
49 $str = 'stuff'; 49 $str = 'stuff';
50 $data = array($str => $str); 50 $data = array($str => $str);
51 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 51 $data['_PAGE_'] = TemplatePage::LINKLIST;
52 $data['_LOGGEDIN_'] = false; 52 $data['_LOGGEDIN_'] = false;
53 53
54 $data = hook_addlink_toolbar_render_header($data); 54 $data = hook_addlink_toolbar_render_header($data);
diff --git a/tests/plugins/PluginPlayvideosTest.php b/tests/plugins/PluginPlayvideosTest.php
index 51472617..b7b6ce53 100644
--- a/tests/plugins/PluginPlayvideosTest.php
+++ b/tests/plugins/PluginPlayvideosTest.php
@@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Playvideos;
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11require_once 'plugins/playvideos/playvideos.php'; 11require_once 'plugins/playvideos/playvideos.php';
12 12
@@ -31,7 +31,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
31 { 31 {
32 $str = 'stuff'; 32 $str = 'stuff';
33 $data = array($str => $str); 33 $data = array($str => $str);
34 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 34 $data['_PAGE_'] = TemplatePage::LINKLIST;
35 35
36 $data = hook_playvideos_render_header($data); 36 $data = hook_playvideos_render_header($data);
37 $this->assertEquals($str, $data[$str]); 37 $this->assertEquals($str, $data[$str]);
@@ -50,7 +50,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
50 { 50 {
51 $str = 'stuff'; 51 $str = 'stuff';
52 $data = array($str => $str); 52 $data = array($str => $str);
53 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 53 $data['_PAGE_'] = TemplatePage::LINKLIST;
54 54
55 $data = hook_playvideos_render_footer($data); 55 $data = hook_playvideos_render_footer($data);
56 $this->assertEquals($str, $data[$str]); 56 $this->assertEquals($str, $data[$str]);
diff --git a/tests/plugins/PluginPubsubhubbubTest.php b/tests/plugins/PluginPubsubhubbubTest.php
index a7bd8fc9..e66f484e 100644
--- a/tests/plugins/PluginPubsubhubbubTest.php
+++ b/tests/plugins/PluginPubsubhubbubTest.php
@@ -3,7 +3,7 @@ namespace Shaarli\Plugin\Pubsubhubbub;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\PluginManager; 5use Shaarli\Plugin\PluginManager;
6use Shaarli\Router; 6use Shaarli\Render\TemplatePage;
7 7
8require_once 'plugins/pubsubhubbub/pubsubhubbub.php'; 8require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
9 9
@@ -34,7 +34,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
34 $hub = 'http://domain.hub'; 34 $hub = 'http://domain.hub';
35 $conf = new ConfigManager(self::$configFile); 35 $conf = new ConfigManager(self::$configFile);
36 $conf->set('plugins.PUBSUBHUB_URL', $hub); 36 $conf->set('plugins.PUBSUBHUB_URL', $hub);
37 $data['_PAGE_'] = Router::$PAGE_FEED_RSS; 37 $data['_PAGE_'] = TemplatePage::FEED_RSS;
38 38
39 $data = hook_pubsubhubbub_render_feed($data, $conf); 39 $data = hook_pubsubhubbub_render_feed($data, $conf);
40 $expected = '<atom:link rel="hub" href="'. $hub .'" />'; 40 $expected = '<atom:link rel="hub" href="'. $hub .'" />';
@@ -49,7 +49,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
49 $hub = 'http://domain.hub'; 49 $hub = 'http://domain.hub';
50 $conf = new ConfigManager(self::$configFile); 50 $conf = new ConfigManager(self::$configFile);
51 $conf->set('plugins.PUBSUBHUB_URL', $hub); 51 $conf->set('plugins.PUBSUBHUB_URL', $hub);
52 $data['_PAGE_'] = Router::$PAGE_FEED_ATOM; 52 $data['_PAGE_'] = TemplatePage::FEED_ATOM;
53 53
54 $data = hook_pubsubhubbub_render_feed($data, $conf); 54 $data = hook_pubsubhubbub_render_feed($data, $conf);
55 $expected = '<link rel="hub" href="'. $hub .'" />'; 55 $expected = '<link rel="hub" href="'. $hub .'" />';
diff --git a/tests/plugins/PluginQrcodeTest.php b/tests/plugins/PluginQrcodeTest.php
index 0c61e14a..c9f8c733 100644
--- a/tests/plugins/PluginQrcodeTest.php
+++ b/tests/plugins/PluginQrcodeTest.php
@@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Qrcode;
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11require_once 'plugins/qrcode/qrcode.php'; 11require_once 'plugins/qrcode/qrcode.php';
12 12
@@ -57,7 +57,7 @@ class PluginQrcodeTest extends \PHPUnit\Framework\TestCase
57 { 57 {
58 $str = 'stuff'; 58 $str = 'stuff';
59 $data = array($str => $str); 59 $data = array($str => $str);
60 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 60 $data['_PAGE_'] = TemplatePage::LINKLIST;
61 61
62 $data = hook_qrcode_render_footer($data); 62 $data = hook_qrcode_render_footer($data);
63 $this->assertEquals($str, $data[$str]); 63 $this->assertEquals($str, $data[$str]);
diff --git a/tests/updater/UpdaterTest.php b/tests/updater/UpdaterTest.php
index c689982b..afc35aec 100644
--- a/tests/updater/UpdaterTest.php
+++ b/tests/updater/UpdaterTest.php
@@ -2,7 +2,10 @@
2namespace Shaarli\Updater; 2namespace Shaarli\Updater;
3 3
4use Exception; 4use Exception;
5use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Bookmark\BookmarkServiceInterface;
5use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
6 9
7require_once 'tests/updater/DummyUpdater.php'; 10require_once 'tests/updater/DummyUpdater.php';
8require_once 'tests/utils/ReferenceLinkDB.php'; 11require_once 'tests/utils/ReferenceLinkDB.php';
@@ -29,6 +32,12 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
29 */ 32 */
30 protected $conf; 33 protected $conf;
31 34
35 /** @var BookmarkServiceInterface */
36 protected $bookmarkService;
37
38 /** @var Updater */
39 protected $updater;
40
32 /** 41 /**
33 * Executed before each test. 42 * Executed before each test.
34 */ 43 */
@@ -36,6 +45,8 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
36 { 45 {
37 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); 46 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
38 $this->conf = new ConfigManager(self::$configFile); 47 $this->conf = new ConfigManager(self::$configFile);
48 $this->bookmarkService = new BookmarkFileService($this->conf, $this->createMock(History::class), true);
49 $this->updater = new Updater([], $this->bookmarkService, $this->conf, true);
39 } 50 }
40 51
41 /** 52 /**
@@ -167,4 +178,12 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
167 $updater = new DummyUpdater($updates, array(), $this->conf, true); 178 $updater = new DummyUpdater($updates, array(), $this->conf, true);
168 $updater->update(); 179 $updater->update();
169 } 180 }
181
182 public function testUpdateMethodRelativeHomeLinkRename(): void
183 {
184 $this->conf->set('general.header_link', '?');
185 $this->updater->updateMethodRelativeHomeLink();
186
187 static::assertSame();
188 }
170} 189}
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 8c1c2036..c7617b22 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -224,7 +224,7 @@
224 </div> 224 </div>
225 {/if} 225 {/if}
226 {/if} 226 {/if}
227 <a href="{$base_path}/?{$value.shorturl}" title="{$strPermalink}"> 227 <a href="{$base_path}/shaare/{$value.shorturl}" title="{$strPermalink}">
228 {if="!$hide_timestamps || $is_logged_in"} 228 {if="!$hide_timestamps || $is_logged_in"}
229 {$updated=$value.updated_timestamp ? $strEdited. format_date($value.updated) : $strPermalink} 229 {$updated=$value.updated_timestamp ? $strEdited. format_date($value.updated) : $strPermalink}
230 <span class="linkdate" title="{$updated}"> 230 <span class="linkdate" title="{$updated}">