diff options
-rw-r--r-- | application/front/controllers/TagCloudController.php | 59 | ||||
-rw-r--r-- | doc/md/Translations.md | 2 | ||||
-rw-r--r-- | index.php | 24 | ||||
-rw-r--r-- | tests/front/controller/TagCloudControllerTest.php | 203 | ||||
-rw-r--r-- | tpl/default/changetag.html | 2 | ||||
-rw-r--r-- | tpl/default/tag.list.html | 4 | ||||
-rw-r--r-- | tpl/default/tag.sort.html | 4 |
7 files changed, 247 insertions, 51 deletions
diff --git a/application/front/controllers/TagCloudController.php b/application/front/controllers/TagCloudController.php index 93e3ae27..1ff7c2e6 100644 --- a/application/front/controllers/TagCloudController.php +++ b/application/front/controllers/TagCloudController.php | |||
@@ -10,12 +10,15 @@ use Slim\Http\Response; | |||
10 | /** | 10 | /** |
11 | * Class TagCloud | 11 | * Class TagCloud |
12 | * | 12 | * |
13 | * Slim controller used to render the tag cloud page. | 13 | * Slim controller used to render the tag cloud and tag list pages. |
14 | * | 14 | * |
15 | * @package Front\Controller | 15 | * @package Front\Controller |
16 | */ | 16 | */ |
17 | class TagCloudController extends ShaarliController | 17 | class TagCloudController extends ShaarliController |
18 | { | 18 | { |
19 | protected const TYPE_CLOUD = 'cloud'; | ||
20 | protected const TYPE_LIST = 'list'; | ||
21 | |||
19 | /** | 22 | /** |
20 | * Display the tag cloud through the template engine. | 23 | * Display the tag cloud through the template engine. |
21 | * This controller a few filters: | 24 | * This controller a few filters: |
@@ -24,26 +27,53 @@ class TagCloudController extends ShaarliController | |||
24 | */ | 27 | */ |
25 | public function cloud(Request $request, Response $response): Response | 28 | public function cloud(Request $request, Response $response): Response |
26 | { | 29 | { |
30 | return $this->processRequest(static::TYPE_CLOUD, $request, $response); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Display the tag list through the template engine. | ||
35 | * This controller a few filters: | ||
36 | * - Visibility stored in the session for logged in users | ||
37 | * - `searchtags` query parameter: will return tags associated with filter in at least one bookmark | ||
38 | * - `sort` query parameters: | ||
39 | * + `usage` (default): most used tags first | ||
40 | * + `alpha`: alphabetical order | ||
41 | */ | ||
42 | public function list(Request $request, Response $response): Response | ||
43 | { | ||
44 | return $this->processRequest(static::TYPE_LIST, $request, $response); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Process the request for both tag cloud and tag list endpoints. | ||
49 | */ | ||
50 | protected function processRequest(string $type, Request $request, Response $response): Response | ||
51 | { | ||
27 | if ($this->container->loginManager->isLoggedIn() === true) { | 52 | if ($this->container->loginManager->isLoggedIn() === true) { |
28 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); | 53 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); |
29 | } | 54 | } |
30 | 55 | ||
56 | $sort = $request->getQueryParam('sort'); | ||
31 | $searchTags = $request->getQueryParam('searchtags'); | 57 | $searchTags = $request->getQueryParam('searchtags'); |
32 | $filteringTags = $searchTags !== null ? explode(' ', $searchTags) : []; | 58 | $filteringTags = $searchTags !== null ? explode(' ', $searchTags) : []; |
33 | 59 | ||
34 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); | 60 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); |
35 | 61 | ||
36 | // TODO: the sorting should be handled by bookmarkService instead of the controller | 62 | if (static::TYPE_CLOUD === $type || 'alpha' === $sort) { |
37 | alphabetical_sort($tags, false, true); | 63 | // TODO: the sorting should be handled by bookmarkService instead of the controller |
64 | alphabetical_sort($tags, false, true); | ||
65 | } | ||
38 | 66 | ||
39 | $tagList = $this->formatTagsForCloud($tags); | 67 | if (static::TYPE_CLOUD === $type) { |
68 | $tags = $this->formatTagsForCloud($tags); | ||
69 | } | ||
40 | 70 | ||
41 | $searchTags = implode(' ', escape($filteringTags)); | 71 | $searchTags = implode(' ', escape($filteringTags)); |
42 | $data = [ | 72 | $data = [ |
43 | 'search_tags' => $searchTags, | 73 | 'search_tags' => $searchTags, |
44 | 'tags' => $tagList, | 74 | 'tags' => $tags, |
45 | ]; | 75 | ]; |
46 | $data = $this->executeHooks($data); | 76 | $data = $this->executeHooks('tag' . $type, $data); |
47 | foreach ($data as $key => $value) { | 77 | foreach ($data as $key => $value) { |
48 | $this->assignView($key, $value); | 78 | $this->assignView($key, $value); |
49 | } | 79 | } |
@@ -51,12 +81,19 @@ class TagCloudController extends ShaarliController | |||
51 | $searchTags = !empty($searchTags) ? $searchTags .' - ' : ''; | 81 | $searchTags = !empty($searchTags) ? $searchTags .' - ' : ''; |
52 | $this->assignView( | 82 | $this->assignView( |
53 | 'pagetitle', | 83 | 'pagetitle', |
54 | $searchTags . t('Tag cloud') .' - '. $this->container->conf->get('general.title', 'Shaarli') | 84 | $searchTags . t('Tag '. $type) .' - '. $this->container->conf->get('general.title', 'Shaarli') |
55 | ); | 85 | ); |
56 | 86 | ||
57 | return $response->write($this->render('tag.cloud')); | 87 | return $response->write($this->render('tag.'. $type)); |
58 | } | 88 | } |
59 | 89 | ||
90 | /** | ||
91 | * Format the tags array for the tag cloud template. | ||
92 | * | ||
93 | * @param array<string, int> $tags List of tags as key with count as value | ||
94 | * | ||
95 | * @return mixed[] List of tags as key, with count and expected font size in a subarray | ||
96 | */ | ||
60 | protected function formatTagsForCloud(array $tags): array | 97 | protected function formatTagsForCloud(array $tags): array |
61 | { | 98 | { |
62 | // We sort tags alphabetically, then choose a font size according to count. | 99 | // We sort tags alphabetically, then choose a font size according to count. |
@@ -81,12 +118,12 @@ class TagCloudController extends ShaarliController | |||
81 | /** | 118 | /** |
82 | * @param mixed[] $data Template data | 119 | * @param mixed[] $data Template data |
83 | * | 120 | * |
84 | * @return mixed[] Template data after active plugins render_picwall hook execution. | 121 | * @return mixed[] Template data after active plugins hook execution. |
85 | */ | 122 | */ |
86 | protected function executeHooks(array $data): array | 123 | protected function executeHooks(string $template, array $data): array |
87 | { | 124 | { |
88 | $this->container->pluginManager->executeHooks( | 125 | $this->container->pluginManager->executeHooks( |
89 | 'render_tagcloud', | 126 | 'render_'. $template, |
90 | $data, | 127 | $data, |
91 | ['loggedin' => $this->container->loginManager->isLoggedIn()] | 128 | ['loggedin' => $this->container->loginManager->isLoggedIn()] |
92 | ); | 129 | ); |
diff --git a/doc/md/Translations.md b/doc/md/Translations.md index b8b7053f..c1a2885d 100644 --- a/doc/md/Translations.md +++ b/doc/md/Translations.md | |||
@@ -45,7 +45,7 @@ http://<replace_domain>/login | |||
45 | http://<replace_domain>/picture-wall | 45 | http://<replace_domain>/picture-wall |
46 | http://<replace_domain>/?do=pluginadmin | 46 | http://<replace_domain>/?do=pluginadmin |
47 | http://<replace_domain>/tag-cloud | 47 | http://<replace_domain>/tag-cloud |
48 | http://<replace_domain>/?do=taglist | 48 | http://<replace_domain>/tag-list |
49 | ``` | 49 | ``` |
50 | 50 | ||
51 | #### Improve existing translation | 51 | #### Improve existing translation |
@@ -622,28 +622,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM | |||
622 | 622 | ||
623 | // -------- Tag list | 623 | // -------- Tag list |
624 | if ($targetPage == Router::$PAGE_TAGLIST) { | 624 | if ($targetPage == Router::$PAGE_TAGLIST) { |
625 | $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; | 625 | header('Location: ./tag-list'); |
626 | $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; | ||
627 | $tags = $bookmarkService->bookmarksCountPerTag($filteringTags, $visibility); | ||
628 | |||
629 | if (! empty($_GET['sort']) && $_GET['sort'] === 'alpha') { | ||
630 | alphabetical_sort($tags, false, true); | ||
631 | } | ||
632 | |||
633 | $searchTags = implode(' ', escape($filteringTags)); | ||
634 | $data = [ | ||
635 | 'search_tags' => $searchTags, | ||
636 | 'tags' => $tags, | ||
637 | ]; | ||
638 | $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => $loginManager->isLoggedIn()]); | ||
639 | |||
640 | foreach ($data as $key => $value) { | ||
641 | $PAGE->assign($key, $value); | ||
642 | } | ||
643 | |||
644 | $searchTags = ! empty($searchTags) ? $searchTags .' - ' : ''; | ||
645 | $PAGE->assign('pagetitle', $searchTags . t('Tag list') .' - '. $conf->get('general.title', 'Shaarli')); | ||
646 | $PAGE->renderPage('tag.list'); | ||
647 | exit; | 626 | exit; |
648 | } | 627 | } |
649 | 628 | ||
@@ -1870,6 +1849,7 @@ $app->group('', function () { | |||
1870 | $this->get('/logout', '\Shaarli\Front\Controller\LogoutController:index')->setName('logout'); | 1849 | $this->get('/logout', '\Shaarli\Front\Controller\LogoutController:index')->setName('logout'); |
1871 | $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall'); | 1850 | $this->get('/picture-wall', '\Shaarli\Front\Controller\PictureWallController:index')->setName('picwall'); |
1872 | $this->get('/tag-cloud', '\Shaarli\Front\Controller\TagCloudController:cloud')->setName('tagcloud'); | 1851 | $this->get('/tag-cloud', '\Shaarli\Front\Controller\TagCloudController:cloud')->setName('tagcloud'); |
1852 | $this->get('/tag-list', '\Shaarli\Front\Controller\TagCloudController:list')->setName('taglist'); | ||
1873 | $this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\TagController:addTag')->setName('add-tag'); | 1853 | $this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\TagController:addTag')->setName('add-tag'); |
1874 | })->add('\Shaarli\Front\ShaarliMiddleware'); | 1854 | })->add('\Shaarli\Front\ShaarliMiddleware'); |
1875 | 1855 | ||
diff --git a/tests/front/controller/TagCloudControllerTest.php b/tests/front/controller/TagCloudControllerTest.php index 352bdee2..719610d7 100644 --- a/tests/front/controller/TagCloudControllerTest.php +++ b/tests/front/controller/TagCloudControllerTest.php | |||
@@ -30,6 +30,9 @@ class TagCloudControllerTest extends TestCase | |||
30 | $this->controller = new TagCloudController($this->container); | 30 | $this->controller = new TagCloudController($this->container); |
31 | } | 31 | } |
32 | 32 | ||
33 | /** | ||
34 | * Tag Cloud - default parameters | ||
35 | */ | ||
33 | public function testValidCloudControllerInvokeDefault(): void | 36 | public function testValidCloudControllerInvokeDefault(): void |
34 | { | 37 | { |
35 | $this->createValidContainerMockSet(); | 38 | $this->createValidContainerMockSet(); |
@@ -42,7 +45,6 @@ class TagCloudControllerTest extends TestCase | |||
42 | $expectedOrder = ['abc', 'def', 'ghi']; | 45 | $expectedOrder = ['abc', 'def', 'ghi']; |
43 | 46 | ||
44 | $request = $this->createMock(Request::class); | 47 | $request = $this->createMock(Request::class); |
45 | $request->expects(static::once())->method('getQueryParam')->with('searchtags')->willReturn(null); | ||
46 | $response = new Response(); | 48 | $response = new Response(); |
47 | 49 | ||
48 | // Save RainTPL assigned variables | 50 | // Save RainTPL assigned variables |
@@ -92,7 +94,7 @@ class TagCloudControllerTest extends TestCase | |||
92 | } | 94 | } |
93 | 95 | ||
94 | /** | 96 | /** |
95 | * Additional parameters: | 97 | * Tag Cloud - Additional parameters: |
96 | * - logged in | 98 | * - logged in |
97 | * - visibility private | 99 | * - visibility private |
98 | * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore) | 100 | * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore) |
@@ -101,18 +103,17 @@ class TagCloudControllerTest extends TestCase | |||
101 | { | 103 | { |
102 | $this->createValidContainerMockSet(); | 104 | $this->createValidContainerMockSet(); |
103 | 105 | ||
104 | $allTags = [ | ||
105 | 'ghi' => 1, | ||
106 | 'abc' => 3, | ||
107 | 'def' => 12, | ||
108 | ]; | ||
109 | |||
110 | $request = $this->createMock(Request::class); | 106 | $request = $this->createMock(Request::class); |
111 | $request | 107 | $request |
112 | ->expects(static::once()) | ||
113 | ->method('getQueryParam') | 108 | ->method('getQueryParam') |
114 | ->with('searchtags') | 109 | ->with() |
115 | ->willReturn('ghi def') | 110 | ->willReturnCallback(function (string $key): ?string { |
111 | if ('searchtags' === $key) { | ||
112 | return 'ghi def'; | ||
113 | } | ||
114 | |||
115 | return null; | ||
116 | }) | ||
116 | ; | 117 | ; |
117 | $response = new Response(); | 118 | $response = new Response(); |
118 | 119 | ||
@@ -162,12 +163,14 @@ class TagCloudControllerTest extends TestCase | |||
162 | static::assertLessThan(5, $assignedVariables['tags']['abc']['size']); | 163 | static::assertLessThan(5, $assignedVariables['tags']['abc']['size']); |
163 | } | 164 | } |
164 | 165 | ||
166 | /** | ||
167 | * Tag Cloud - empty | ||
168 | */ | ||
165 | public function testEmptyCloud(): void | 169 | public function testEmptyCloud(): void |
166 | { | 170 | { |
167 | $this->createValidContainerMockSet(); | 171 | $this->createValidContainerMockSet(); |
168 | 172 | ||
169 | $request = $this->createMock(Request::class); | 173 | $request = $this->createMock(Request::class); |
170 | $request->expects(static::once())->method('getQueryParam')->with('searchtags')->willReturn(null); | ||
171 | $response = new Response(); | 174 | $response = new Response(); |
172 | 175 | ||
173 | // Save RainTPL assigned variables | 176 | // Save RainTPL assigned variables |
@@ -208,6 +211,182 @@ class TagCloudControllerTest extends TestCase | |||
208 | static::assertCount(0, $assignedVariables['tags']); | 211 | static::assertCount(0, $assignedVariables['tags']); |
209 | } | 212 | } |
210 | 213 | ||
214 | /** | ||
215 | * Tag List - Default sort is by usage DESC | ||
216 | */ | ||
217 | public function testValidListControllerInvokeDefault(): void | ||
218 | { | ||
219 | $this->createValidContainerMockSet(); | ||
220 | |||
221 | $allTags = [ | ||
222 | 'def' => 12, | ||
223 | 'abc' => 3, | ||
224 | 'ghi' => 1, | ||
225 | ]; | ||
226 | |||
227 | $request = $this->createMock(Request::class); | ||
228 | $response = new Response(); | ||
229 | |||
230 | // Save RainTPL assigned variables | ||
231 | $assignedVariables = []; | ||
232 | $this->assignTemplateVars($assignedVariables); | ||
233 | |||
234 | $this->container->bookmarkService | ||
235 | ->expects(static::once()) | ||
236 | ->method('bookmarksCountPerTag') | ||
237 | ->with([], null) | ||
238 | ->willReturnCallback(function () use ($allTags): array { | ||
239 | return $allTags; | ||
240 | }) | ||
241 | ; | ||
242 | |||
243 | // Make sure that PluginManager hook is triggered | ||
244 | $this->container->pluginManager | ||
245 | ->expects(static::at(0)) | ||
246 | ->method('executeHooks') | ||
247 | ->willReturnCallback(function (string $hook, array $data, array $param): array { | ||
248 | static::assertSame('render_taglist', $hook); | ||
249 | static::assertSame('', $data['search_tags']); | ||
250 | static::assertCount(3, $data['tags']); | ||
251 | |||
252 | static::assertArrayHasKey('loggedin', $param); | ||
253 | |||
254 | return $data; | ||
255 | }) | ||
256 | ; | ||
257 | |||
258 | $result = $this->controller->list($request, $response); | ||
259 | |||
260 | static::assertSame(200, $result->getStatusCode()); | ||
261 | static::assertSame('tag.list', (string) $result->getBody()); | ||
262 | static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']); | ||
263 | |||
264 | static::assertSame('', $assignedVariables['search_tags']); | ||
265 | static::assertCount(3, $assignedVariables['tags']); | ||
266 | |||
267 | foreach ($allTags as $tag => $count) { | ||
268 | static::assertSame($count, $assignedVariables['tags'][$tag]); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Tag List - Additional parameters: | ||
274 | * - logged in | ||
275 | * - visibility private | ||
276 | * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore) | ||
277 | * - sort alphabetically | ||
278 | */ | ||
279 | public function testValidListControllerInvokeWithParameters(): void | ||
280 | { | ||
281 | $this->createValidContainerMockSet(); | ||
282 | |||
283 | $request = $this->createMock(Request::class); | ||
284 | $request | ||
285 | ->method('getQueryParam') | ||
286 | ->with() | ||
287 | ->willReturnCallback(function (string $key): ?string { | ||
288 | if ('searchtags' === $key) { | ||
289 | return 'ghi def'; | ||
290 | } elseif ('sort' === $key) { | ||
291 | return 'alpha'; | ||
292 | } | ||
293 | |||
294 | return null; | ||
295 | }) | ||
296 | ; | ||
297 | $response = new Response(); | ||
298 | |||
299 | // Save RainTPL assigned variables | ||
300 | $assignedVariables = []; | ||
301 | $this->assignTemplateVars($assignedVariables); | ||
302 | |||
303 | $this->container->loginManager->method('isLoggedin')->willReturn(true); | ||
304 | $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private'); | ||
305 | |||
306 | $this->container->bookmarkService | ||
307 | ->expects(static::once()) | ||
308 | ->method('bookmarksCountPerTag') | ||
309 | ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE) | ||
310 | ->willReturnCallback(function (): array { | ||
311 | return ['abc' => 3]; | ||
312 | }) | ||
313 | ; | ||
314 | |||
315 | // Make sure that PluginManager hook is triggered | ||
316 | $this->container->pluginManager | ||
317 | ->expects(static::at(0)) | ||
318 | ->method('executeHooks') | ||
319 | ->willReturnCallback(function (string $hook, array $data, array $param): array { | ||
320 | static::assertSame('render_taglist', $hook); | ||
321 | static::assertSame('ghi def', $data['search_tags']); | ||
322 | static::assertCount(1, $data['tags']); | ||
323 | |||
324 | static::assertArrayHasKey('loggedin', $param); | ||
325 | |||
326 | return $data; | ||
327 | }) | ||
328 | ; | ||
329 | |||
330 | $result = $this->controller->list($request, $response); | ||
331 | |||
332 | static::assertSame(200, $result->getStatusCode()); | ||
333 | static::assertSame('tag.list', (string) $result->getBody()); | ||
334 | static::assertSame('ghi def - Tag list - Shaarli', $assignedVariables['pagetitle']); | ||
335 | |||
336 | static::assertSame('ghi def', $assignedVariables['search_tags']); | ||
337 | static::assertCount(1, $assignedVariables['tags']); | ||
338 | static::assertSame(3, $assignedVariables['tags']['abc']); | ||
339 | } | ||
340 | |||
341 | /** | ||
342 | * Tag List - empty | ||
343 | */ | ||
344 | public function testEmptyList(): void | ||
345 | { | ||
346 | $this->createValidContainerMockSet(); | ||
347 | |||
348 | $request = $this->createMock(Request::class); | ||
349 | $response = new Response(); | ||
350 | |||
351 | // Save RainTPL assigned variables | ||
352 | $assignedVariables = []; | ||
353 | $this->assignTemplateVars($assignedVariables); | ||
354 | |||
355 | $this->container->bookmarkService | ||
356 | ->expects(static::once()) | ||
357 | ->method('bookmarksCountPerTag') | ||
358 | ->with([], null) | ||
359 | ->willReturnCallback(function (array $parameters, ?string $visibility): array { | ||
360 | return []; | ||
361 | }) | ||
362 | ; | ||
363 | |||
364 | // Make sure that PluginManager hook is triggered | ||
365 | $this->container->pluginManager | ||
366 | ->expects(static::at(0)) | ||
367 | ->method('executeHooks') | ||
368 | ->willReturnCallback(function (string $hook, array $data, array $param): array { | ||
369 | static::assertSame('render_taglist', $hook); | ||
370 | static::assertSame('', $data['search_tags']); | ||
371 | static::assertCount(0, $data['tags']); | ||
372 | |||
373 | static::assertArrayHasKey('loggedin', $param); | ||
374 | |||
375 | return $data; | ||
376 | }) | ||
377 | ; | ||
378 | |||
379 | $result = $this->controller->list($request, $response); | ||
380 | |||
381 | static::assertSame(200, $result->getStatusCode()); | ||
382 | static::assertSame('tag.list', (string) $result->getBody()); | ||
383 | static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']); | ||
384 | |||
385 | static::assertSame('', $assignedVariables['search_tags']); | ||
386 | static::assertCount(0, $assignedVariables['tags']); | ||
387 | } | ||
388 | |||
389 | |||
211 | protected function createValidContainerMockSet(): void | 390 | protected function createValidContainerMockSet(): void |
212 | { | 391 | { |
213 | $loginManager = $this->createMock(LoginManager::class); | 392 | $loginManager = $this->createMock(LoginManager::class); |
diff --git a/tpl/default/changetag.html b/tpl/default/changetag.html index cc74f786..a1a572ca 100644 --- a/tpl/default/changetag.html +++ b/tpl/default/changetag.html | |||
@@ -32,7 +32,7 @@ | |||
32 | </div> | 32 | </div> |
33 | </form> | 33 | </form> |
34 | 34 | ||
35 | <p>{'You can also edit tags in the'|t} <a href="./?do=taglist&sort=usage">{'tag list'|t}</a>.</p> | 35 | <p>{'You can also edit tags in the'|t} <a href="./tag-list?sort=usage">{'tag list'|t}</a>.</p> |
36 | </div> | 36 | </div> |
37 | </div> | 37 | </div> |
38 | {include="page.footer"} | 38 | {include="page.footer"} |
diff --git a/tpl/default/tag.list.html b/tpl/default/tag.list.html index 01b7a642..3e498f89 100644 --- a/tpl/default/tag.list.html +++ b/tpl/default/tag.list.html | |||
@@ -15,7 +15,7 @@ | |||
15 | <h2 class="window-title">{'Tag list'|t} - {$countTags} {'tags'|t}</h2> | 15 | <h2 class="window-title">{'Tag list'|t} - {$countTags} {'tags'|t}</h2> |
16 | {if="!empty($search_tags)"} | 16 | {if="!empty($search_tags)"} |
17 | <p class="center"> | 17 | <p class="center"> |
18 | <a href="?searchtags={$search_tags|urlencode}" class="pure-button pure-button-shaarli"> | 18 | <a href="./?searchtags={$search_tags|urlencode}" class="pure-button pure-button-shaarli"> |
19 | {'List all links with those tags'|t} | 19 | {'List all links with those tags'|t} |
20 | </a> | 20 | </a> |
21 | </p> | 21 | </p> |
@@ -57,7 +57,7 @@ | |||
57 | {/if} | 57 | {/if} |
58 | 58 | ||
59 | <a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a> | 59 | <a href="./add-tag/{$key|urlencode}" title="{'Filter by tag'|t}" class="count">{$value}</a> |
60 | <a href="?searchtags={$key|urlencode} {$search_tags|urlencode}" class="tag-link">{$key}</a> | 60 | <a href="./?searchtags={$key|urlencode} {$search_tags|urlencode}" class="tag-link">{$key}</a> |
61 | 61 | ||
62 | {loop="$value.tag_plugin"} | 62 | {loop="$value.tag_plugin"} |
63 | {$value} | 63 | {$value} |
diff --git a/tpl/default/tag.sort.html b/tpl/default/tag.sort.html index b7aa7d80..f467e34a 100644 --- a/tpl/default/tag.sort.html +++ b/tpl/default/tag.sort.html | |||
@@ -2,7 +2,7 @@ | |||
2 | <div class="pure-u-1 pure-alert pure-alert-success tag-sort"> | 2 | <div class="pure-u-1 pure-alert pure-alert-success tag-sort"> |
3 | {'Sort by:'|t} | 3 | {'Sort by:'|t} |
4 | <a href="./tag-cloud">{'Cloud'|t}</a> · | 4 | <a href="./tag-cloud">{'Cloud'|t}</a> · |
5 | <a href="./?do=taglist&sort=usage">{'Most used'|t}</a> · | 5 | <a href="./tag-list?sort=usage">{'Most used'|t}</a> · |
6 | <a href="./?do=taglist&sort=alpha">{'Alphabetical'|t}</a> | 6 | <a href="./tag-list?sort=alpha">{'Alphabetical'|t}</a> |
7 | </div> | 7 | </div> |
8 | </div> | 8 | </div> |