1<?php 2 3 4require_once(__DIR__ . "/../ComboStrap/PluginUtility.php"); 5 6use ComboStrap\ArrayUtility; 7use ComboStrap\ButtonTag; 8use ComboStrap\Call; 9use ComboStrap\CallStack; 10use ComboStrap\DropDownTag; 11use ComboStrap\ExceptionBadArgument; 12use ComboStrap\ExceptionBadSyntax; 13use ComboStrap\ExceptionCompile; 14use ComboStrap\ExceptionNotFound; 15use ComboStrap\FileSystems; 16use ComboStrap\LinkMarkup; 17use ComboStrap\LogUtility; 18use ComboStrap\MarkupRef; 19use ComboStrap\MarkupPath; 20use ComboStrap\PluginUtility; 21use ComboStrap\TagAttributes; 22use ComboStrap\ThirdPartyPlugins; 23use ComboStrap\Web\UrlEndpoint; 24 25if (!defined('DOKU_INC')) die(); 26 27/** 28 * 29 * A link pattern to take over the link of Dokuwiki 30 * and transform it as a bootstrap link 31 * 32 * The handle of the move of link is to be found in the 33 * admin action {@link action_plugin_combo_linkmove} 34 * 35 * popular [[ wiki ]] syntax for linking notes 36 * and makes it easy to build personal wikis, 37 * team knowledge bases, 38 * or something like a Second Brain or a Zettelkasten. 39 * 40 */ 41class syntax_plugin_combo_link extends DokuWiki_Syntax_Plugin 42{ 43 const TAG = 'link'; 44 const COMPONENT = 'combo_link'; 45 46 /** 47 * Disable the link component 48 */ 49 const CONF_DISABLE_LINK = "disableLink"; 50 51 /** 52 * The link Tag 53 * a or p 54 */ 55 const LINK_TAG = "linkTag"; 56 57 /** 58 * Do the link component allows to be spawn on multilines 59 */ 60 const CLICKABLE_ATTRIBUTE = "clickable"; 61 public const ATTRIBUTE_LABEL = 'label'; 62 /** 63 * The key of the array for the handle cache 64 */ 65 public const MARKUP_REF_ATTRIBUTE = 'ref'; 66 67 public const ATTRIBUTE_IMAGE_IN_LABEL = 'image-in-label'; 68 69 /** 70 * A link may have a title or not 71 * ie 72 * [[path:page]] 73 * [[path:page|title]] 74 * are valid 75 * 76 * Get the content until one of this character is found: 77 * * | 78 * * or ]] 79 * * or \n (No line break allowed, too much difficult to debug) 80 * * and not [ (for two links on the same line) 81 */ 82 public const ENTRY_PATTERN_SINGLE_LINE = "\[\[[^\|\]]*(?=[^\n\[]*\]\])"; 83 public const EXIT_PATTERN = "\]\]"; 84 85 86 /** 87 * Dokuwiki Link pattern ter info 88 * Found in {@link \dokuwiki\Parsing\ParserMode\Internallink} 89 */ 90 const SPECIAL_PATTERN = "\[\[.*?\]\](?!\])"; 91 92 /** 93 * The link title attribute (ie popup) 94 */ 95 const TITLE_ATTRIBUTE = "title"; 96 const STRETCHED_LINK = "stretched-link"; 97 const CANONICAL = "link"; 98 99 100 /** 101 * Parse the match of a syntax {@link DokuWiki_Syntax_Plugin} handle function 102 * @param $match 103 * @return string[] - an array with the attributes constant `ATTRIBUTE_xxxx` as key 104 * 105 * Code adapted from {@link Doku_Handler::internallink()} 106 */ 107 public static function parse($match): array 108 { 109 110 // Strip the opening and closing markup 111 $linkString = preg_replace(array('/^\[\[/', '/]]$/u'), '', $match); 112 113 // Split title from URL 114 $linkArray = explode('|', $linkString, 2); 115 116 // Id 117 $attributes[self::MARKUP_REF_ATTRIBUTE] = trim($linkArray[0]); 118 119 120 // Text or image 121 if (!isset($linkArray[1])) { 122 $attributes[self::ATTRIBUTE_LABEL] = null; 123 } else { 124 // An image in the title 125 if (preg_match('/^{{[^}]+}}$/', $linkArray[1])) { 126 // If the title is an image, convert it to an array containing the image details 127 $attributes[self::ATTRIBUTE_IMAGE_IN_LABEL] = Doku_Handler_Parse_Media($linkArray[1]); 128 } else { 129 $attributes[self::ATTRIBUTE_LABEL] = $linkArray[1]; 130 } 131 } 132 133 return $attributes; 134 135 } 136 137 138 /** 139 * Syntax Type. 140 * 141 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 142 * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 143 */ 144 function getType() 145 { 146 return 'substition'; 147 } 148 149 /** 150 * How Dokuwiki will add P element 151 * 152 * * 'normal' - The plugin can be used inside paragraphs 153 * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 154 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 155 * 156 * @see DokuWiki_Syntax_Plugin::getPType() 157 */ 158 function getPType() 159 { 160 return 'normal'; 161 } 162 163 /** 164 * @return array 165 * Allow which kind of plugin inside 166 * 167 * No one of array('container', 'baseonly', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 168 * because we manage self the content and we call self the parser 169 */ 170 function getAllowedTypes(): array 171 { 172 return array('substition', 'formatting', 'disabled'); 173 } 174 175 /** 176 * @param string $mode 177 * @return bool 178 * Accepts inside 179 */ 180 public function accepts($mode): bool 181 { 182 /** 183 * To avoid that the description if it contains a link 184 * will be taken by the links mode 185 * 186 * For instance, [[https://hallo|https://hallo]] will send https://hallo 187 * to the external link mode 188 */ 189 $linkModes = [ 190 "externallink", 191 "locallink", 192 "internallink", 193 "interwikilink", 194 "emaillink", 195 "emphasis", // double slash can not be used inside to preserve the possibility to write an URL in the description 196 //"emphasis_open", // italic use // and therefore take over a link as description which is not handy when copying a tweet 197 //"emphasis_close", 198 //"acrnonym" 199 ]; 200 if (in_array($mode, $linkModes)) { 201 return false; 202 } else { 203 return true; 204 } 205 } 206 207 208 /** 209 * @see Doku_Parser_Mode::getSort() 210 * The mode with the lowest sort number will win out 211 */ 212 function getSort() 213 { 214 /** 215 * It should be less than the number 216 * at {@link \dokuwiki\Parsing\ParserMode\Internallink::getSort} 217 * and the like 218 * 219 * For whatever reason, the number below should be less than 100, 220 * otherwise on windows with DokuWiki Stick, the link syntax may be not taken 221 * into account 222 */ 223 return 99; 224 } 225 226 227 function connectTo($mode) 228 { 229 230 if (!$this->getConf(self::CONF_DISABLE_LINK, false) 231 && 232 $mode !== PluginUtility::getModeFromPluginName(ThirdPartyPlugins::IMAGE_MAPPING_NAME) 233 ) { 234 235 $pattern = self::ENTRY_PATTERN_SINGLE_LINE; 236 $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 237 238 } 239 240 } 241 242 public function postConnect() 243 { 244 if (!$this->getConf(self::CONF_DISABLE_LINK, false)) { 245 $this->Lexer->addExitPattern(self::EXIT_PATTERN, PluginUtility::getModeFromTag($this->getPluginComponent())); 246 } 247 } 248 249 250 /** 251 * The handler for an internal link 252 * based on `internallink` in {@link Doku_Handler} 253 * The handler call the good renderer in {@link Doku_Renderer_xhtml} with 254 * the parameters (ie for instance internallink) 255 * @param string $match 256 * @param int $state 257 * @param int $pos 258 * @param Doku_Handler $handler 259 * @return array|bool 260 */ 261 function handle($match, $state, $pos, Doku_Handler $handler) 262 { 263 264 switch ($state) { 265 case DOKU_LEXER_ENTER: 266 $parsedArray = self::parse($match); 267 $htmlAttributes = TagAttributes::createEmpty(self::TAG); 268 269 /** 270 * The markup ref needs to be passed to the 271 * instructions stack (because we support link with a variable as markup ref 272 * via a {@link syntax_plugin_combo_fragment} in a {@link syntax_plugin_combo_iterator} 273 * 274 * The variable is replaced in the {@link syntax_plugin_combo_link::render()} 275 * at runtime while rendering 276 */ 277 $markupRef = $parsedArray[self::MARKUP_REF_ATTRIBUTE]; 278 if ($markupRef !== null) { 279 /** 280 * If the Rel is a wiki link, we make the path absolute and not relative 281 * (this is for the {@link \ComboStrap\FetcherPageBundler)}} 282 * otherwise the links are not good and are seen as non-existent 283 */ 284 try { 285 $markupRefObject = MarkupRef::createLinkFromRef($markupRef); 286 $scheme = $markupRefObject->getSchemeType(); 287 if ($scheme === MarkupRef::WIKI_URI) { 288 $markupRef = $markupRefObject->getPath()->toAbsoluteId(); 289 } 290 } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotFound $e) { 291 // no a valid ref 292 } 293 $htmlAttributes->addComponentAttributeValue(self::MARKUP_REF_ATTRIBUTE, $markupRef); 294 } 295 296 297 /** 298 * Extra HTML attribute 299 */ 300 $callStack = CallStack::createFromHandler($handler); 301 $parent = $callStack->moveToParent(); 302 $parentName = ""; 303 if ($parent !== false) { 304 305 /** 306 * Button Link 307 * Getting the attributes 308 */ 309 $parentName = $parent->getTagName(); 310 if ($parentName == ButtonTag::MARKUP_LONG) { 311 $htmlAttributes->mergeWithCallStackArray($parent->getAttributes()); 312 } 313 314 /** 315 * Searching Clickable parent 316 */ 317 $maxLevel = 3; 318 $level = 0; 319 while ( 320 $parent != false && 321 !$parent->hasAttribute(self::CLICKABLE_ATTRIBUTE) && 322 $level < $maxLevel 323 ) { 324 $parent = $callStack->moveToParent(); 325 $level++; 326 } 327 if ($parent !== false) { 328 if ($parent->getAttribute(self::CLICKABLE_ATTRIBUTE)) { 329 $htmlAttributes->addClassName(self::STRETCHED_LINK); 330 $parent->addClassName("position-relative"); 331 $parent->removeAttribute(self::CLICKABLE_ATTRIBUTE); 332 } 333 } 334 335 } 336 $returnedArray[PluginUtility::STATE] = $state; 337 $returnedArray[PluginUtility::ATTRIBUTES] = $htmlAttributes->toCallStackArray(); 338 $returnedArray[PluginUtility::CONTEXT] = $parentName; 339 return $returnedArray; 340 341 case DOKU_LEXER_UNMATCHED: 342 343 $data = PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 344 /** 345 * Delete the separator `|` between the ref and the description if any 346 */ 347 $tag = CallStack::createFromHandler($handler); 348 $parent = $tag->moveToParent(); 349 if ($parent->getTagName() == self::TAG) { 350 if (strpos($match, '|') === 0) { 351 $data[PluginUtility::PAYLOAD] = substr($match, 1); 352 } 353 } 354 return $data; 355 356 case DOKU_LEXER_EXIT: 357 358 $callStack = CallStack::createFromHandler($handler); 359 $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 360 361 $openingAttributes = $openingTag->getAttributes(); 362 $openingPosition = $openingTag->getKey(); 363 364 $callStack->moveToEnd(); 365 $previousCall = $callStack->previous(); 366 $previousCallPosition = $previousCall->getKey(); 367 $previousCallContent = $previousCall->getCapturedContent(); 368 369 /** 370 * Link label 371 * is set if there is no content 372 * between enter and exit node 373 */ 374 $linkLabel = ""; 375 if ( 376 $openingPosition == $previousCallPosition // ie [[id]] 377 || 378 ($openingPosition == $previousCallPosition - 1 && $previousCallContent == "|") // ie [[id|]] 379 ) { 380 // There is no name 381 $markupRef = $openingTag->getAttribute(self::MARKUP_REF_ATTRIBUTE); 382 if ($markupRef !== null) { 383 try { 384 $linkLabel = LinkMarkup::createFromRef($markupRef) 385 ->getDefaultLabel(); 386 } catch (ExceptionCompile $e) { 387 LogUtility::error("No default Label can be defined. Error while parsing the markup ref ($markupRef). Error: {$e->getMessage()}", self::CANONICAL, $e); 388 } 389 390 } 391 } 392 return array( 393 PluginUtility::STATE => $state, 394 PluginUtility::ATTRIBUTES => $openingAttributes, 395 PluginUtility::PAYLOAD => $linkLabel, 396 PluginUtility::CONTEXT => $openingTag->getContext() 397 ); 398 } 399 return true; 400 401 402 } 403 404 /** 405 * Render the output 406 * @param string $format 407 * @param Doku_Renderer $renderer 408 * @param array $data - what the function handle() return'ed 409 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 410 * @see DokuWiki_Syntax_Plugin::render() 411 * 412 * 413 */ 414 function render($format, Doku_Renderer $renderer, $data): bool 415 { 416 // The data 417 $state = $data[PluginUtility::STATE]; 418 switch ($format) { 419 case 'xhtml': 420 421 /** @var Doku_Renderer_xhtml $renderer */ 422 /** 423 * Cache problem may occurs while releasing 424 */ 425 if (isset($data[PluginUtility::ATTRIBUTES])) { 426 $callStackAttributes = $data[PluginUtility::ATTRIBUTES]; 427 } else { 428 $callStackAttributes = $data; 429 } 430 431 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(self::TAG); 432 433 switch ($state) { 434 case DOKU_LEXER_ENTER: 435 $tagAttributes = TagAttributes::createFromCallStackArray($callStackAttributes, self::TAG); 436 437 $markupRef = $tagAttributes->getValueAndRemove(self::MARKUP_REF_ATTRIBUTE); 438 if ($markupRef === null) { 439 $message = "Internal Error: A link reference was not found"; 440 LogUtility::internalError($message); 441 $renderer->doc .= LogUtility::wrapInRedForHtml($message); 442 return false; 443 } 444 try { 445 $markupRef = syntax_plugin_combo_variable::replaceVariablesWithValuesFromContext($markupRef); 446 $markupLink = LinkMarkup::createFromRef($markupRef); 447 $markupAttributes = $markupLink->toAttributes(); 448 } catch (ExceptionCompile $e) { 449 // uncomment to get the original error stack trace in dev 450 // and see where the exception comes from 451 // Don't forget to comment back 452// if (PluginUtility::isDevOrTest()) { 453// throw new ExceptionRuntime("Error on markup ref", self::TAG, 0, $e); 454// } 455 456 /** 457 * Error. Example: unknown inter-wiki ... 458 * We still create the a to be xhtml compliante 459 */ 460 $url = UrlEndpoint::createSupportUrl(); 461 $markupAttributes = TagAttributes::createEmpty() 462 ->addOutputAttributeValue("href", $url->toString()) 463 ->addClassName(LinkMarkup::getHtmlClassNotExist()); 464 $renderer->doc .= $markupAttributes->toHtmlEnterTag("a") . $e->getMessage(); 465 466 LogUtility::warning($e->getMessage(), "link", $e); 467 return false; 468 469 } 470 // markup attributes is leading because it has already output attribute such as href 471 $markupAttributes->mergeWithCallStackArray($tagAttributes->toCallStackArray()); 472 $tagAttributes = $markupAttributes; 473 474 475 /** 476 * Extra styling 477 */ 478 $parentTag = $data[PluginUtility::CONTEXT]; 479 $htmlPrefix = ""; 480 switch ($parentTag) { 481 /** 482 * Button link 483 */ 484 case ButtonTag::MARKUP_LONG: 485 $tagAttributes->addOutputAttributeValue("role", "button"); 486 ButtonTag::processButtonAttributesToHtmlAttributes($tagAttributes); 487 break; 488 case DropDownTag::TAG: 489 $tagAttributes->addClassName("dropdown-item"); 490 break; 491 case syntax_plugin_combo_navbarcollapse::COMPONENT: 492 $tagAttributes->addClassName("navbar-link"); 493 $htmlPrefix = '<div class="navbar-nav">'; 494 break; 495 case syntax_plugin_combo_navbargroup::COMPONENT: 496 $tagAttributes->addClassName("nav-link"); 497 $htmlPrefix = '<li class="nav-item">'; 498 break; 499 default: 500 case syntax_plugin_combo_badge::TAG: 501 case syntax_plugin_combo_cite::TAG: 502 case syntax_plugin_combo_contentlistitem::DOKU_TAG: 503 case syntax_plugin_combo_preformatted::TAG: 504 break; 505 506 } 507 508 /** 509 * Add it to the rendering 510 */ 511 $renderer->doc .= $htmlPrefix . $tagAttributes->toHtmlEnterTag("a"); 512 break; 513 case DOKU_LEXER_UNMATCHED: 514 $renderer->doc .= PluginUtility::renderUnmatched($data); 515 break; 516 case DOKU_LEXER_EXIT: 517 518 // if there is no link name defined, we get the name as ref in the payload 519 // otherwise null string 520 $renderer->doc .= $data[PluginUtility::PAYLOAD] ?? ''; 521 522 // Close the link 523 $renderer->doc .= "</a>"; 524 525 // Close the html wrapper element 526 $context = $data[PluginUtility::CONTEXT]; 527 switch ($context) { 528 case syntax_plugin_combo_navbarcollapse::COMPONENT: 529 $renderer->doc .= '</div>'; 530 break; 531 case syntax_plugin_combo_navbargroup::COMPONENT: 532 $renderer->doc .= '</li>'; 533 break; 534 } 535 536 } 537 return true; 538 case 'metadata': 539 540 /** 541 * @var Doku_Renderer_metadata $renderer 542 */ 543 switch ($state) { 544 case DOKU_LEXER_ENTER: 545 /** 546 * Keep track of the backlinks ie meta['relation']['references'] 547 * @var Doku_Renderer_metadata $renderer 548 */ 549 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 550 551 $markupRef = $tagAttributes->getValue(self::MARKUP_REF_ATTRIBUTE); 552 if ($markupRef === null) { 553 LogUtility::internalError("The markup ref was not found for a link."); 554 return false; 555 } 556 try { 557 $type = MarkupRef::createLinkFromRef($markupRef) 558 ->getSchemeType(); 559 } catch (ExceptionCompile $e) { 560 LogUtility::error("The markup ref could not be parsed. Error:{$e->getMessage()}"); 561 return false; 562 } 563 $name = $tagAttributes->getValue(self::ATTRIBUTE_LABEL); 564 565 switch ($type) { 566 case MarkupRef::WIKI_URI: 567 /** 568 * The relative link should be passed (ie the original) 569 * Dokuwiki has a default description 570 * We can't pass empty or the array(title), it does not work 571 */ 572 $descriptionToDelete = "b"; 573 $renderer->internallink($markupRef, $descriptionToDelete); 574 $renderer->doc = substr($renderer->doc, 0, -strlen($descriptionToDelete)); 575 break; 576 case MarkupRef::WEB_URI: 577 $renderer->externallink($markupRef, $name); 578 break; 579 case MarkupRef::LOCAL_URI: 580 $renderer->locallink($markupRef, $name); 581 break; 582 case MarkupRef::EMAIL_URI: 583 $renderer->emaillink($markupRef, $name); 584 break; 585 case MarkupRef::INTERWIKI_URI: 586 $interWikiSplit = preg_split("/>/", $markupRef); 587 $renderer->interwikilink($markupRef, $name, $interWikiSplit[0], $interWikiSplit[1]); 588 break; 589 case MarkupRef::WINDOWS_SHARE_URI: 590 $renderer->windowssharelink($markupRef, $name); 591 break; 592 case MarkupRef::VARIABLE_URI: 593 // No backlinks for link template 594 break; 595 default: 596 LogUtility::msg("The markup reference ({$markupRef}) with the type $type was not processed into the metadata"); 597 } 598 599 return true; 600 case DOKU_LEXER_UNMATCHED: 601 $renderer->doc .= PluginUtility::renderUnmatched($data); 602 break; 603 } 604 break; 605 606 case renderer_plugin_combo_analytics::RENDERER_FORMAT: 607 608 if ($state === DOKU_LEXER_ENTER) { 609 /** 610 * 611 * @var renderer_plugin_combo_analytics $renderer 612 */ 613 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 614 $ref = $tagAttributes->getValue(self::MARKUP_REF_ATTRIBUTE); 615 try { 616 $markupRef = LinkMarkup::createFromRef($ref); 617 } catch (ExceptionCompile $e) { 618 LogUtility::error("Error while parsing the ref ($ref). Error: {$e->getMessage()}. No further analytics."); 619 return false; 620 } 621 $refType = $markupRef->getMarkupRef()->getSchemeType(); 622 623 624 /** 625 * @param array $stats 626 * Calculate internal link statistics 627 */ 628 $stats = &$renderer->stats; 629 switch ($refType) { 630 631 case MarkupRef::WIKI_URI: 632 633 /** 634 * Internal link count 635 */ 636 if (!array_key_exists(renderer_plugin_combo_analytics::INTERNAL_LINK_COUNT, $stats)) { 637 $stats[renderer_plugin_combo_analytics::INTERNAL_LINK_COUNT] = 0; 638 } 639 $stats[renderer_plugin_combo_analytics::INTERNAL_LINK_COUNT]++; 640 641 642 /** 643 * Broken link ? 644 */ 645 try { 646 $path = $markupRef->getMarkupRef()->getPath(); 647 $linkedPage = MarkupPath::createPageFromPathObject($path); 648 if (!FileSystems::exists($path)) { 649 $internalLinkBroken = $stats[renderer_plugin_combo_analytics::INTERNAL_LINK_BROKEN_COUNT] ?? 0; 650 $stats[renderer_plugin_combo_analytics::INTERNAL_LINK_BROKEN_COUNT] = $internalLinkBroken + 1; 651 $stats[renderer_plugin_combo_analytics::INFO][] = "The internal linked page `{$linkedPage}` does not exist"; 652 } 653 } catch (ExceptionNotFound $e) { 654 // no local path 655 } 656 657 /** 658 * Calculate link distance 659 */ 660 global $ID; 661 $id = $linkedPage->getWikiId(); 662 $a = explode(':', getNS($ID)); 663 $b = explode(':', getNS($id)); 664 while (isset($a[0]) && $a[0] == $b[0]) { 665 array_shift($a); 666 array_shift($b); 667 } 668 $length = count($a) + count($b); 669 $stats[renderer_plugin_combo_analytics::INTERNAL_LINK_DISTANCE][] = $length; 670 break; 671 672 case MarkupRef::WEB_URI: 673 674 if (!array_key_exists(renderer_plugin_combo_analytics::EXTERNAL_LINK_COUNT, $stats)) { 675 $stats[renderer_plugin_combo_analytics::EXTERNAL_LINK_COUNT] = 0; 676 } 677 $stats[renderer_plugin_combo_analytics::EXTERNAL_LINK_COUNT]++; 678 break; 679 680 case MarkupRef::LOCAL_URI: 681 682 if (!array_key_exists(renderer_plugin_combo_analytics::LOCAL_LINK_COUNT, $stats)) { 683 $stats[renderer_plugin_combo_analytics::LOCAL_LINK_COUNT] = 0; 684 } 685 $stats[renderer_plugin_combo_analytics::LOCAL_LINK_COUNT]++; 686 break; 687 688 case MarkupRef::INTERWIKI_URI: 689 690 if (!array_key_exists(renderer_plugin_combo_analytics::INTERWIKI_LINK_COUNT, $stats)) { 691 $stats[renderer_plugin_combo_analytics::INTERWIKI_LINK_COUNT] = 0; 692 } 693 $stats[renderer_plugin_combo_analytics::INTERWIKI_LINK_COUNT]++; 694 break; 695 696 case MarkupRef::EMAIL_URI: 697 698 if (!array_key_exists(renderer_plugin_combo_analytics::EMAIL_COUNT, $stats)) { 699 $stats[renderer_plugin_combo_analytics::EMAIL_COUNT] = 0; 700 } 701 $stats[renderer_plugin_combo_analytics::EMAIL_COUNT]++; 702 break; 703 704 case MarkupRef::WINDOWS_SHARE_URI: 705 706 if (!array_key_exists(renderer_plugin_combo_analytics::WINDOWS_SHARE_COUNT, $stats)) { 707 $stats[renderer_plugin_combo_analytics::WINDOWS_SHARE_COUNT] = 0; 708 } 709 $stats[renderer_plugin_combo_analytics::WINDOWS_SHARE_COUNT]++; 710 break; 711 712 case MarkupRef::VARIABLE_URI: 713 714 if (!array_key_exists(renderer_plugin_combo_analytics::TEMPLATE_LINK_COUNT, $stats)) { 715 $stats[renderer_plugin_combo_analytics::TEMPLATE_LINK_COUNT] = 0; 716 } 717 $stats[renderer_plugin_combo_analytics::TEMPLATE_LINK_COUNT]++; 718 break; 719 720 default: 721 722 LogUtility::msg("The link `{$ref}` with the type ($refType) is not taken into account into the statistics"); 723 724 } 725 726 727 break; 728 } 729 730 } 731 732 return false; 733 } 734 735 736 /** 737 * Utility function to add a link into the callstack 738 * @param CallStack $callStack 739 * @param TagAttributes $tagAttributes 740 */ 741 public 742 static function addOpenLinkTagInCallStack(CallStack $callStack, TagAttributes $tagAttributes) 743 { 744 $parent = $callStack->moveToParent(); 745 $context = ""; 746 $attributes = $tagAttributes->toCallStackArray(); 747 if ($parent !== false) { 748 $context = $parent->getTagName(); 749 if ($context === ButtonTag::MARKUP_LONG) { 750 // the link takes by default the data from the button 751 $parentAttributes = $parent->getAttributes(); 752 if ($parentAttributes !== null) { 753 $attributes = ArrayUtility::mergeByValue($parentAttributes, $attributes); 754 } 755 } 756 } 757 $callStack->appendCallAtTheEnd( 758 Call::createComboCall( 759 syntax_plugin_combo_link::TAG, 760 DOKU_LEXER_ENTER, 761 $attributes, 762 $context 763 )); 764 } 765 766 public 767 static function addExitLinkTagInCallStack(CallStack $callStack) 768 { 769 $callStack->appendCallAtTheEnd( 770 Call::createComboCall( 771 syntax_plugin_combo_link::TAG, 772 DOKU_LEXER_EXIT 773 )); 774 } 775} 776 777