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