xref: /plugin/pagesicon/helper.php (revision da933f899c3fc9c630c3a0106c319fb606ed9385)
1<?php
2if (!defined('DOKU_INC')) die();
3
4class helper_plugin_pagesicon extends DokuWiki_Plugin
5{
6    private function getMediaMTime(string $mediaID): int
7    {
8        $mediaID = cleanID($mediaID);
9        if ($mediaID === '') return 0;
10        $file = mediaFN($mediaID);
11        if (!@file_exists($file)) return 0;
12        $mtime = @filemtime($file);
13        return $mtime ? (int)$mtime : 0;
14    }
15
16    private function appendVersionToUrl(string $url, int $mtime): string
17    {
18        if ($url === '' || $mtime <= 0) return $url;
19        $sep = strpos($url, '?') === false ? '?' : '&';
20        return $url . $sep . 'pi_ts=' . $mtime;
21    }
22
23    public function notifyIconUpdated(string $targetPage, string $action = 'update', string $mediaID = ''): void
24    {
25        global $conf;
26
27        @io_saveFile($conf['cachedir'] . '/purgefile', time());
28
29        $data = [
30            'target_page' => cleanID($targetPage),
31            'action' => $action,
32            'media_id' => cleanID($mediaID),
33        ];
34        \dokuwiki\Extension\Event::createAndTrigger('PLUGIN_PAGESICON_UPDATED', $data);
35    }
36
37    private function buildConfiguredCandidatesFromRaw(string $raw, string $namespace, string $pageID): array
38    {
39        $configured = [];
40        $entries = array_filter(array_map('trim', explode(';', $raw)));
41
42        foreach ($entries as $entry) {
43            $name = str_replace('~pagename~', $pageID, $entry);
44            if ($name === '') continue;
45
46            if (strpos($name, ':') === false && $namespace !== '') {
47                $configured[] = $namespace . ':' . $name;
48            } else {
49                $configured[] = ltrim($name, ':');
50            }
51        }
52
53        return array_values(array_unique($configured));
54    }
55
56    private function buildConfiguredCandidates(string $namespace, string $pageID, string $sizeMode): array
57    {
58        $bigRaw = trim((string)$this->getConf('icon_name'));
59        $smallRaw = trim((string)$this->getConf('icon_thumbnail_name'));
60
61        $big = $this->buildConfiguredCandidatesFromRaw($bigRaw, $namespace, $pageID);
62        $small = $this->buildConfiguredCandidatesFromRaw($smallRaw, $namespace, $pageID);
63
64        if ($sizeMode === 'big') return $big;
65        if ($sizeMode === 'small') return $small;
66        if ($sizeMode === 'smallorbig') return array_values(array_unique(array_merge($small, $big)));
67
68        // Default: bigorsmall
69        return array_values(array_unique(array_merge($big, $small)));
70    }
71
72    private function normalizeSizeMode(string $size): string
73    {
74        $size = strtolower(trim($size));
75        $allowed = ['big', 'small', 'bigorsmall', 'smallorbig'];
76        if (in_array($size, $allowed, true)) return $size;
77        return 'bigorsmall';
78    }
79
80    private function getExtensions(): array
81    {
82        $raw = trim((string)$this->getConf('extensions'));
83        if ($raw === '') return ['svg', 'png', 'jpg', 'jpeg'];
84
85        $extensions = array_values(array_unique(array_filter(array_map(function ($ext) {
86            return strtolower(ltrim(trim((string)$ext), '.'));
87        }, explode(';', $raw)))));
88
89        return $extensions ?: ['svg', 'png', 'jpg', 'jpeg'];
90    }
91
92    private function hasKnownExtension(string $name, array $extensions): bool
93    {
94        $fileExt = strtolower((string)pathinfo($name, PATHINFO_EXTENSION));
95        return $fileExt !== '' && in_array($fileExt, $extensions, true);
96    }
97
98    public function getPageImage(string $namespace, string $pageID, string $size = 'bigorsmall')
99    {
100        $sizeMode = $this->normalizeSizeMode($size);
101        $extensions = $this->getExtensions();
102        $namespace = $namespace ?: '';
103        $pageBase = $namespace ? ($namespace . ':' . $pageID) : $pageID;
104        $nsBase = $namespace ? ($namespace . ':') : '';
105
106        $genericBig = [
107            $pageBase,
108            $pageBase . ':logo',
109            $nsBase . 'logo',
110        ];
111        $genericSmall = [
112            $pageBase . ':thumbnail',
113            $nsBase . 'thumbnail',
114        ];
115
116        if ($sizeMode === 'big') {
117            $generic = $genericBig;
118        } elseif ($sizeMode === 'small') {
119            $generic = $genericSmall;
120        } elseif ($sizeMode === 'smallorbig') {
121            $generic = array_merge($genericSmall, $genericBig);
122        } else {
123            $generic = array_merge($genericBig, $genericSmall);
124        }
125
126        $imageNames = array_merge($this->buildConfiguredCandidates($namespace, $pageID, $sizeMode), $generic);
127
128        foreach ($imageNames as $name) {
129            if ($this->hasKnownExtension($name, $extensions)) {
130                if (@file_exists(mediaFN($name))) return $name;
131                continue;
132            }
133
134            foreach ($extensions as $ext) {
135                $path = $name . '.' . $ext;
136                if (@file_exists(mediaFN($path))) return $path;
137            }
138        }
139
140        return false;
141    }
142
143    public function getUploadIconPage(string $targetPage = '')
144    {
145        global $ID;
146
147        $targetPage = cleanID($targetPage);
148        if ($targetPage === '') {
149            $targetPage = cleanID(getNS((string)$ID));
150        }
151        if ($targetPage === '') {
152            $targetPage = cleanID((string)$ID);
153        }
154        if ($targetPage === '') return null;
155
156        if (auth_quickaclcheck($targetPage) < AUTH_UPLOAD) {
157            return null;
158        }
159
160        return wl($targetPage, ['do' => 'pagesicon']);
161    }
162
163    public function getMediaImage(string $mediaID, string $size = 'bigorsmall')
164    {
165        $mediaID = cleanID($mediaID);
166        if ($mediaID === '') return false;
167
168        $namespace = getNS($mediaID);
169        $filename = noNS($mediaID);
170        $base = (string)pathinfo($filename, PATHINFO_FILENAME);
171        $pageID = cleanID($base);
172        if ($pageID === '') return false;
173
174        return $this->getPageImage($namespace, $pageID, $size);
175    }
176
177    public function getImageIcon(
178        string $namespace,
179        string $pageID,
180        string $size = 'bigorsmall',
181        array $params = ['width' => 55],
182        ?int &$mtime = null
183    ) {
184        $mediaID = $this->getPageImage($namespace, $pageID, $size);
185        if (!$mediaID) {
186            $mtime = 0;
187            return false;
188        }
189
190        $mtime = $this->getMediaMTime((string)$mediaID);
191        $url = (string)ml((string)$mediaID, $params);
192        if ($url === '') return false;
193        return $this->appendVersionToUrl($url, $mtime);
194    }
195
196    public function getMediaIcon(
197        string $mediaID,
198        string $size = 'bigorsmall',
199        array $params = ['width' => 55],
200        ?int &$mtime = null
201    ) {
202        $iconMediaID = $this->getMediaImage($mediaID, $size);
203        if (!$iconMediaID) {
204            $mtime = 0;
205            return false;
206        }
207
208        $mtime = $this->getMediaMTime((string)$iconMediaID);
209        $url = (string)ml((string)$iconMediaID, $params);
210        if ($url === '') return false;
211        return $this->appendVersionToUrl($url, $mtime);
212    }
213
214    public function getUploadMediaIconPage(string $mediaID = '')
215    {
216        $mediaID = cleanID($mediaID);
217        if ($mediaID === '') return null;
218
219        $namespace = getNS($mediaID);
220        $filename = noNS($mediaID);
221        $base = (string)pathinfo($filename, PATHINFO_FILENAME);
222        $targetPage = cleanID($namespace !== '' ? ($namespace . ':' . $base) : $base);
223        if ($targetPage === '') return null;
224
225        return $this->getUploadIconPage($targetPage);
226    }
227}
228