diff options
-rw-r--r-- | application/front/controller/admin/ManageShaareController.php | 36 | ||||
-rw-r--r-- | index.php | 17 | ||||
-rw-r--r-- | tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php | 145 | ||||
-rw-r--r-- | tpl/default/linklist.html | 2 |
4 files changed, 185 insertions, 15 deletions
diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php index ff330a99..bdfc5ca7 100644 --- a/application/front/controller/admin/ManageShaareController.php +++ b/application/front/controller/admin/ManageShaareController.php | |||
@@ -297,6 +297,42 @@ class ManageShaareController extends ShaarliAdminController | |||
297 | } | 297 | } |
298 | 298 | ||
299 | /** | 299 | /** |
300 | * GET /admin/shaare/{id}/pin - Pin or unpin a bookmark. | ||
301 | */ | ||
302 | public function pinBookmark(Request $request, Response $response, array $args): Response | ||
303 | { | ||
304 | $this->checkToken($request); | ||
305 | |||
306 | $id = $args['id'] ?? ''; | ||
307 | try { | ||
308 | if (false === ctype_digit($id)) { | ||
309 | throw new BookmarkNotFoundException(); | ||
310 | } | ||
311 | $bookmark = $this->container->bookmarkService->get((int) $id); // Read database | ||
312 | } catch (BookmarkNotFoundException $e) { | ||
313 | $this->saveErrorMessage(sprintf( | ||
314 | t('Bookmark with identifier %s could not be found.'), | ||
315 | $id | ||
316 | )); | ||
317 | |||
318 | return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); | ||
319 | } | ||
320 | |||
321 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
322 | |||
323 | $bookmark->setSticky(!$bookmark->isSticky()); | ||
324 | |||
325 | // To preserve backward compatibility with 3rd parties, plugins still use arrays | ||
326 | $data = $formatter->format($bookmark); | ||
327 | $this->container->pluginManager->executeHooks('save_link', $data); | ||
328 | $bookmark->fromArray($data); | ||
329 | |||
330 | $this->container->bookmarkService->set($bookmark); | ||
331 | |||
332 | return $this->redirectFromReferer($request, $response, ['/pin'], ['pin']); | ||
333 | } | ||
334 | |||
335 | /** | ||
300 | * Helper function used to display the shaare form whether it's a new or existing bookmark. | 336 | * Helper function used to display the shaare form whether it's a new or existing bookmark. |
301 | * | 337 | * |
302 | * @param array $link data used in template, either from parameters or from the data store | 338 | * @param array $link data used in template, either from parameters or from the data store |
@@ -567,20 +567,8 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM | |||
567 | } | 567 | } |
568 | 568 | ||
569 | if ($targetPage == Router::$PAGE_PINLINK) { | 569 | if ($targetPage == Router::$PAGE_PINLINK) { |
570 | if (! isset($_GET['id']) || !$bookmarkService->exists($_GET['id'])) { | 570 | // This route is no longer supported in legacy mode |
571 | // FIXME! Use a proper error system. | 571 | header('Location: ./'); |
572 | $msg = t('Invalid link ID provided'); | ||
573 | echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>'; | ||
574 | exit; | ||
575 | } | ||
576 | if (! $sessionManager->checkToken($_GET['token'])) { | ||
577 | die('Wrong token.'); | ||
578 | } | ||
579 | |||
580 | $link = $bookmarkService->get($_GET['id']); | ||
581 | $link->setSticky(! $link->isSticky()); | ||
582 | $bookmarkService->set($link); | ||
583 | header('Location: '.index_url($_SERVER)); | ||
584 | exit; | 572 | exit; |
585 | } | 573 | } |
586 | 574 | ||
@@ -1121,6 +1109,7 @@ $app->group('', function () { | |||
1121 | $this->post('/admin/shaare', '\Shaarli\Front\Controller\Admin\ManageShaareController:save'); | 1109 | $this->post('/admin/shaare', '\Shaarli\Front\Controller\Admin\ManageShaareController:save'); |
1122 | $this->get('/admin/shaare/delete', '\Shaarli\Front\Controller\Admin\ManageShaareController:deleteBookmark'); | 1110 | $this->get('/admin/shaare/delete', '\Shaarli\Front\Controller\Admin\ManageShaareController:deleteBookmark'); |
1123 | $this->get('/admin/shaare/visibility', '\Shaarli\Front\Controller\Admin\ManageShaareController:changeVisibility'); | 1111 | $this->get('/admin/shaare/visibility', '\Shaarli\Front\Controller\Admin\ManageShaareController:changeVisibility'); |
1112 | $this->get('/admin/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ManageShaareController:pinBookmark'); | ||
1124 | 1113 | ||
1125 | $this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage'); | 1114 | $this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage'); |
1126 | $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility'); | 1115 | $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility'); |
diff --git a/tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php new file mode 100644 index 00000000..1607b475 --- /dev/null +++ b/tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php | |||
@@ -0,0 +1,145 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest; | ||
6 | |||
7 | use PHPUnit\Framework\TestCase; | ||
8 | use Shaarli\Bookmark\Bookmark; | ||
9 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; | ||
10 | use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper; | ||
11 | use Shaarli\Front\Controller\Admin\ManageShaareController; | ||
12 | use Shaarli\Http\HttpAccess; | ||
13 | use Shaarli\Security\SessionManager; | ||
14 | use Slim\Http\Request; | ||
15 | use Slim\Http\Response; | ||
16 | |||
17 | class PinBookmarkTest extends TestCase | ||
18 | { | ||
19 | use FrontAdminControllerMockHelper; | ||
20 | |||
21 | /** @var ManageShaareController */ | ||
22 | protected $controller; | ||
23 | |||
24 | public function setUp(): void | ||
25 | { | ||
26 | $this->createContainer(); | ||
27 | |||
28 | $this->container->httpAccess = $this->createMock(HttpAccess::class); | ||
29 | $this->controller = new ManageShaareController($this->container); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Test pin bookmark - with valid input | ||
34 | * | ||
35 | * @dataProvider initialStickyValuesProvider() | ||
36 | */ | ||
37 | public function testPinBookmarkIsStickyNull(?bool $sticky, bool $expectedValue): void | ||
38 | { | ||
39 | $id = 123; | ||
40 | |||
41 | $request = $this->createMock(Request::class); | ||
42 | $response = new Response(); | ||
43 | |||
44 | $bookmark = (new Bookmark()) | ||
45 | ->setId(123) | ||
46 | ->setUrl('http://domain.tld') | ||
47 | ->setTitle('Title 123') | ||
48 | ->setSticky($sticky) | ||
49 | ; | ||
50 | |||
51 | $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); | ||
52 | $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true); | ||
53 | |||
54 | // Make sure that PluginManager hook is triggered | ||
55 | $this->container->pluginManager | ||
56 | ->expects(static::once()) | ||
57 | ->method('executeHooks') | ||
58 | ->with('save_link') | ||
59 | ; | ||
60 | |||
61 | $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]); | ||
62 | |||
63 | static::assertSame(302, $result->getStatusCode()); | ||
64 | static::assertSame(['/subfolder/'], $result->getHeader('location')); | ||
65 | |||
66 | static::assertSame($expectedValue, $bookmark->isSticky()); | ||
67 | } | ||
68 | |||
69 | public function initialStickyValuesProvider(): array | ||
70 | { | ||
71 | // [initialStickyState, isStickyAfterPin] | ||
72 | return [[null, true], [false, true], [true, false]]; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Test pin bookmark - invalid bookmark ID | ||
77 | */ | ||
78 | public function testDisplayEditFormInvalidId(): void | ||
79 | { | ||
80 | $id = 'invalid'; | ||
81 | |||
82 | $request = $this->createMock(Request::class); | ||
83 | $response = new Response(); | ||
84 | |||
85 | $this->container->sessionManager | ||
86 | ->expects(static::once()) | ||
87 | ->method('setSessionParameter') | ||
88 | ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.']) | ||
89 | ; | ||
90 | |||
91 | $result = $this->controller->pinBookmark($request, $response, ['id' => $id]); | ||
92 | |||
93 | static::assertSame(302, $result->getStatusCode()); | ||
94 | static::assertSame(['/subfolder/'], $result->getHeader('location')); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Test pin bookmark - Bookmark ID not provided | ||
99 | */ | ||
100 | public function testDisplayEditFormIdNotProvided(): void | ||
101 | { | ||
102 | $request = $this->createMock(Request::class); | ||
103 | $response = new Response(); | ||
104 | |||
105 | $this->container->sessionManager | ||
106 | ->expects(static::once()) | ||
107 | ->method('setSessionParameter') | ||
108 | ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.']) | ||
109 | ; | ||
110 | |||
111 | $result = $this->controller->pinBookmark($request, $response, []); | ||
112 | |||
113 | static::assertSame(302, $result->getStatusCode()); | ||
114 | static::assertSame(['/subfolder/'], $result->getHeader('location')); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Test pin bookmark - bookmark not found | ||
119 | */ | ||
120 | public function testDisplayEditFormBookmarkNotFound(): void | ||
121 | { | ||
122 | $id = 123; | ||
123 | |||
124 | $request = $this->createMock(Request::class); | ||
125 | $response = new Response(); | ||
126 | |||
127 | $this->container->bookmarkService | ||
128 | ->expects(static::once()) | ||
129 | ->method('get') | ||
130 | ->with($id) | ||
131 | ->willThrowException(new BookmarkNotFoundException()) | ||
132 | ; | ||
133 | |||
134 | $this->container->sessionManager | ||
135 | ->expects(static::once()) | ||
136 | ->method('setSessionParameter') | ||
137 | ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.']) | ||
138 | ; | ||
139 | |||
140 | $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]); | ||
141 | |||
142 | static::assertSame(302, $result->getStatusCode()); | ||
143 | static::assertSame(['/subfolder/'], $result->getHeader('location')); | ||
144 | } | ||
145 | } | ||
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html index 2e2f96d6..b0a5fdf2 100644 --- a/tpl/default/linklist.html +++ b/tpl/default/linklist.html | |||
@@ -207,7 +207,7 @@ | |||
207 | </a> | 207 | </a> |
208 | </span> | 208 | </span> |
209 | <span class="linklist-item-infos-controls-item ctrl-pin"> | 209 | <span class="linklist-item-infos-controls-item ctrl-pin"> |
210 | <a href="{$base_path}/?do=pin&id={$value.id}&token={$token}" | 210 | <a href="{$base_path}/admin/shaare/{$value.id}/pin?token={$token}" |
211 | title="{$strToggleSticky}" aria-label="{$strToggleSticky}" class="pin-link {if="$value.sticky"}pinned-link{/if} pure-u-0 pure-u-lg-visible"> | 211 | title="{$strToggleSticky}" aria-label="{$strToggleSticky}" class="pin-link {if="$value.sticky"}pinned-link{/if} pure-u-0 pure-u-lg-visible"> |
212 | <i class="fa fa-thumb-tack" aria-hidden="true"></i> | 212 | <i class="fa fa-thumb-tack" aria-hidden="true"></i> |
213 | </a> | 213 | </a> |