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