1007225e5Sgerardnico<?php 2007225e5Sgerardnico 3007225e5Sgerardnico 471f916b9Sgerardnicorequire_once(__DIR__ . "/../class/Analytics.php"); 5007225e5Sgerardnicorequire_once(__DIR__ . "/../class/PluginUtility.php"); 6007225e5Sgerardnicorequire_once(__DIR__ . "/../class/LinkUtility.php"); 7007225e5Sgerardnicorequire_once(__DIR__ . "/../class/HtmlUtility.php"); 8007225e5Sgerardnico 9007225e5Sgerardnicouse ComboStrap\Analytics; 10007225e5Sgerardnicouse ComboStrap\LinkUtility; 11007225e5Sgerardnicouse ComboStrap\PluginUtility; 12007225e5Sgerardnicouse ComboStrap\Tag; 13007225e5Sgerardnico 14007225e5Sgerardnicoif (!defined('DOKU_INC')) die(); 15007225e5Sgerardnico 16007225e5Sgerardnico/** 17007225e5Sgerardnico * 18007225e5Sgerardnico * A link pattern to take over the link of Dokuwiki 19007225e5Sgerardnico * and transform it as a bootstrap link 20007225e5Sgerardnico * 21007225e5Sgerardnico * The handle of the move of link is to be found in the 22007225e5Sgerardnico * admin action {@link action_plugin_combo_linkmove} 23007225e5Sgerardnico * 24007225e5Sgerardnico */ 25007225e5Sgerardnicoclass syntax_plugin_combo_link extends DokuWiki_Syntax_Plugin 26007225e5Sgerardnico{ 27007225e5Sgerardnico const TAG = 'link'; 28ef295d81Sgerardnico const COMPONENT = 'combo_link'; 29007225e5Sgerardnico 305f891b7eSNickeau /** 31*21913ab3SNickeau * Disable the link 32*21913ab3SNickeau */ 33*21913ab3SNickeau const CONF_DISABLE_LINK = "disableLink"; 34*21913ab3SNickeau 35*21913ab3SNickeau /** 365f891b7eSNickeau * The link Tag 375f891b7eSNickeau */ 385f891b7eSNickeau const LINK_TAG = "linkTag"; 395f891b7eSNickeau 40*21913ab3SNickeau /** 41*21913ab3SNickeau * Do the link component allows to be spawn on multilines 42*21913ab3SNickeau */ 43*21913ab3SNickeau const CONF_ENABLE_MULTI_LINES_LINK = "enableMultiLinesLink"; 44*21913ab3SNickeau 455f891b7eSNickeau 46007225e5Sgerardnico /** 47007225e5Sgerardnico * Syntax Type. 48007225e5Sgerardnico * 49007225e5Sgerardnico * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 50007225e5Sgerardnico * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 51007225e5Sgerardnico */ 52007225e5Sgerardnico function getType() 53007225e5Sgerardnico { 54007225e5Sgerardnico return 'substition'; 55007225e5Sgerardnico } 56007225e5Sgerardnico 57007225e5Sgerardnico /** 58007225e5Sgerardnico * How Dokuwiki will add P element 59007225e5Sgerardnico * 60007225e5Sgerardnico * * 'normal' - The plugin can be used inside paragraphs 61007225e5Sgerardnico * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 62007225e5Sgerardnico * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 63007225e5Sgerardnico * 64007225e5Sgerardnico * @see DokuWiki_Syntax_Plugin::getPType() 65007225e5Sgerardnico */ 66007225e5Sgerardnico function getPType() 67007225e5Sgerardnico { 68007225e5Sgerardnico return 'normal'; 69007225e5Sgerardnico } 70007225e5Sgerardnico 71007225e5Sgerardnico /** 72007225e5Sgerardnico * @return array 73007225e5Sgerardnico * Allow which kind of plugin inside 74007225e5Sgerardnico * 75007225e5Sgerardnico * No one of array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 76007225e5Sgerardnico * because we manage self the content and we call self the parser 77007225e5Sgerardnico */ 78007225e5Sgerardnico function getAllowedTypes() 79007225e5Sgerardnico { 80007225e5Sgerardnico return array('substition', 'formatting', 'disabled'); 81007225e5Sgerardnico } 82007225e5Sgerardnico 835f891b7eSNickeau public function accepts($mode) 845f891b7eSNickeau { 855f891b7eSNickeau /** 865f891b7eSNickeau * To avoid that the description if it contains a link 875f891b7eSNickeau * will be taken by the links mode 885f891b7eSNickeau * 895f891b7eSNickeau * For instance, [[https://hallo|https://hallo]] will send https://hallo 905f891b7eSNickeau * to the external link mode 915f891b7eSNickeau */ 925f891b7eSNickeau $linkModes = [ 935f891b7eSNickeau "externallink", 945f891b7eSNickeau "locallink", 955f891b7eSNickeau "internallink", 965f891b7eSNickeau "interwikilink", 975f891b7eSNickeau "emaillink", 985f891b7eSNickeau //"emphasis_open", // italic use // and therefore take over a link as description which is not handy when copying a tweet 995f891b7eSNickeau //"emphasis_close", 1005f891b7eSNickeau //"acrnonym" 1015f891b7eSNickeau ]; 1025f891b7eSNickeau if (in_array($mode, $linkModes)) { 1035f891b7eSNickeau return false; 1045f891b7eSNickeau } else { 1055f891b7eSNickeau return true; 1065f891b7eSNickeau } 1075f891b7eSNickeau } 1085f891b7eSNickeau 1095f891b7eSNickeau 110007225e5Sgerardnico /** 111007225e5Sgerardnico * @see Doku_Parser_Mode::getSort() 112007225e5Sgerardnico * The mode with the lowest sort number will win out 113007225e5Sgerardnico */ 114007225e5Sgerardnico function getSort() 115007225e5Sgerardnico { 116007225e5Sgerardnico return 100; 117007225e5Sgerardnico } 118007225e5Sgerardnico 119007225e5Sgerardnico 120007225e5Sgerardnico function connectTo($mode) 121007225e5Sgerardnico { 122d262537cSgerardnico 123*21913ab3SNickeau if (!$this->getConf(self::CONF_DISABLE_LINK, false)) { 124*21913ab3SNickeau $pattern = LinkUtility::ENTRY_PATTERN_SINGLE_LINE; 125*21913ab3SNickeau if ($this->getConf(self::CONF_ENABLE_MULTI_LINES_LINK,false)){ 126*21913ab3SNickeau $pattern = LinkUtility::ENTRY_PATTERN_MULTI_LINE; 127*21913ab3SNickeau } 128*21913ab3SNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeForComponent($this->getPluginComponent())); 129*21913ab3SNickeau } 130d262537cSgerardnico 131007225e5Sgerardnico } 132007225e5Sgerardnico 1335f891b7eSNickeau public function postConnect() 1345f891b7eSNickeau { 135*21913ab3SNickeau if (!$this->getConf(self::CONF_DISABLE_LINK, false)) { 1365f891b7eSNickeau $this->Lexer->addExitPattern(LinkUtility::EXIT_PATTERN, PluginUtility::getModeForComponent($this->getPluginComponent())); 1375f891b7eSNickeau } 138*21913ab3SNickeau } 1395f891b7eSNickeau 140007225e5Sgerardnico 141007225e5Sgerardnico /** 142007225e5Sgerardnico * The handler for an internal link 143007225e5Sgerardnico * based on `internallink` in {@link Doku_Handler} 144007225e5Sgerardnico * The handler call the good renderer in {@link Doku_Renderer_xhtml} with 145007225e5Sgerardnico * the parameters (ie for instance internallink) 146007225e5Sgerardnico * @param string $match 147007225e5Sgerardnico * @param int $state 148007225e5Sgerardnico * @param int $pos 149007225e5Sgerardnico * @param Doku_Handler $handler 150007225e5Sgerardnico * @return array|bool 151007225e5Sgerardnico */ 152007225e5Sgerardnico function handle($match, $state, $pos, Doku_Handler $handler) 153007225e5Sgerardnico { 154007225e5Sgerardnico 155007225e5Sgerardnico /** 156007225e5Sgerardnico * Because we use the specialPattern, there is only one state ie DOKU_LEXER_SPECIAL 157007225e5Sgerardnico */ 1585f891b7eSNickeau switch ($state) { 1595f891b7eSNickeau case DOKU_LEXER_ENTER: 1605f891b7eSNickeau $attributes = LinkUtility::parse($match); 1615f891b7eSNickeau $tag = new Tag(self::TAG, $attributes, $state, $handler); 162007225e5Sgerardnico $parent = $tag->getParent(); 163007225e5Sgerardnico $parentName = ""; 164007225e5Sgerardnico if ($parent != null) { 165007225e5Sgerardnico $parentName = $parent->getName(); 166*21913ab3SNickeau switch ($parentName) { 167*21913ab3SNickeau case syntax_plugin_combo_button::TAG: 1685f891b7eSNickeau $attributes = PluginUtility::mergeAttributes($attributes, $parent->getAttributes()); 169*21913ab3SNickeau $firstContainingBlock = $parent->getParent(); 170*21913ab3SNickeau break; 171*21913ab3SNickeau case syntax_plugin_combo_column::TAG: 172*21913ab3SNickeau // A col is in a row 173*21913ab3SNickeau $firstContainingBlock = $parent->getParent(); 174*21913ab3SNickeau break; 175*21913ab3SNickeau case "section": 176*21913ab3SNickeau // When editing, there is a section 177*21913ab3SNickeau $firstContainingBlock = $parent->getParent(); 178*21913ab3SNickeau break; 179*21913ab3SNickeau default: 180*21913ab3SNickeau $firstContainingBlock = $parent; 181*21913ab3SNickeau } 182*21913ab3SNickeau if ($firstContainingBlock != null) { 183*21913ab3SNickeau if ($firstContainingBlock->getAttribute("clickable")) { 184*21913ab3SNickeau PluginUtility::addClass2Attributes("stretched-link", $attributes); 185*21913ab3SNickeau $firstContainingBlock->addClass("position-relative"); 186*21913ab3SNickeau $firstContainingBlock->unsetAttribute("clickable"); 1875f891b7eSNickeau } 1885f891b7eSNickeau } 189*21913ab3SNickeau } 190*21913ab3SNickeau 1915f891b7eSNickeau $link = new LinkUtility($attributes[LinkUtility::ATTRIBUTE_REF]); 1925f891b7eSNickeau $linkTag = $link->getHtmlTag(); 1935f891b7eSNickeau return array( 1945f891b7eSNickeau PluginUtility::STATE => $state, 1955f891b7eSNickeau PluginUtility::ATTRIBUTES => $attributes, 1965f891b7eSNickeau PluginUtility::CONTEXT => $parentName, 1975f891b7eSNickeau self::LINK_TAG => $linkTag 1985f891b7eSNickeau ); 1995f891b7eSNickeau case DOKU_LEXER_UNMATCHED: 2005f891b7eSNickeau 20132b85071SNickeau $data = PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 2025f891b7eSNickeau /** 20332b85071SNickeau * Delete the separator `|` between the ref and the description if any 2045f891b7eSNickeau */ 2055f891b7eSNickeau $tag = new Tag(self::TAG, array(), $state, $handler); 2065f891b7eSNickeau $parent = $tag->getParent(); 2075f891b7eSNickeau if ($parent->getName() == self::TAG) { 2085f891b7eSNickeau if (strpos($match, '|') === 0) { 20932b85071SNickeau $data[PluginUtility::PAYLOAD] = substr($match, 1); 2105f891b7eSNickeau } 211007225e5Sgerardnico } 21232b85071SNickeau return $data; 213007225e5Sgerardnico 2145f891b7eSNickeau case DOKU_LEXER_EXIT: 2155f891b7eSNickeau $tag = new Tag(self::TAG, array(), $state, $handler); 2165f891b7eSNickeau $openingTag = $tag->getOpeningTag(); 2175f891b7eSNickeau $openingAttributes = $openingTag->getAttributes(); 2185f891b7eSNickeau $linkTag = $openingTag->getData()[self::LINK_TAG]; 2195f891b7eSNickeau 220*21913ab3SNickeau if ($openingTag->getActualPosition() == $tag->getActualPosition() - 1) { 2215f891b7eSNickeau // There is no name 2225f891b7eSNickeau $link = new LinkUtility($openingAttributes[LinkUtility::ATTRIBUTE_REF]); 2235f891b7eSNickeau $linkName = $link->getName(); 2245f891b7eSNickeau } else { 2255f891b7eSNickeau $linkName = ""; 2265f891b7eSNickeau } 2275f891b7eSNickeau return array( 2285f891b7eSNickeau PluginUtility::STATE => $state, 2295f891b7eSNickeau PluginUtility::ATTRIBUTES => $openingAttributes, 2305f891b7eSNickeau PluginUtility::PAYLOAD => $linkName, 2315f891b7eSNickeau PluginUtility::CONTEXT => $openingTag->getContext(), 2325f891b7eSNickeau self::LINK_TAG => $linkTag 2335f891b7eSNickeau ); 2345f891b7eSNickeau } 2355f891b7eSNickeau return true; 2365f891b7eSNickeau 237007225e5Sgerardnico 238007225e5Sgerardnico } 239007225e5Sgerardnico 240007225e5Sgerardnico /** 241007225e5Sgerardnico * Render the output 242007225e5Sgerardnico * @param string $format 243007225e5Sgerardnico * @param Doku_Renderer $renderer 244007225e5Sgerardnico * @param array $data - what the function handle() return'ed 245007225e5Sgerardnico * @return boolean - rendered correctly? (however, returned value is not used at the moment) 246007225e5Sgerardnico * @see DokuWiki_Syntax_Plugin::render() 247007225e5Sgerardnico * 248007225e5Sgerardnico * 249007225e5Sgerardnico */ 250007225e5Sgerardnico function render($format, Doku_Renderer $renderer, $data) 251007225e5Sgerardnico { 252007225e5Sgerardnico // The data 253007225e5Sgerardnico switch ($format) { 254007225e5Sgerardnico case 'xhtml': 255007225e5Sgerardnico 256007225e5Sgerardnico /** @var Doku_Renderer_xhtml $renderer */ 257007225e5Sgerardnico /** 25819b0880dSgerardnico * Cache problem may occurs while releasing 259007225e5Sgerardnico */ 260007225e5Sgerardnico if (isset($data[PluginUtility::ATTRIBUTES])) { 261007225e5Sgerardnico $attributes = $data[PluginUtility::ATTRIBUTES]; 262007225e5Sgerardnico } else { 263007225e5Sgerardnico $attributes = $data; 264007225e5Sgerardnico } 2655f891b7eSNickeau 266*21913ab3SNickeau PluginUtility::getSnippetManager()->attachCssSnippetForBar(self::TAG); 2675f891b7eSNickeau 2685f891b7eSNickeau 2695f891b7eSNickeau $state = $data[PluginUtility::STATE]; 2705f891b7eSNickeau $payload = $data[PluginUtility::PAYLOAD]; 2715f891b7eSNickeau switch ($state) { 2725f891b7eSNickeau case DOKU_LEXER_ENTER: 2739f4383e9Sgerardnico $ref = $attributes[LinkUtility::ATTRIBUTE_REF]; 2745f891b7eSNickeau unset($attributes[LinkUtility::ATTRIBUTE_REF]); 275722648eaSgerardnico $name = $attributes[LinkUtility::ATTRIBUTE_NAME]; 2765f891b7eSNickeau unset($attributes[LinkUtility::ATTRIBUTE_NAME]); 2779f4383e9Sgerardnico $link = new LinkUtility($ref); 278722648eaSgerardnico if ($name != null) { 279722648eaSgerardnico $link->setName($name); 28019b0880dSgerardnico } 2815f891b7eSNickeau $link->setAttributes($attributes); 2825f891b7eSNickeau 283d262537cSgerardnico 28419b0880dSgerardnico /** 2855f891b7eSNickeau * Extra styling 28619b0880dSgerardnico */ 2875f891b7eSNickeau $parentTag = $data[PluginUtility::CONTEXT]; 2889f4383e9Sgerardnico switch ($parentTag) { 289*21913ab3SNickeau /** 290*21913ab3SNickeau * Button link 291*21913ab3SNickeau */ 2929f4383e9Sgerardnico case syntax_plugin_combo_button::TAG: 2935f891b7eSNickeau $attributes["role"] = "button"; 2945f891b7eSNickeau syntax_plugin_combo_button::processButtonAttributesToHtmlAttributes($attributes); 2955f891b7eSNickeau $htmlLink = $link->renderOpenTag($renderer); 2969f4383e9Sgerardnico break; 2975f891b7eSNickeau case syntax_plugin_combo_badge::TAG: 2989f4383e9Sgerardnico case syntax_plugin_combo_cite::TAG: 2999f4383e9Sgerardnico case syntax_plugin_combo_listitem::TAG: 3009f4383e9Sgerardnico case syntax_plugin_combo_preformatted::TAG: 3015f891b7eSNickeau $htmlLink = $link->renderOpenTag($renderer); 3029f4383e9Sgerardnico break; 3030a517624Sgerardnico case syntax_plugin_combo_dropdown::TAG: 3045f891b7eSNickeau PluginUtility::addClass2Attributes("dropdown-item", $attributes); 3055f891b7eSNickeau $htmlLink = $link->renderOpenTag($renderer); 3060a517624Sgerardnico break; 3079f4383e9Sgerardnico case syntax_plugin_combo_navbarcollapse::COMPONENT: 3085f891b7eSNickeau PluginUtility::addClass2Attributes("navbar-link", $attributes); 3095f891b7eSNickeau $htmlLink = '<div class="navbar-nav">' . $link->renderOpenTag($renderer); 3109f4383e9Sgerardnico break; 3110a517624Sgerardnico case syntax_plugin_combo_navbargroup::COMPONENT: 3125f891b7eSNickeau PluginUtility::addClass2Attributes("nav-link", $attributes); 3135f891b7eSNickeau $htmlLink = '<li class="nav-item">' . $link->renderOpenTag($renderer); 3140a517624Sgerardnico break; 3155f891b7eSNickeau default: 3165f891b7eSNickeau 3175f891b7eSNickeau $htmlLink = $link->renderOpenTag($renderer); 3180a517624Sgerardnico 3199f4383e9Sgerardnico } 3209f4383e9Sgerardnico 32119b0880dSgerardnico 32219b0880dSgerardnico /** 32319b0880dSgerardnico * Add it to the rendering 32419b0880dSgerardnico */ 325007225e5Sgerardnico $renderer->doc .= $htmlLink; 3265f891b7eSNickeau break; 3275f891b7eSNickeau case DOKU_LEXER_UNMATCHED: 32832b85071SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3295f891b7eSNickeau break; 3305f891b7eSNickeau case DOKU_LEXER_EXIT: 3315f891b7eSNickeau 3325f891b7eSNickeau // if there is no link name defined, we get the name as ref in the payload 3335f891b7eSNickeau // otherwise null string 3345f891b7eSNickeau $renderer->doc .= $payload; 3355f891b7eSNickeau 336e3d0019cSgerardnico // Close the link 337e3d0019cSgerardnico $linkTag = $data[self::LINK_TAG]; 338e3d0019cSgerardnico $renderer->doc .= "</$linkTag>"; 339e3d0019cSgerardnico 340e3d0019cSgerardnico // Close the html wrapper element 3415f891b7eSNickeau $context = $data[PluginUtility::CONTEXT]; 3425f891b7eSNickeau switch ($context) { 3435f891b7eSNickeau case syntax_plugin_combo_navbarcollapse::COMPONENT: 3445f891b7eSNickeau $renderer->doc .= '</div>'; 3455f891b7eSNickeau break; 3465f891b7eSNickeau case syntax_plugin_combo_navbargroup::COMPONENT: 3475f891b7eSNickeau $renderer->doc .= '</li>'; 3485f891b7eSNickeau break; 3495f891b7eSNickeau } 3505f891b7eSNickeau 351e3d0019cSgerardnico 3525f891b7eSNickeau } 3535f891b7eSNickeau 354007225e5Sgerardnico 355007225e5Sgerardnico return true; 356007225e5Sgerardnico break; 357007225e5Sgerardnico 3585f891b7eSNickeau case 'metadata': 359007225e5Sgerardnico 3605f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3615f891b7eSNickeau if ($state == DOKU_LEXER_ENTER) { 362007225e5Sgerardnico /** 363007225e5Sgerardnico * Keep track of the backlinks ie meta['relation']['references'] 364007225e5Sgerardnico * @var Doku_Renderer_metadata $renderer 365007225e5Sgerardnico */ 366007225e5Sgerardnico if (isset($data[PluginUtility::ATTRIBUTES])) { 367007225e5Sgerardnico $attributes = $data[PluginUtility::ATTRIBUTES]; 368007225e5Sgerardnico } else { 369007225e5Sgerardnico $attributes = $data; 370007225e5Sgerardnico } 3719f4383e9Sgerardnico $ref = $attributes[LinkUtility::ATTRIBUTE_REF]; 3729f4383e9Sgerardnico 3739f4383e9Sgerardnico $link = new LinkUtility($ref); 3749f4383e9Sgerardnico $name = $attributes[LinkUtility::ATTRIBUTE_NAME]; 3759f4383e9Sgerardnico if ($name != null) { 3769f4383e9Sgerardnico $link->setName($name); 3779f4383e9Sgerardnico } 3789f4383e9Sgerardnico $link->handleMetadata($renderer); 379007225e5Sgerardnico 380007225e5Sgerardnico return true; 3815f891b7eSNickeau } 382007225e5Sgerardnico break; 383007225e5Sgerardnico 384007225e5Sgerardnico case Analytics::RENDERER_FORMAT: 3855f891b7eSNickeau 3865f891b7eSNickeau $state = $data[PluginUtility::STATE]; 3875f891b7eSNickeau if ($state == DOKU_LEXER_ENTER) { 388007225e5Sgerardnico /** 389007225e5Sgerardnico * 390007225e5Sgerardnico * @var renderer_plugin_combo_analytics $renderer 391007225e5Sgerardnico */ 3921c5862d3Sgerardnico $attributes = $data[PluginUtility::ATTRIBUTES]; 3939f4383e9Sgerardnico $ref = $attributes[LinkUtility::ATTRIBUTE_REF]; 3949f4383e9Sgerardnico $link = new LinkUtility($ref); 3959f4383e9Sgerardnico $link->processLinkStats($renderer->stats); 396007225e5Sgerardnico break; 3975f891b7eSNickeau } 398007225e5Sgerardnico 399007225e5Sgerardnico } 400007225e5Sgerardnico // unsupported $mode 401007225e5Sgerardnico return false; 402007225e5Sgerardnico } 403007225e5Sgerardnico 404007225e5Sgerardnico 405007225e5Sgerardnico} 406007225e5Sgerardnico 407