xref: /plugin/pagesicon/helper.php (revision 74a9e7636f2e172c4b5990b529a0ab322717c972)
1<?php
2if (!defined('DOKU_INC')) die();
3
4class helper_plugin_pagesicon extends DokuWiki_Plugin
5{
6    private function getBundledDefaultImagePath(): string
7    {
8        return DOKU_INC . 'lib/plugins/pagesicon/images/default_image.png';
9    }
10
11    private function getBundledDefaultImageUrl(): string
12    {
13        $path = $this->getBundledDefaultImagePath();
14        if (!@file_exists($path)) return '';
15
16        $base = rtrim((string)DOKU_BASE, '/');
17        $url = $base . '/lib/plugins/pagesicon/images/default_image.png';
18        $mtime = @filemtime($path);
19        return $this->appendVersionToUrl($url, $mtime ? (int)$mtime : 0);
20    }
21
22    private function getConfiguredDefaultImageMediaID()
23    {
24        $mediaID = cleanID((string)$this->getConf('default_image'));
25        if ($mediaID === '') return false;
26        if (!@file_exists(mediaFN($mediaID))) return false;
27        return $mediaID;
28    }
29
30    private function getMediaMTime(string $mediaID): int
31    {
32        $mediaID = cleanID($mediaID);
33        if ($mediaID === '') return 0;
34        $file = mediaFN($mediaID);
35        if (!@file_exists($file)) return 0;
36        $mtime = @filemtime($file);
37        return $mtime ? (int)$mtime : 0;
38    }
39
40    private function appendVersionToUrl(string $url, int $mtime): string
41    {
42        if ($url === '' || $mtime <= 0) return $url;
43        $sep = strpos($url, '?') === false ? '?' : '&';
44        return $url . $sep . 'pi_ts=' . $mtime;
45    }
46
47    public function notifyIconUpdated(string $targetPage, string $action = 'update', string $mediaID = ''): void
48    {
49        global $conf;
50
51        @io_saveFile($conf['cachedir'] . '/purgefile', time());
52
53        $data = [
54            'target_page' => cleanID($targetPage),
55            'action' => $action,
56            'media_id' => cleanID($mediaID),
57        ];
58        \dokuwiki\Extension\Event::createAndTrigger('PLUGIN_PAGESICON_UPDATED', $data);
59    }
60
61    private function buildConfiguredCandidatesFromRaw(string $raw, string $namespace, string $pageID): array
62    {
63        $configured = [];
64        $entries = array_filter(array_map('trim', explode(';', $raw)));
65
66        foreach ($entries as $entry) {
67            $name = str_replace('~pagename~', $pageID, $entry);
68            if ($name === '') continue;
69
70            if (strpos($name, ':') === false && $namespace !== '') {
71                $configured[] = $namespace . ':' . $name;
72            } else {
73                $configured[] = ltrim($name, ':');
74            }
75        }
76
77        return array_values(array_unique($configured));
78    }
79
80    private function buildConfiguredCandidates(string $namespace, string $pageID, string $sizeMode): array
81    {
82        $bigRaw = trim((string)$this->getConf('icon_name'));
83        $smallRaw = trim((string)$this->getConf('icon_thumbnail_name'));
84
85        $big = $this->buildConfiguredCandidatesFromRaw($bigRaw, $namespace, $pageID);
86        $small = $this->buildConfiguredCandidatesFromRaw($smallRaw, $namespace, $pageID);
87
88        if ($sizeMode === 'big') return $big;
89        if ($sizeMode === 'small') return $small;
90        if ($sizeMode === 'smallorbig') return array_values(array_unique(array_merge($small, $big)));
91
92        // Default: bigorsmall
93        return array_values(array_unique(array_merge($big, $small)));
94    }
95
96    private function normalizeSizeMode(string $size): string
97    {
98        $size = strtolower(trim($size));
99        $allowed = ['big', 'small', 'bigorsmall', 'smallorbig'];
100        if (in_array($size, $allowed, true)) return $size;
101        return 'bigorsmall';
102    }
103
104    private function getExtensions(): array
105    {
106        $raw = trim((string)$this->getConf('extensions'));
107        if ($raw === '') return ['svg', 'png', 'jpg', 'jpeg'];
108
109        $extensions = array_values(array_unique(array_filter(array_map(function ($ext) {
110            return strtolower(ltrim(trim((string)$ext), '.'));
111        }, explode(';', $raw)))));
112
113        return $extensions ?: ['svg', 'png', 'jpg', 'jpeg'];
114    }
115
116    private function hasKnownExtension(string $name, array $extensions): bool
117    {
118        $fileExt = strtolower((string)pathinfo($name, PATHINFO_EXTENSION));
119        return $fileExt !== '' && in_array($fileExt, $extensions, true);
120    }
121
122    public function getPageIconId(
123        string $namespace,
124        string $pageID,
125        string $size = 'bigorsmall'
126    )
127    {
128        $sizeMode = $this->normalizeSizeMode($size);
129        $extensions = $this->getExtensions();
130        $namespace = $namespace ?: '';
131        $pageBase = $namespace ? ($namespace . ':' . $pageID) : $pageID;
132        $nsBase = $namespace ? ($namespace . ':') : '';
133
134        $genericBig = [
135            $pageBase,
136            $pageBase . ':logo',
137            $nsBase . 'logo',
138        ];
139        $genericSmall = [
140            $pageBase . ':thumbnail',
141            $nsBase . 'thumbnail',
142        ];
143
144        if ($sizeMode === 'big') {
145            $generic = $genericBig;
146        } elseif ($sizeMode === 'small') {
147            $generic = $genericSmall;
148        } elseif ($sizeMode === 'smallorbig') {
149            $generic = array_merge($genericSmall, $genericBig);
150        } else {
151            $generic = array_merge($genericBig, $genericSmall);
152        }
153
154        $imageNames = array_merge($this->buildConfiguredCandidates($namespace, $pageID, $sizeMode), $generic);
155
156        foreach ($imageNames as $name) {
157            if ($this->hasKnownExtension($name, $extensions)) {
158                if (@file_exists(mediaFN($name))) return $name;
159                continue;
160            }
161
162            foreach ($extensions as $ext) {
163                $path = $name . '.' . $ext;
164                if (@file_exists(mediaFN($path))) return $path;
165            }
166        }
167
168        return false;
169    }
170
171    // Legacy alias kept for backward compatibility.
172    public function getPageImage(
173        string $namespace,
174        string $pageID,
175        string $size = 'bigorsmall',
176        bool $withDefault = false
177    ) {
178        return $this->getPageIconId($namespace, $pageID, $size);
179    }
180
181    public function getUploadIconPage(string $targetPage = '')
182    {
183        global $ID;
184
185        $targetPage = cleanID($targetPage);
186        if ($targetPage === '') {
187            $targetPage = cleanID(getNS((string)$ID));
188        }
189        if ($targetPage === '') {
190            $targetPage = cleanID((string)$ID);
191        }
192        if ($targetPage === '') return null;
193
194        if (auth_quickaclcheck($targetPage) < AUTH_UPLOAD) {
195            return null;
196        }
197
198        return wl($targetPage, ['do' => 'pagesicon']);
199    }
200
201    public function getMediaIconId(string $mediaID, string $size = 'bigorsmall')
202    {
203        $mediaID = cleanID($mediaID);
204        if ($mediaID === '') return false;
205
206        $namespace = getNS($mediaID);
207        $filename = noNS($mediaID);
208        $base = (string)pathinfo($filename, PATHINFO_FILENAME);
209        $pageID = cleanID($base);
210        if ($pageID === '') return false;
211
212        return $this->getPageIconId($namespace, $pageID, $size);
213    }
214
215    // Legacy alias kept for backward compatibility.
216    public function getMediaImage(string $mediaID, string $size = 'bigorsmall', bool $withDefault = false)
217    {
218        return $this->getMediaIconId($mediaID, $size);
219    }
220
221    public function getDefaultIconUrl(array $params = ['width' => 55], ?int &$mtime = null)
222    {
223        $mediaID = $this->getConfiguredDefaultImageMediaID();
224        if ($mediaID) {
225            $mtime = $this->getMediaMTime((string)$mediaID);
226            $url = (string)ml((string)$mediaID, $params);
227            if ($url === '') return false;
228            return $this->appendVersionToUrl($url, $mtime);
229        }
230
231        $mtime = 0;
232        $bundled = $this->getBundledDefaultImageUrl();
233        if ($bundled !== '') return $bundled;
234
235        return false;
236    }
237
238    // Legacy alias kept for backward compatibility.
239    public function getDefaultImageIcon(array $params = ['width' => 55], ?int &$mtime = null)
240    {
241        return $this->getDefaultIconUrl($params, $mtime);
242    }
243
244    public function getPageIconUrl(
245        string $namespace,
246        string $pageID,
247        string $size = 'bigorsmall',
248        array $params = ['width' => 55],
249        ?int &$mtime = null,
250        bool $withDefault = false
251    ) {
252        $mediaID = $this->getPageIconId($namespace, $pageID, $size);
253        if (!$mediaID) {
254            if ($withDefault) {
255                return $this->getDefaultIconUrl($params, $mtime);
256            }
257            $mtime = 0;
258            return false;
259        }
260
261        $mtime = $this->getMediaMTime((string)$mediaID);
262        $url = (string)ml((string)$mediaID, $params);
263        if ($url === '') return false;
264        return $this->appendVersionToUrl($url, $mtime);
265    }
266
267    // Legacy alias kept for backward compatibility.
268    public function getImageIcon(
269        string $namespace,
270        string $pageID,
271        string $size = 'bigorsmall',
272        array $params = ['width' => 55],
273        ?int &$mtime = null,
274        bool $withDefault = false
275    ) {
276        return $this->getPageIconUrl($namespace, $pageID, $size, $params, $mtime, $withDefault);
277    }
278
279    public function getMediaIconUrl(
280        string $mediaID,
281        string $size = 'bigorsmall',
282        array $params = ['width' => 55],
283        ?int &$mtime = null,
284        bool $withDefault = false
285    ) {
286        $iconMediaID = $this->getMediaIconId($mediaID, $size);
287        if (!$iconMediaID) {
288            if ($withDefault) {
289                return $this->getDefaultIconUrl($params, $mtime);
290            }
291            $mtime = 0;
292            return false;
293        }
294
295        $mtime = $this->getMediaMTime((string)$iconMediaID);
296        $url = (string)ml((string)$iconMediaID, $params);
297        if ($url === '') return false;
298        return $this->appendVersionToUrl($url, $mtime);
299    }
300
301    // Legacy alias kept for backward compatibility.
302    public function getMediaIcon(
303        string $mediaID,
304        string $size = 'bigorsmall',
305        array $params = ['width' => 55],
306        ?int &$mtime = null,
307        bool $withDefault = false
308    ) {
309        return $this->getMediaIconUrl($mediaID, $size, $params, $mtime, $withDefault);
310    }
311
312    public function getUploadMediaIconPage(string $mediaID = '')
313    {
314        $mediaID = cleanID($mediaID);
315        if ($mediaID === '') return null;
316
317        $namespace = getNS($mediaID);
318        $filename = noNS($mediaID);
319        $base = (string)pathinfo($filename, PATHINFO_FILENAME);
320        $targetPage = cleanID($namespace !== '' ? ($namespace . ':' . $base) : $base);
321        if ($targetPage === '') return null;
322
323        return $this->getUploadIconPage($targetPage);
324    }
325}
326