11776c5c5Sdh-tools<?php 21776c5c5Sdh-tools 31776c5c5Sdh-toolsuse dokuwiki\Extension\ActionPlugin; 41776c5c5Sdh-toolsuse dokuwiki\Extension\Event; 51776c5c5Sdh-toolsuse dokuwiki\Extension\EventHandler; 61776c5c5Sdh-tools 71776c5c5Sdh-tools/** 81776c5c5Sdh-tools * Action component for globally loading the active Fontello stylesheet. 91776c5c5Sdh-tools */ 101776c5c5Sdh-toolsclass action_plugin_fontello extends ActionPlugin 111776c5c5Sdh-tools{ 121776c5c5Sdh-tools /** 131776c5c5Sdh-tools * @param EventHandler $controller 141776c5c5Sdh-tools * @return void 151776c5c5Sdh-tools */ 161776c5c5Sdh-tools public function register(EventHandler $controller) 171776c5c5Sdh-tools { 181776c5c5Sdh-tools $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'handleJsInfo'); 191776c5c5Sdh-tools $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'handleHeader'); 201776c5c5Sdh-tools $controller->register_hook('RENDERER_CONTENT_POSTPROCESS', 'BEFORE', $this, 'handleRendererPostprocess'); 211776c5c5Sdh-tools $controller->register_hook('TPL_TOC_RENDER', 'BEFORE', $this, 'handleToc'); 221776c5c5Sdh-tools $controller->register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'handleContentDisplay'); 231776c5c5Sdh-tools $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'handleToolbar'); 241776c5c5Sdh-tools $controller->register_hook('JS_CACHE_USE', 'BEFORE', $this, 'handleJsCache'); 251776c5c5Sdh-tools } 261776c5c5Sdh-tools 271776c5c5Sdh-tools /** 281776c5c5Sdh-tools * Provide active icon metadata to plugin JavaScript before JSINFO is emitted. 291776c5c5Sdh-tools * 301776c5c5Sdh-tools * @return void 311776c5c5Sdh-tools */ 321776c5c5Sdh-tools public function handleJsInfo() 331776c5c5Sdh-tools { 341776c5c5Sdh-tools global $JSINFO; 351776c5c5Sdh-tools 361776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 371776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 381776c5c5Sdh-tools if ($helper === null || !$helper->hasActivePackage()) return; 391776c5c5Sdh-tools 401776c5c5Sdh-tools $package = $helper->getPackageInfo(); 411776c5c5Sdh-tools if ($package === null) return; 421776c5c5Sdh-tools 431776c5c5Sdh-tools $icons = []; 441776c5c5Sdh-tools foreach ($package['icons'] as $icon) { 451776c5c5Sdh-tools if (!isset($icon['name'], $icon['class'])) continue; 461776c5c5Sdh-tools $icons[$icon['name']] = $icon['class']; 471776c5c5Sdh-tools } 481776c5c5Sdh-tools 491776c5c5Sdh-tools $JSINFO['plugin_fontello'] = [ 501776c5c5Sdh-tools 'icons' => $icons, 511776c5c5Sdh-tools 'showInToc' => (bool) $helper->getConf('showInToc'), 521776c5c5Sdh-tools ]; 531776c5c5Sdh-tools } 541776c5c5Sdh-tools 551776c5c5Sdh-tools /** 561776c5c5Sdh-tools * Inject the active stylesheet when a package is available. 571776c5c5Sdh-tools * 581776c5c5Sdh-tools * @param Event $event 591776c5c5Sdh-tools * @param mixed $param 601776c5c5Sdh-tools * @return void 611776c5c5Sdh-tools */ 621776c5c5Sdh-tools public function handleHeader(Event &$event, $param) 631776c5c5Sdh-tools { 641776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 651776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 661776c5c5Sdh-tools if ($helper === null || !$helper->hasActivePackage()) return; 671776c5c5Sdh-tools 681776c5c5Sdh-tools foreach ($event->data['link'] as $link) { 691776c5c5Sdh-tools if (($link['href'] ?? '') === $helper->getCssUrl()) return; 701776c5c5Sdh-tools } 711776c5c5Sdh-tools 721776c5c5Sdh-tools $event->data['link'][] = [ 731776c5c5Sdh-tools 'rel' => 'stylesheet', 741776c5c5Sdh-tools 'type' => 'text/css', 751776c5c5Sdh-tools 'href' => $helper->getCssUrl(), 761776c5c5Sdh-tools ]; 771776c5c5Sdh-tools } 781776c5c5Sdh-tools 791776c5c5Sdh-tools /** 801776c5c5Sdh-tools * Remove TOC-hidden icon tokens before DokuWiki builds the TOC HTML. 811776c5c5Sdh-tools * 821776c5c5Sdh-tools * @param Event $event 831776c5c5Sdh-tools * @param mixed $param 841776c5c5Sdh-tools * @return void 851776c5c5Sdh-tools */ 861776c5c5Sdh-tools public function handleToc(Event &$event, $param) 871776c5c5Sdh-tools { 881776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 891776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 901776c5c5Sdh-tools if ($helper === null || !is_array($event->data)) return; 911776c5c5Sdh-tools 921776c5c5Sdh-tools foreach ($event->data as $index => $item) { 931776c5c5Sdh-tools if (!isset($item['title'])) continue; 941776c5c5Sdh-tools 951776c5c5Sdh-tools $event->data[$index]['title'] = $this->filterTocTitle((string) $item['title'], $helper); 961776c5c5Sdh-tools } 971776c5c5Sdh-tools } 981776c5c5Sdh-tools 991776c5c5Sdh-tools /** 1001776c5c5Sdh-tools * Render Fontello tokens in rendered XHTML headings. 1011776c5c5Sdh-tools * 1021776c5c5Sdh-tools * @param Event $event 1031776c5c5Sdh-tools * @param mixed $param 1041776c5c5Sdh-tools * @return void 1051776c5c5Sdh-tools */ 1061776c5c5Sdh-tools public function handleRendererPostprocess(Event &$event, $param) 1071776c5c5Sdh-tools { 1081776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 1091776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 1101776c5c5Sdh-tools if ( 1111776c5c5Sdh-tools $helper === null || 1121776c5c5Sdh-tools !$helper->hasActivePackage() || 1131776c5c5Sdh-tools !is_array($event->data) || 1141776c5c5Sdh-tools ($event->data[0] ?? '') !== 'xhtml' || 1151776c5c5Sdh-tools !isset($event->data[1]) || 1161776c5c5Sdh-tools !is_string($event->data[1]) 1171776c5c5Sdh-tools ) { 1181776c5c5Sdh-tools return; 1191776c5c5Sdh-tools } 1201776c5c5Sdh-tools 1211776c5c5Sdh-tools $event->data[1] = $this->replaceHeadingIconTokens($event->data[1], $helper); 1221776c5c5Sdh-tools $event->data[1] = $this->replaceLinkIconTokens($event->data[1], $helper); 1231776c5c5Sdh-tools $event->data[1] = $this->replaceCatlistIconTokens($event->data[1], $helper); 1241776c5c5Sdh-tools } 1251776c5c5Sdh-tools 1261776c5c5Sdh-tools /** 1271776c5c5Sdh-tools * Render Fontello tokens in visible TOC links. 1281776c5c5Sdh-tools * 1291776c5c5Sdh-tools * @param Event $event 1301776c5c5Sdh-tools * @param mixed $param 1311776c5c5Sdh-tools * @return void 1321776c5c5Sdh-tools */ 1331776c5c5Sdh-tools public function handleContentDisplay(Event &$event, $param) 1341776c5c5Sdh-tools { 1351776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 1361776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 1371776c5c5Sdh-tools if ($helper === null || !$helper->hasActivePackage() || !is_string($event->data)) return; 1381776c5c5Sdh-tools 1391776c5c5Sdh-tools $event->data = preg_replace_callback( 1401776c5c5Sdh-tools '/(<!-- TOC START -->.*?<!-- TOC END -->)/s', 1411776c5c5Sdh-tools function ($match) use ($helper) { 1421776c5c5Sdh-tools return $this->replaceEscapedIconTokens($match[1], $helper, false); 1431776c5c5Sdh-tools }, 1441776c5c5Sdh-tools $event->data 1451776c5c5Sdh-tools ); 1461776c5c5Sdh-tools } 1471776c5c5Sdh-tools 1481776c5c5Sdh-tools /** 1491776c5c5Sdh-tools * Add a Fontello picker button to the editor toolbar. 1501776c5c5Sdh-tools * 1511776c5c5Sdh-tools * @param Event $event 1521776c5c5Sdh-tools * @param mixed $param 1531776c5c5Sdh-tools * @return void 1541776c5c5Sdh-tools */ 1551776c5c5Sdh-tools public function handleToolbar(Event &$event, $param) 1561776c5c5Sdh-tools { 1571776c5c5Sdh-tools /** @var helper_plugin_fontello $helper */ 1581776c5c5Sdh-tools $helper = $this->loadHelper('fontello', false); 1591776c5c5Sdh-tools if ($helper === null || !$helper->hasActivePackage()) return; 1601776c5c5Sdh-tools 1611776c5c5Sdh-tools $icons = $helper->getActiveIcons(); 1621776c5c5Sdh-tools if ($icons === []) return; 1631776c5c5Sdh-tools 1641776c5c5Sdh-tools $button = [ 1651776c5c5Sdh-tools 'type' => 'fontello', 1661776c5c5Sdh-tools 'title' => $this->getLang('toolbar_icons'), 1671776c5c5Sdh-tools 'icon' => DOKU_BASE . 'lib/plugins/fontello/images/toolbar/fontello.svg', 1681776c5c5Sdh-tools 'class' => 'pk_fontello', 1691776c5c5Sdh-tools 'list' => array_map(static function ($icon) { 1701776c5c5Sdh-tools return [ 1711776c5c5Sdh-tools 'name' => $icon['name'], 1721776c5c5Sdh-tools 'class' => $icon['class'], 1731776c5c5Sdh-tools 'insert' => '<icon:' . $icon['name'] . '>', 1741776c5c5Sdh-tools ]; 1751776c5c5Sdh-tools }, $icons), 1761776c5c5Sdh-tools 'block' => false, 1771776c5c5Sdh-tools ]; 1781776c5c5Sdh-tools 1791776c5c5Sdh-tools $insertAt = count($event->data); 1801776c5c5Sdh-tools foreach ($event->data as $index => $item) { 1811776c5c5Sdh-tools if (($item['type'] ?? '') === 'picker' && ($item['icobase'] ?? '') === 'smileys') { 1821776c5c5Sdh-tools $insertAt = $index + 1; 1831776c5c5Sdh-tools break; 1841776c5c5Sdh-tools } 1851776c5c5Sdh-tools } 1861776c5c5Sdh-tools 1871776c5c5Sdh-tools array_splice($event->data, $insertAt, 0, [$button]); 1881776c5c5Sdh-tools } 1891776c5c5Sdh-tools 1901776c5c5Sdh-tools /** 1911776c5c5Sdh-tools * Make dynamic toolbar data sensitive to active package changes. 1921776c5c5Sdh-tools * 1931776c5c5Sdh-tools * DokuWiki caches the generated toolbar JavaScript. The toolbar button list 1941776c5c5Sdh-tools * depends on runtime JSON files, so they need to be cache dependencies. 1951776c5c5Sdh-tools * 1961776c5c5Sdh-tools * @param Event $event 1971776c5c5Sdh-tools * @param mixed $param 1981776c5c5Sdh-tools * @return void 1991776c5c5Sdh-tools */ 2001776c5c5Sdh-tools public function handleJsCache(Event &$event, $param) 2011776c5c5Sdh-tools { 2021776c5c5Sdh-tools if (!isset($event->data->depends['files']) || !is_array($event->data->depends['files'])) { 2031776c5c5Sdh-tools $event->data->depends['files'] = []; 2041776c5c5Sdh-tools } 2051776c5c5Sdh-tools 206*95357802SDaniel Hofer $files = [ 2071776c5c5Sdh-tools DOKU_PLUGIN . 'fontello/assets/active/config.json', 2081776c5c5Sdh-tools DOKU_PLUGIN . 'fontello/assets/active/enabled.json', 209*95357802SDaniel Hofer ]; 210*95357802SDaniel Hofer 211*95357802SDaniel Hofer foreach ($files as $file) { 2121776c5c5Sdh-tools if (file_exists($file)) { 2131776c5c5Sdh-tools $event->data->depends['files'][] = $file; 2141776c5c5Sdh-tools } 2151776c5c5Sdh-tools } 2161776c5c5Sdh-tools } 2171776c5c5Sdh-tools 2181776c5c5Sdh-tools /** 2191776c5c5Sdh-tools * Replace escaped icon tokens in XHTML headings. 2201776c5c5Sdh-tools * 2211776c5c5Sdh-tools * @param string $html 2221776c5c5Sdh-tools * @param helper_plugin_fontello $helper 2231776c5c5Sdh-tools * @return string 2241776c5c5Sdh-tools */ 2251776c5c5Sdh-tools protected function replaceHeadingIconTokens($html, helper_plugin_fontello $helper) 2261776c5c5Sdh-tools { 2271776c5c5Sdh-tools return preg_replace_callback( 2281776c5c5Sdh-tools '/<h([1-6])\b([^>]*)>(.*?)<\/h\1>/s', 2291776c5c5Sdh-tools function ($match) use ($helper) { 2301776c5c5Sdh-tools return '<h' . $match[1] . $match[2] . '>' . 2311776c5c5Sdh-tools $this->replaceEscapedIconTokens($match[3], $helper, true) . 2321776c5c5Sdh-tools '</h' . $match[1] . '>'; 2331776c5c5Sdh-tools }, 2341776c5c5Sdh-tools $html 2351776c5c5Sdh-tools ); 2361776c5c5Sdh-tools } 2371776c5c5Sdh-tools 2381776c5c5Sdh-tools /** 2391776c5c5Sdh-tools * Replace escaped icon tokens in rendered link labels. 2401776c5c5Sdh-tools * 2411776c5c5Sdh-tools * This covers plugins such as catlist that render page titles via the 2421776c5c5Sdh-tools * XHTML renderer's internallink() method instead of reparsing title text. 2431776c5c5Sdh-tools * 2441776c5c5Sdh-tools * @param string $html 2451776c5c5Sdh-tools * @param helper_plugin_fontello $helper 2461776c5c5Sdh-tools * @return string 2471776c5c5Sdh-tools */ 2481776c5c5Sdh-tools protected function replaceLinkIconTokens($html, helper_plugin_fontello $helper) 2491776c5c5Sdh-tools { 2501776c5c5Sdh-tools return preg_replace_callback( 2511776c5c5Sdh-tools '/<a\b([^>]*)>(.*?)<\/a>/s', 2521776c5c5Sdh-tools function ($match) use ($helper) { 2531776c5c5Sdh-tools return '<a' . $match[1] . '>' . 2541776c5c5Sdh-tools $this->replaceEscapedIconTokens($match[2], $helper, true) . 2551776c5c5Sdh-tools '</a>'; 2561776c5c5Sdh-tools }, 2571776c5c5Sdh-tools $html 2581776c5c5Sdh-tools ); 2591776c5c5Sdh-tools } 2601776c5c5Sdh-tools 2611776c5c5Sdh-tools /** 2621776c5c5Sdh-tools * Replace escaped icon tokens in catlist labels that are not links. 2631776c5c5Sdh-tools * 2641776c5c5Sdh-tools * @param string $html 2651776c5c5Sdh-tools * @param helper_plugin_fontello $helper 2661776c5c5Sdh-tools * @return string 2671776c5c5Sdh-tools */ 2681776c5c5Sdh-tools protected function replaceCatlistIconTokens($html, helper_plugin_fontello $helper) 2691776c5c5Sdh-tools { 270*95357802SDaniel Hofer $pattern = '/<(?P<tag>h[1-5]|strong|span|li)\b' . 271*95357802SDaniel Hofer '(?P<attrs>[^>]*\bclass="[^"]*\bcatlist-(?:head|nshead|page)\b[^"]*"[^>]*)>' . 272*95357802SDaniel Hofer '(?P<body>.*?)<\/(?P=tag)>/s'; 273*95357802SDaniel Hofer 2741776c5c5Sdh-tools return preg_replace_callback( 275*95357802SDaniel Hofer $pattern, 2761776c5c5Sdh-tools function ($match) use ($helper) { 2771776c5c5Sdh-tools return '<' . $match['tag'] . $match['attrs'] . '>' . 2781776c5c5Sdh-tools $this->replaceEscapedIconTokens($match['body'], $helper, true) . 2791776c5c5Sdh-tools '</' . $match['tag'] . '>'; 2801776c5c5Sdh-tools }, 2811776c5c5Sdh-tools $html 2821776c5c5Sdh-tools ); 2831776c5c5Sdh-tools } 2841776c5c5Sdh-tools 2851776c5c5Sdh-tools /** 2861776c5c5Sdh-tools * Keep only TOC-visible tokens in a title. 2871776c5c5Sdh-tools * 2881776c5c5Sdh-tools * @param string $title 2891776c5c5Sdh-tools * @param helper_plugin_fontello $helper 2901776c5c5Sdh-tools * @return string 2911776c5c5Sdh-tools */ 2921776c5c5Sdh-tools protected function filterTocTitle($title, helper_plugin_fontello $helper) 2931776c5c5Sdh-tools { 2941776c5c5Sdh-tools $title = preg_replace_callback( 2951776c5c5Sdh-tools '/<icon:[A-Za-z0-9_-]+(?:\|(?:toc|notoc))?>/', 2961776c5c5Sdh-tools function ($match) use ($helper) { 2971776c5c5Sdh-tools $token = $helper->parseIconToken($match[0]); 2981776c5c5Sdh-tools if ($token === null || !$helper->iconTokenShowsInToc($token)) return ''; 2991776c5c5Sdh-tools if ($helper->renderIconXhtml($token['name']) === null) return $match[0]; 3001776c5c5Sdh-tools return $match[0]; 3011776c5c5Sdh-tools }, 3021776c5c5Sdh-tools $title 3031776c5c5Sdh-tools ); 3041776c5c5Sdh-tools 3051776c5c5Sdh-tools return trim(preg_replace('/[ \t]{2,}/', ' ', $title)); 3061776c5c5Sdh-tools } 3071776c5c5Sdh-tools 3081776c5c5Sdh-tools /** 3091776c5c5Sdh-tools * Replace escaped icon tokens with local icon HTML. 3101776c5c5Sdh-tools * 3111776c5c5Sdh-tools * @param string $html 3121776c5c5Sdh-tools * @param helper_plugin_fontello $helper 3131776c5c5Sdh-tools * @param bool $ignoreTocFlag 3141776c5c5Sdh-tools * @return string 3151776c5c5Sdh-tools */ 3161776c5c5Sdh-tools protected function replaceEscapedIconTokens($html, helper_plugin_fontello $helper, $ignoreTocFlag) 3171776c5c5Sdh-tools { 3181776c5c5Sdh-tools return preg_replace_callback( 3191776c5c5Sdh-tools '/<icon:([A-Za-z0-9_-]+)(?:\|(toc|notoc))?>/', 3201776c5c5Sdh-tools function ($match) use ($helper, $ignoreTocFlag) { 3211776c5c5Sdh-tools $raw = '<icon:' . $match[1] . (isset($match[2]) && $match[2] !== '' ? '|' . $match[2] : '') . '>'; 3221776c5c5Sdh-tools $token = $helper->parseIconToken($raw); 3231776c5c5Sdh-tools if ($token === null) return $match[0]; 3241776c5c5Sdh-tools if (!$ignoreTocFlag && !$helper->iconTokenShowsInToc($token)) return ''; 3251776c5c5Sdh-tools 3261776c5c5Sdh-tools return $helper->renderIconXhtml($token['name']) ?: $match[0]; 3271776c5c5Sdh-tools }, 3281776c5c5Sdh-tools $html 3291776c5c5Sdh-tools ); 3301776c5c5Sdh-tools } 3311776c5c5Sdh-tools} 332