]>
Commit | Line | Data |
---|---|---|
c266a89d A |
1 | <?php |
2 | ||
3 | declare(strict_types=1); | |
4 | ||
2899ebb5 | 5 | namespace Shaarli\Front\Controller\Visitor; |
c266a89d A |
6 | |
7 | use Slim\Http\Request; | |
8 | use Slim\Http\Response; | |
9 | ||
10 | /** | |
11 | * Class TagCloud | |
12 | * | |
60ae2412 | 13 | * Slim controller used to render the tag cloud and tag list pages. |
c266a89d | 14 | */ |
2899ebb5 | 15 | class TagCloudController extends ShaarliVisitorController |
c266a89d | 16 | { |
60ae2412 A |
17 | protected const TYPE_CLOUD = 'cloud'; |
18 | protected const TYPE_LIST = 'list'; | |
19 | ||
3772298e A |
20 | /** |
21 | * Display the tag cloud through the template engine. | |
22 | * This controller a few filters: | |
23 | * - Visibility stored in the session for logged in users | |
24 | * - `searchtags` query parameter: will return tags associated with filter in at least one bookmark | |
25 | */ | |
26 | public function cloud(Request $request, Response $response): Response | |
60ae2412 A |
27 | { |
28 | return $this->processRequest(static::TYPE_CLOUD, $request, $response); | |
29 | } | |
30 | ||
31 | /** | |
32 | * Display the tag list through the template engine. | |
33 | * This controller a few filters: | |
34 | * - Visibility stored in the session for logged in users | |
35 | * - `searchtags` query parameter: will return tags associated with filter in at least one bookmark | |
36 | * - `sort` query parameters: | |
37 | * + `usage` (default): most used tags first | |
38 | * + `alpha`: alphabetical order | |
39 | */ | |
40 | public function list(Request $request, Response $response): Response | |
41 | { | |
42 | return $this->processRequest(static::TYPE_LIST, $request, $response); | |
43 | } | |
44 | ||
45 | /** | |
46 | * Process the request for both tag cloud and tag list endpoints. | |
47 | */ | |
48 | protected function processRequest(string $type, Request $request, Response $response): Response | |
c266a89d | 49 | { |
b3bd8c3e | 50 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); |
c266a89d A |
51 | if ($this->container->loginManager->isLoggedIn() === true) { |
52 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); | |
53 | } | |
54 | ||
60ae2412 | 55 | $sort = $request->getQueryParam('sort'); |
c266a89d | 56 | $searchTags = $request->getQueryParam('searchtags'); |
b3bd8c3e | 57 | $filteringTags = $searchTags !== null ? explode($tagsSeparator, $searchTags) : []; |
c266a89d A |
58 | |
59 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); | |
60 | ||
60ae2412 A |
61 | if (static::TYPE_CLOUD === $type || 'alpha' === $sort) { |
62 | // TODO: the sorting should be handled by bookmarkService instead of the controller | |
63 | alphabetical_sort($tags, false, true); | |
64 | } | |
c266a89d | 65 | |
60ae2412 A |
66 | if (static::TYPE_CLOUD === $type) { |
67 | $tags = $this->formatTagsForCloud($tags); | |
68 | } | |
c266a89d | 69 | |
72fbbcd6 A |
70 | $tagsUrl = []; |
71 | foreach ($tags as $tag => $value) { | |
72 | $tagsUrl[escape($tag)] = urlencode((string) $tag); | |
73 | } | |
74 | ||
b3bd8c3e A |
75 | $searchTags = tags_array2str($filteringTags, $tagsSeparator); |
76 | $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; | |
77 | $searchTagsUrl = urlencode($searchTags); | |
c266a89d | 78 | $data = [ |
72fbbcd6 A |
79 | 'search_tags' => escape($searchTags), |
80 | 'search_tags_url' => $searchTagsUrl, | |
81 | 'tags' => escape($tags), | |
82 | 'tags_url' => $tagsUrl, | |
c266a89d | 83 | ]; |
9fbc4229 A |
84 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); |
85 | $this->assignAllView($data); | |
c266a89d | 86 | |
53054b2b | 87 | $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) . ' - ' : ''; |
c266a89d A |
88 | $this->assignView( |
89 | 'pagetitle', | |
53054b2b | 90 | $searchTags . t('Tag ' . $type) . ' - ' . $this->container->conf->get('general.title', 'Shaarli') |
c266a89d A |
91 | ); |
92 | ||
9fbc4229 | 93 | return $response->write($this->render('tag.' . $type)); |
c266a89d A |
94 | } |
95 | ||
60ae2412 A |
96 | /** |
97 | * Format the tags array for the tag cloud template. | |
98 | * | |
99 | * @param array<string, int> $tags List of tags as key with count as value | |
100 | * | |
101 | * @return mixed[] List of tags as key, with count and expected font size in a subarray | |
102 | */ | |
3772298e A |
103 | protected function formatTagsForCloud(array $tags): array |
104 | { | |
105 | // We sort tags alphabetically, then choose a font size according to count. | |
106 | // First, find max value. | |
107 | $maxCount = count($tags) > 0 ? max($tags) : 0; | |
108 | $logMaxCount = $maxCount > 1 ? log($maxCount, 30) : 1; | |
109 | $tagList = []; | |
110 | foreach ($tags as $key => $value) { | |
111 | // Tag font size scaling: | |
112 | // default 15 and 30 logarithm bases affect scaling, | |
113 | // 2.2 and 0.8 are arbitrary font sizes in em. | |
114 | $size = log($value, 15) / $logMaxCount * 2.2 + 0.8; | |
115 | $tagList[$key] = [ | |
116 | 'count' => $value, | |
117 | 'size' => number_format($size, 2, '.', ''), | |
118 | ]; | |
119 | } | |
120 | ||
121 | return $tagList; | |
122 | } | |
c266a89d | 123 | } |