]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | ||
3 | declare(strict_types=1); | |
4 | ||
5 | namespace Shaarli\Front\Controller\Visitor; | |
6 | ||
7 | use Slim\Http\Request; | |
8 | use Slim\Http\Response; | |
9 | ||
10 | /** | |
11 | * Class TagCloud | |
12 | * | |
13 | * Slim controller used to render the tag cloud and tag list pages. | |
14 | */ | |
15 | class TagCloudController extends ShaarliVisitorController | |
16 | { | |
17 | protected const TYPE_CLOUD = 'cloud'; | |
18 | protected const TYPE_LIST = 'list'; | |
19 | ||
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 | |
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 | |
49 | { | |
50 | $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); | |
51 | if ($this->container->loginManager->isLoggedIn() === true) { | |
52 | $visibility = $this->container->sessionManager->getSessionParameter('visibility'); | |
53 | } | |
54 | ||
55 | $sort = $request->getQueryParam('sort'); | |
56 | $searchTags = $request->getQueryParam('searchtags'); | |
57 | $filteringTags = $searchTags !== null ? explode($tagsSeparator, $searchTags) : []; | |
58 | ||
59 | $tags = $this->container->bookmarkService->bookmarksCountPerTag($filteringTags, $visibility ?? null); | |
60 | ||
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 | } | |
65 | ||
66 | if (static::TYPE_CLOUD === $type) { | |
67 | $tags = $this->formatTagsForCloud($tags); | |
68 | } | |
69 | ||
70 | $tagsUrl = []; | |
71 | foreach ($tags as $tag => $value) { | |
72 | $tagsUrl[escape($tag)] = urlencode((string) $tag); | |
73 | } | |
74 | ||
75 | $searchTags = tags_array2str($filteringTags, $tagsSeparator); | |
76 | $searchTags = !empty($searchTags) ? trim($searchTags, $tagsSeparator) . $tagsSeparator : ''; | |
77 | $searchTagsUrl = urlencode($searchTags); | |
78 | $data = [ | |
79 | 'search_tags' => escape($searchTags), | |
80 | 'search_tags_url' => $searchTagsUrl, | |
81 | 'tags' => escape($tags), | |
82 | 'tags_url' => $tagsUrl, | |
83 | ]; | |
84 | $this->executePageHooks('render_tag' . $type, $data, 'tag.' . $type); | |
85 | $this->assignAllView($data); | |
86 | ||
87 | $searchTags = !empty($searchTags) ? trim(str_replace($tagsSeparator, ' ', $searchTags)) . ' - ' : ''; | |
88 | $this->assignView( | |
89 | 'pagetitle', | |
90 | $searchTags . t('Tag ' . $type) . ' - ' . $this->container->conf->get('general.title', 'Shaarli') | |
91 | ); | |
92 | ||
93 | return $response->write($this->render('tag.' . $type)); | |
94 | } | |
95 | ||
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 | */ | |
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 | } | |
123 | } |