16bfd3f23SEli Fenton<?php 26bfd3f23SEli Fentonif(!defined('DOKU_INC')) die(); 3*e3504fb9SZiothuse dokuwiki\File\PageResolver; 46bfd3f23SEli Fenton 56bfd3f23SEli Fenton/** 66bfd3f23SEli Fenton * Auto-Tooltip DokuWiki plugin 76bfd3f23SEli Fenton * 86bfd3f23SEli Fenton * @license MIT 96bfd3f23SEli Fenton * @author Eli Fenton 106bfd3f23SEli Fenton */ 11dd27e4fbSEli Fentonclass helper_plugin_autotooltip extends DokuWiki_Plugin { 126bfd3f23SEli Fenton private $localRenderer; 13d39942acSEli Fenton private static $metaCache = []; 146bfd3f23SEli Fenton 156bfd3f23SEli Fenton public function __construct() { 166bfd3f23SEli Fenton $this->localRenderer = new Doku_Renderer_xhtml; 176bfd3f23SEli Fenton } 186bfd3f23SEli Fenton 196bfd3f23SEli Fenton 206bfd3f23SEli Fenton /** 219e2add7bSEli Fenton * Return methods of this helper 229e2add7bSEli Fenton * 239e2add7bSEli Fenton * @return array with methods description 249e2add7bSEli Fenton */ 25e2969550SEli Fenton public function getMethods() { 269e2add7bSEli Fenton $result = array(); 279e2add7bSEli Fenton $result[] = array( 289e2add7bSEli Fenton 'name' => 'forText', 299e2add7bSEli Fenton 'desc' => 'Manually construct a tooltip', 309e2add7bSEli Fenton 'params' => array( 319e2add7bSEli Fenton 'content' => 'string', 329e2add7bSEli Fenton 'tooltip' => 'string', 339e2add7bSEli Fenton 'title (optional)' => 'string', 349e2add7bSEli Fenton 'preTitle (optional)' => 'string', 359e2add7bSEli Fenton 'classes (optional)' => 'string', 36b9ce7d66SEli Fenton 'textClasses (optional)' => 'string', 379e2add7bSEli Fenton ), 389e2add7bSEli Fenton 'return' => array('result' => 'string') 399e2add7bSEli Fenton ); 409e2add7bSEli Fenton $result[] = array( 419e2add7bSEli Fenton 'name' => 'forWikilink', 429e2add7bSEli Fenton 'desc' => 'Generate a tooltip from a wikilink', 439e2add7bSEli Fenton 'params' => array( 449e2add7bSEli Fenton 'id' => 'string', 459e2add7bSEli Fenton 'content (optional)' => 'string', 469e2add7bSEli Fenton 'preTitle (optional)' => 'string', 479e2add7bSEli Fenton 'classes (optional)' => 'string', 48b9ce7d66SEli Fenton 'textClasses (optional)' => 'string', 499e2add7bSEli Fenton ), 509e2add7bSEli Fenton 'return' => array('result' => 'string') 519e2add7bSEli Fenton ); 529e2add7bSEli Fenton return $result; 539e2add7bSEli Fenton } 549e2add7bSEli Fenton 559e2add7bSEli Fenton 569e2add7bSEli Fenton /** 576bfd3f23SEli Fenton * Return a simple tooltip. 586bfd3f23SEli Fenton * 5907c401fcSEli Fenton * @param string $content - The on-page content. May contain newlines. 60948a1374SEli Fenton * @param string $tooltip - The tooltip content. Newlines will be rendered as line breaks. 61948a1374SEli Fenton * @param string $title - The title inside the tooltip. 62948a1374SEli Fenton * @param string $preTitle - Text to display before the title. Newlines will be rendered as line breaks. 636bfd3f23SEli Fenton * @param string $classes - CSS classes to add to this tooltip. 64b9ce7d66SEli Fenton * @param string $textClasses - CSS classes to add to the linked text. 656bfd3f23SEli Fenton * @return string 666bfd3f23SEli Fenton */ 67e2969550SEli Fenton public function forText($content, $tooltip, $title='', $preTitle = '', $classes = '', $textClasses = '') { 68b7bccb09SEli Fenton if (empty($classes)) { 69be213c94SEli Fenton $classes = $this->getConf('style'); 70be213c94SEli Fenton } 71b7bccb09SEli Fenton if (empty($classes)) { 72be213c94SEli Fenton $classes = 'default'; 73be213c94SEli Fenton } 744cbd2578SEli Fenton $delay = $this->getConf('delay') ?: 0; 75be213c94SEli Fenton 76be213c94SEli Fenton // Sanitize 77be213c94SEli Fenton $classes = htmlspecialchars($classes); 78be213c94SEli Fenton // Add the plugin prefix to all classes. 79be213c94SEli Fenton $classes = preg_replace('/(\w+)/', 'plugin-autotooltip__$1', $classes); 80be213c94SEli Fenton 81be213c94SEli Fenton $partCount = (empty($title) ? 0 : 1) + (empty($preTitle) ? 0 : 1) + (empty($tooltip) ? 0 : 1); 82be213c94SEli Fenton if ($partCount > 1 || strchr($tooltip, "\n") !== FALSE || strlen($tooltip) > 40) { 83be213c94SEli Fenton $classes .= ' plugin-autotooltip_big'; 846bfd3f23SEli Fenton } 856bfd3f23SEli Fenton 86b9ce7d66SEli Fenton if (empty($textClasses)) { 87b9ce7d66SEli Fenton $textClasses = 'plugin-autotooltip_linked'; 88969d3afcSEli Fenton if (strstr($content, '<a ') === FALSE) { 89b9ce7d66SEli Fenton $textClasses .= ' plugin-autotooltip_simple'; 90969d3afcSEli Fenton } 91969d3afcSEli Fenton } 926bfd3f23SEli Fenton 93e4338dabSEli Fenton $contentParts = []; 94e4338dabSEli Fenton if (!empty($preTitle)) { 95948a1374SEli Fenton $contentParts[] = $this->_formatTT($preTitle); 96e4338dabSEli Fenton } 97e4338dabSEli Fenton if (!empty($title)) { 98e4338dabSEli Fenton $contentParts[] = '<span class="plugin-autotooltip-title">' . $title . '</span>'; 99e4338dabSEli Fenton } 100e4338dabSEli Fenton if (!empty($tooltip)) { 101948a1374SEli Fenton $contentParts[] = $this->_formatTT($tooltip); 102e4338dabSEli Fenton } 103e4338dabSEli Fenton 104d20b44e1SEli Fenton return '<span class="' . $textClasses . '" onmouseover="autotooltip.show(event)" onmouseout="autotooltip.hide()" data-delay="' . $delay . '">' . 1056bfd3f23SEli Fenton $content . 10607c401fcSEli Fenton '<span class="plugin-autotooltip-hidden-classes">' . $classes . '</span>' . 1076e7b98e9SEli Fenton '<!-- googleoff: all -->' . 108e4338dabSEli Fenton '<span class="plugin-autotooltip-hidden-tip">' . 109e4338dabSEli Fenton implode('<br><br>', $contentParts) . 110e4338dabSEli Fenton '</span>' . 1116e7b98e9SEli Fenton '<!-- googleon: all -->' . 11207c401fcSEli Fenton '</span>'; 1136bfd3f23SEli Fenton } 1146bfd3f23SEli Fenton 1156bfd3f23SEli Fenton 1166bfd3f23SEli Fenton /** 1176bfd3f23SEli Fenton * Render a tooltip, with the title and abstract of a page. 1186bfd3f23SEli Fenton * 1196bfd3f23SEli Fenton * @param string $id - A page id. 120948a1374SEli Fenton * @param string $content - The on-page content. Newlines will be rendered as line breaks. Omit to use the page's title. 121948a1374SEli Fenton * @param string $preTitle - Text to display before the title in the tooltip. Newlines will be rendered as line breaks. 1226bfd3f23SEli Fenton * @param string $classes - CSS classes to add to this tooltip. 123b9ce7d66SEli Fenton * @param string $textClasses - CSS classes to add to the linked text. 1246bfd3f23SEli Fenton * @return string 1256bfd3f23SEli Fenton */ 126e2969550SEli Fenton public function forWikilink($id, $content = null, $preTitle = '', $classes = '', $textClasses = '') { 127d39942acSEli Fenton global $ID; 128d39942acSEli Fenton $id = resolve_id(getNS($ID), $id, false); 129d39942acSEli Fenton 130d39942acSEli Fenton $meta = self::read_meta_fast($id); 131d39942acSEli Fenton $title = $meta['title']; 1326bfd3f23SEli Fenton 133812ebc9aSEli Fenton $link = $this->localRenderer->internallink($id, $content ?: $title, null, true); 1346bfd3f23SEli Fenton 135d39942acSEli Fenton if (page_exists(preg_replace('/\#.*$/', '', $id))) { 1364cbd2578SEli Fenton $link = $this->stripNativeTooltip($link); 137b9ce7d66SEli Fenton return $this->forText($link, $meta['abstract'], $title, $preTitle, $classes, $textClasses); 1386bfd3f23SEli Fenton } 1396bfd3f23SEli Fenton else { 1406bfd3f23SEli Fenton return $link; 1416bfd3f23SEli Fenton } 1426bfd3f23SEli Fenton } 1436bfd3f23SEli Fenton 1446bfd3f23SEli Fenton 1456bfd3f23SEli Fenton /** 146e2969550SEli Fenton * Is this id excluded from the plugin? 147e2969550SEli Fenton * 148e2969550SEli Fenton * @param string $id 149e2969550SEli Fenton * @return boolean 150e2969550SEli Fenton */ 151e2969550SEli Fenton public function isExcluded($id) { 152e2969550SEli Fenton $inclusions = $this->getConf('linkall_inclusions'); 153e2969550SEli Fenton $exclusions = $this->getConf('linkall_exclusions'); 154e2969550SEli Fenton return (!empty($inclusions) && !preg_match("/$inclusions/", $id)) || 155e2969550SEli Fenton (!empty($exclusions) && preg_match("/$exclusions/", $id)); 156e2969550SEli Fenton } 157e2969550SEli Fenton 158e2969550SEli Fenton 159e2969550SEli Fenton /** 1604cbd2578SEli Fenton * Strip the native title= tooltip from an anchor tag. 1614cbd2578SEli Fenton * 1624cbd2578SEli Fenton * @param string $link 1634cbd2578SEli Fenton * @return string 1644cbd2578SEli Fenton */ 165e2969550SEli Fenton public function stripNativeTooltip($link) { 1664cbd2578SEli Fenton return preg_replace('/title="[^"]*"/', '', $link); 1674cbd2578SEli Fenton } 1684cbd2578SEli Fenton 1694cbd2578SEli Fenton 1704cbd2578SEli Fenton /** 171d39942acSEli Fenton * Reads specific metadata about 10x faster than p_get_metadata. p_get_metadata only uses caching for the current 172d39942acSEli Fenton * page, and uses the very slow php serialization. However, in a wiki with infrequently accessed pages, it's 173d39942acSEli Fenton * extremely slow. 174d39942acSEli Fenton * 175d39942acSEli Fenton * @param string $id 176d39942acSEli Fenton * @return array - An array containing 'title' and 'abstract.' 177d39942acSEli Fenton */ 178d39942acSEli Fenton static function read_meta_fast($id) { 179d39942acSEli Fenton global $ID; 180*e3504fb9SZioth 181*e3504fb9SZioth $resolver = new PageResolver($ID); 182*e3504fb9SZioth $id = $resolver->resolveId(preg_replace('/\#.*$/', '', $id), null, true); 183*e3504fb9SZioth 184d39942acSEli Fenton 185d39942acSEli Fenton if (isset(self::$metaCache[$id])) { 186d39942acSEli Fenton return self::$metaCache[$id]; 187d39942acSEli Fenton } 188d39942acSEli Fenton 189b9ce7d66SEli Fenton $results = [ 190b9ce7d66SEli Fenton 'title' => p_get_metadata(cleanID($id), 'title'), 191b9ce7d66SEli Fenton 'abstract' => p_get_metadata(cleanID($id), 'plugin_description keywords') ?: p_get_metadata(cleanID($id), 'description abstract') 192b9ce7d66SEli Fenton ]; 193d39942acSEli Fenton 194d39942acSEli Fenton // By default, the abstract starts with the title. Remove it so it's not displayed twice, but still fetch 195d39942acSEli Fenton // both pieces of metadata, in case another plugin rewrote the abstract. 196d39942acSEli Fenton $results['abstract'] = preg_replace( 197d39942acSEli Fenton '/^' . self::_pregEscape($results['title']) . '(\r?\n)+/', 198d39942acSEli Fenton '', 199d39942acSEli Fenton $results['abstract'] 200d39942acSEli Fenton ); 201d39942acSEli Fenton 202d39942acSEli Fenton self::$metaCache[$id] = $results; 203d39942acSEli Fenton return $results; 204d39942acSEli Fenton } 205d39942acSEli Fenton 206d39942acSEli Fenton 207d39942acSEli Fenton /** 2086bfd3f23SEli Fenton * Format tooltip text. 2096bfd3f23SEli Fenton * 2106bfd3f23SEli Fenton * @param string $tt - Tooltip text. 2116bfd3f23SEli Fenton * @return string 2126bfd3f23SEli Fenton */ 2136bfd3f23SEli Fenton private function _formatTT($tt) { 214948a1374SEli Fenton // Convert double-newlines into vertical space. 215948a1374SEli Fenton $tt = preg_replace('/(\r?\n){2,}/', '<br><br>', $tt); 216948a1374SEli Fenton // Single newlines get collapsed, just like in HTML. 217948a1374SEli Fenton return preg_replace('/(\r?\n)/', ' ', $tt); 2186bfd3f23SEli Fenton } 219d852dc10SEli Fenton 220d852dc10SEli Fenton 221d852dc10SEli Fenton /** 222d852dc10SEli Fenton * Escape a string for inclusion in a regular expression, assuming forward slash is used as the delimiter. 223d852dc10SEli Fenton * 224d852dc10SEli Fenton * @param string $r - The regex string, without delimiters. 225d852dc10SEli Fenton * @return string 226d852dc10SEli Fenton */ 227d39942acSEli Fenton private static function _pregEscape($r) { 228d852dc10SEli Fenton return preg_replace('/\//', '\\/', preg_quote($r)); 229d852dc10SEli Fenton } 2306bfd3f23SEli Fenton} 231