aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/front/controller/admin/ManageShaareController.php36
-rw-r--r--index.php17
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php145
-rw-r--r--tpl/default/linklist.html2
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
diff --git a/index.php b/index.php
index 93e5590b..c8660658 100644
--- a/index.php
+++ b/index.php
@@ -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
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
10use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
11use Shaarli\Front\Controller\Admin\ManageShaareController;
12use Shaarli\Http\HttpAccess;
13use Shaarli\Security\SessionManager;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class 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&amp;id={$value.id}&amp;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>