1<?php 2 3 4namespace ComboStrap; 5 6 7use ComboStrap\Api\ApiRouter; 8use dokuwiki\Extension\Plugin; 9use dokuwiki\Extension\SyntaxPlugin; 10 11 12/** 13 * Class url static 14 * List of static utilities 15 */ 16class PluginUtility 17{ 18 19 const DOKU_DATA_DIR = '/dokudata/pages'; 20 const DOKU_CACHE_DIR = '/dokudata/cache'; 21 22 /** 23 * Key in the data array between the handle and render function 24 */ 25 const STATE = "state"; 26 const PAYLOAD = "payload"; // The html or text 27 const ATTRIBUTES = "attributes"; 28// The context is generally the parent tag but it may be also the grandfather. 29// It permits to determine the HTML that is outputted 30 const CONTEXT = 'context'; 31 const TAG = "tag"; 32 33 /** 34 * The name of the hidden/private namespace 35 * where the icon and other artifactory are stored 36 */ 37 const COMBOSTRAP_NAMESPACE_NAME = "combostrap"; 38 39 const PARENT = "parent"; 40 const POSITION = "position"; 41 42 43 const EXIT_MESSAGE = "exit_message"; 44 const EXIT_CODE = "exit_code"; 45 46 const DISPLAY = "display"; 47 const MARKUP_TAG = "markup-tag"; 48 49 50 /** 51 * The URL base of the documentation 52 */ 53 static $URL_APEX; 54 55 56 /** 57 * @var string - the plugin base name (ie the directory) 58 * ie $INFO_PLUGIN['base']; 59 * This is a constant because it permits code analytics 60 * such as verification of a path 61 */ 62 const PLUGIN_BASE_NAME = "combo"; 63 64 /** 65 * The name of the template plugin 66 */ 67 const TEMPLATE_STRAP_NAME = "strap"; 68 69 /** 70 * @var array 71 */ 72 static $INFO_PLUGIN; 73 74 static $PLUGIN_LANG; 75 76 /** 77 * The plugin name 78 * (not the same than the base as it's not related to the directory 79 * @var string 80 */ 81 public static $PLUGIN_NAME; 82 /** 83 * @var LocalPath 84 */ 85 private static $PLUGIN_INFO_FILE; 86 87 88 /** 89 * Initiate the static variable 90 * See the call after this class 91 */ 92 static function init() 93 { 94 95 $pluginInfoFile = DirectoryLayout::getPluginInfoPath(); 96 self::$INFO_PLUGIN = confToHash($pluginInfoFile->toAbsoluteId()); 97 self::$PLUGIN_NAME = 'ComboStrap'; 98 global $lang; 99 self::$PLUGIN_LANG = $lang[self::PLUGIN_BASE_NAME] ?? null; 100 self::$URL_APEX = "https://" . parse_url(self::$INFO_PLUGIN['url'], PHP_URL_HOST); 101 //self::$VERSION = self::$INFO_PLUGIN['version']; 102 103 } 104 105 /** 106 * @param $inputExpression 107 * @return false|int 1|0 108 * returns: 109 * - 1 if the input expression is a pattern, 110 * - 0 if not, 111 * - FALSE if an error occurred. 112 */ 113 static function isRegularExpression($inputExpression) 114 { 115 116 $regularExpressionPattern = "/(\\/.*\\/[gmixXsuUAJ]?)/"; 117 return preg_match($regularExpressionPattern, $inputExpression); 118 119 } 120 121 /** 122 * Return a mode from a tag (ie from a {@link Plugin::getPluginComponent()} 123 * @param $tag 124 * @return string 125 * 126 * A mode is just a name for a class 127 * Example: $Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock()); 128 */ 129 public static function getModeFromTag($tag) 130 { 131 return "plugin_" . self::getComponentName($tag); 132 } 133 134 135 /** 136 * This pattern allows space after the tag name 137 * for an end tag 138 * As XHTML (https://www.w3.org/TR/REC-xml/#dt-etag) 139 * @param $tag 140 * @return string 141 */ 142 public static function getEndTagPattern($tag) 143 { 144 return "</$tag\s*>"; 145 } 146 147 /** 148 * @param $tag 149 * @return string 150 * 151 * Create a open tag pattern without lookahead. 152 * Used for 153 * @link https://dev.w3.org/html5/html-author/#void-elements-0 154 */ 155 public static function getVoidElementTagPattern($tag) 156 { 157 return ' < ' . $tag . ' .*?>'; 158 } 159 160 161 /** 162 * Take an array where the key is the attribute name 163 * and return a HTML tag string 164 * 165 * The attribute name and value are escaped 166 * 167 * @param $attributes - combo attributes 168 * @return string 169 * @deprecated to allowed background and other metadata, use {@link TagAttributes::toHtmlEnterTag()} 170 */ 171 public static function array2HTMLAttributesAsString($attributes) 172 { 173 174 $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 175 return $tagAttributes->toHTMLAttributeString(); 176 177 } 178 179 /** 180 * 181 * Parse the attributes part of a match 182 * 183 * Example: 184 * line-numbers="value" 185 * line-numbers='value' 186 * 187 * This value may be in: 188 * * configuration value 189 * * as well as in the match of a {@link SyntaxPlugin} 190 * 191 * @param $string 192 * @return array 193 * 194 * To parse a match, use {@link PluginUtility::getTagAttributes()} 195 * 196 * 197 */ 198 public static function parseAttributes($string) 199 { 200 201 $parameters = array(); 202 203// Rules 204// * name may be alone (ie true boolean attribute) 205// * a name may get a `-` 206// * there may be space every everywhere when the value is enclosed with a quote 207// * there may be no space in the value and between the equal sign when the value is not enclosed 208// 209// /i not case sensitive 210 $attributePattern = '\s*([-\w]+)\s*(?:=(\s*[\'"]([^`"]*)[\'"]\s*|[^\s]*))?'; 211 $result = preg_match_all('/' . $attributePattern . '/i', $string, $matches); 212 if ($result != 0) { 213 foreach ($matches[1] as $key => $parameterKey) { 214 215// group 3 (ie the value between quotes) 216 $value = $matches[3][$key]; 217 if ($value == "") { 218// check the value without quotes 219 $value = $matches[2][$key]; 220 } 221// if there is no value, this is a boolean 222 if ($value == "") { 223 $value = true; 224 } else { 225 $value = hsc($value); 226 } 227 $parameters[hsc(strtolower($parameterKey))] = $value; 228 } 229 } 230 return $parameters; 231 232 } 233 234 public static function getTagAttributes(string $match, array $knownTypes = [], bool $allowFirstBooleanAttributesAsType = false): array 235 { 236 return self::getQualifiedTagAttributes($match, false, "", $knownTypes, $allowFirstBooleanAttributesAsType); 237 } 238 239 /** 240 * Return the attribute of a tag 241 * Because they are users input, they are all escaped 242 * @param $match 243 * @param $hasThirdValue - if true, the third parameter is treated as value, not a property and returned in the `third` key 244 * use for the code/file/console where they accept a name as third value 245 * @param $keyThirdArgument - if a third argument is found, return it with this key 246 * @param array|null $knownTypes 247 * @param bool $allowFirstBooleanAttributesAsType 248 * @return array 249 */ 250 public static function getQualifiedTagAttributes($match, $hasThirdValue, $keyThirdArgument, array $knownTypes = [], bool $allowFirstBooleanAttributesAsType = false): array 251 { 252 253 $match = PluginUtility::getPreprocessEnterTag($match); 254 255 // Suppress the tag name (ie until the first blank) 256 $spacePosition = strpos($match, " "); 257 if (!$spacePosition) { 258 // No space, meaning this is only the tag name 259 return array(); 260 } 261 $match = trim(substr($match, $spacePosition)); 262 if ($match == "") { 263 return array(); 264 } 265 266 /** 267 * Do we have a type as first argument ? 268 */ 269 $attributes = array(); 270 $spacePosition = strpos($match, " "); 271 if ($spacePosition) { 272 $nextArgument = substr($match, 0, $spacePosition); 273 } else { 274 $nextArgument = $match; 275 } 276 277 $isBooleanAttribute = !strpos($nextArgument, "="); 278 $isType = false; 279 if ($isBooleanAttribute) { 280 $possibleTypeLowercase = strtolower($nextArgument); 281 if ($allowFirstBooleanAttributesAsType) { 282 $isType = true; 283 $nextArgument = $possibleTypeLowercase; 284 } else { 285 if (!empty($knownTypes) && in_array($possibleTypeLowercase, $knownTypes)) { 286 $isType = true; 287 $nextArgument = $possibleTypeLowercase; 288 } 289 } 290 } 291 if ($isType) { 292 293 $attributes[TagAttributes::TYPE_KEY] = $nextArgument; 294 /** 295 * Suppress the type 296 */ 297 $match = substr($match, strlen($nextArgument)); 298 $match = trim($match); 299 300 /** 301 * Do we have a value as first argument ? 302 */ 303 if (!empty($hasThirdValue)) { 304 $spacePosition = strpos($match, " "); 305 if ($spacePosition) { 306 $nextArgument = substr($match, 0, $spacePosition); 307 } else { 308 $nextArgument = $match; 309 } 310 if (!strpos($nextArgument, "=") && !empty($nextArgument)) { 311 $attributes[$keyThirdArgument] = $nextArgument; 312 /** 313 * Suppress the third argument 314 */ 315 $match = substr($match, strlen($nextArgument)); 316 $match = trim($match); 317 } 318 } 319 } 320 321 /** 322 * Parse the remaining attributes 323 */ 324 $parsedAttributes = self::parseAttributes($match); 325 326 /** 327 * Merge 328 */ 329 $attributes = array_merge($attributes, $parsedAttributes);; 330 331 return $attributes; 332 333 } 334 335 /** 336 * @param $tag 337 * @return string 338 * Create a pattern used where the tag is not a container. 339 * ie 340 * <br/> 341 * 342 * <icon/> 343 * This is generally used with a subtition plugin 344 * and a {@link Lexer::addSpecialPattern} state 345 * where the tag is just replaced 346 */ 347 public static function getEmptyTagPattern($tag): string 348 { 349 350 /** 351 * A tag should start with the tag 352 * `(?=[/ ]{1})` - a space or the / (lookahead) => to allow allow tag name with minus character 353 * `(?![^/]>)` - it's not a normal tag (ie a > with the previous character that is not /) 354 * `[^>]*` then until the > is found (dokuwiki capture greedy, don't use the point character) 355 * then until the close `/>` character 356 */ 357 return '<' . $tag . '(?=[/ ]{1})(?![^/]>)[^>]*\/>'; 358 } 359 360 public static function getEmptyTagPatternGeneral(): string 361 { 362 363 return self::getEmptyTagPattern("[\w-]+"); 364 } 365 366 /** 367 * Just call this function from a class like that 368 * getTageName(get_called_class()) 369 * to get the tag name (ie the component plugin) 370 * of a syntax plugin 371 * 372 * @param $get_called_class 373 * @return string 374 */ 375 public static function getTagName($get_called_class) 376 { 377 list(/* $t */, /* $p */, /* $n */, $c) = explode('_', $get_called_class, 4); 378 return (isset($c) ? $c : ''); 379 } 380 381 /** 382 * Just call this function from a class like that 383 * getAdminPageName(get_called_class()) 384 * to get the page name of a admin plugin 385 * 386 * @param $get_called_class 387 * @return string - the admin page name 388 */ 389 public static function getAdminPageName($get_called_class) 390 { 391 $names = explode('_', $get_called_class); 392 $names = array_slice($names, -2); 393 return implode('_', $names); 394 } 395 396 public static function getNameSpace() 397 { 398// No : at the begin of the namespace please 399 return self::PLUGIN_BASE_NAME . ':'; 400 } 401 402 /** 403 * @param $get_called_class - the plugin class 404 * @return array 405 */ 406 public static function getTags($get_called_class) 407 { 408 $elements = array(); 409 $elementName = PluginUtility::getTagName($get_called_class); 410 $elements[] = $elementName; 411 $elements[] = strtoupper($elementName); 412 return $elements; 413 } 414 415 /** 416 * Render a text 417 * @param $pageContent 418 * @return string|null 419 */ 420 public static function render($pageContent): ?string 421 { 422 return MarkupRenderUtility::renderText2XhtmlAndStripPEventually($pageContent, false); 423 } 424 425 426 /** 427 * This method will takes attributes 428 * and process the plugin styling attribute such as width and height 429 * to put them in a style HTML attribute 430 * @param TagAttributes $attributes 431 */ 432 public static function processStyle(&$attributes) 433 { 434 // Style 435 $styleAttributeName = "style"; 436 if ($attributes->hasComponentAttribute($styleAttributeName)) { 437 $properties = explode(";", $attributes->getValueAndRemove($styleAttributeName)); 438 foreach ($properties as $property) { 439 list($key, $value) = explode(":", $property); 440 if ($key != "") { 441 $attributes->addStyleDeclarationIfNotSet($key, $value); 442 } 443 } 444 } 445 446 447 /** 448 * Border Color 449 * For background color, see {@link TagAttributes::processBackground()} 450 * For text color, see {@link TextColor} 451 */ 452 453 if ($attributes->hasComponentAttribute(ColorRgb::BORDER_COLOR)) { 454 $colorValue = $attributes->getValueAndRemove(ColorRgb::BORDER_COLOR); 455 $attributes->addStyleDeclarationIfNotSet(ColorRgb::BORDER_COLOR, ColorRgb::createFromString($colorValue)->toCssValue()); 456 self::checkDefaultBorderColorAttributes($attributes); 457 } 458 459 460 } 461 462 /** 463 * Return the name of the requested script 464 */ 465 public 466 static function getRequestScript() 467 { 468 $scriptPath = null; 469 $testPropertyValue = self::getPropertyValue("SCRIPT_NAME"); 470 if (defined('DOKU_UNITTEST') && $testPropertyValue != null) { 471 return $testPropertyValue; 472 } 473 if (array_key_exists("DOCUMENT_URI", $_SERVER)) { 474 $scriptPath = $_SERVER["DOCUMENT_URI"]; 475 } 476 if ($scriptPath == null && array_key_exists("SCRIPT_NAME", $_SERVER)) { 477 $scriptPath = $_SERVER["SCRIPT_NAME"]; 478 } 479 if ($scriptPath == null) { 480 msg("Unable to find the main script", LogUtility::LVL_MSG_ERROR); 481 } 482 $path_parts = pathinfo($scriptPath); 483 return $path_parts['basename']; 484 } 485 486 /** 487 * 488 * @param $name 489 * @param $default 490 * @return string - the value of a query string property or if in test mode, the value of a test variable 491 * set with {@link self::setTestProperty} 492 * This is used to test script that are not supported by the dokuwiki test framework 493 * such as css.php 494 * @deprecated use {@link ApiRouter::getRequestParameter()} 495 */ 496 public 497 static function getPropertyValue($name, $default = null) 498 { 499 global $INPUT; 500 $value = $INPUT->str($name); 501 if ($value == null && defined('DOKU_UNITTEST')) { 502 global $COMBO; 503 if ($COMBO !== null) { 504 $value = $COMBO[$name]; 505 } 506 } 507 if ($value == null) { 508 return $default; 509 } else { 510 return $value; 511 } 512 513 } 514 515 /** 516 * Create an URL to the documentation website 517 * @param $canonical - canonical id or slug 518 * @param $label - the text of the link 519 * @param bool $withIcon - used to break the recursion with the message in the {@link IconDownloader} 520 * @return string - an url 521 */ 522 public 523 static function getDocumentationHyperLink($canonical, $label, bool $withIcon = true, $tooltip = ""): string 524 { 525 526 $xhtmlIcon = ""; 527 if ($withIcon) { 528 529 $logoPath = WikiPath::createComboResource("images:logo.svg"); 530 try { 531 $fetchImage = FetcherSvg::createSvgFromPath($logoPath); 532 $fetchImage->setRequestedType(FetcherSvg::ICON_TYPE) 533 ->setRequestedWidth(20); 534 $xhtmlIcon = SvgImageLink::createFromFetcher($fetchImage) 535 ->renderMediaTag(); 536 } catch (ExceptionCompile $e) { 537 /** 538 * We don't throw because this function 539 * is also used by: 540 * * the log functionality to show link to the documentation creating a loop 541 * * inside the configuration description crashing the page 542 */ 543 if (PluginUtility::isDevOrTest()) { 544// shows errors in the html only on dev/test 545 $xhtmlIcon = "Error: {$e->getMessage()}"; 546 } 547 } 548 549 } 550 $urlApex = self::$URL_APEX; 551 $path = str_replace(":", "/", $canonical); 552 if (empty($tooltip)) { 553 $title = $label; 554 } else { 555 $title = $tooltip; 556 } 557 $htmlToolTip = ""; 558 if (!empty($tooltip)) { 559 $dataAttributeNamespace = Bootstrap::getDataNamespace(); 560 $htmlToolTip = "data{$dataAttributeNamespace}-toggle=\"tooltip\""; 561 } 562 return "$xhtmlIcon<a href=\"$urlApex/$path\" title=\"$title\" $htmlToolTip style=\"text-decoration:none;\">$label</a>"; 563 } 564 565 /** 566 * An utility function to not search every time which array should be first 567 * @param array $inlineAttributes - the component inline attributes 568 * @param array $defaultAttributes - the default configuration attributes 569 * @return array - a merged array 570 */ 571 public 572 static function mergeAttributes(array $inlineAttributes, array $defaultAttributes = array()) 573 { 574 return array_merge($defaultAttributes, $inlineAttributes); 575 } 576 577 /** 578 * A pattern for a container tag 579 * that needs to catch the content 580 * 581 * Use as a special pattern (substition) 582 * 583 * The {@link \syntax_plugin_combo_math} use it 584 * @param $tag 585 * @return string - a pattern 586 */ 587 public 588 static function getLeafContainerTagPattern($tag) 589 { 590 return '<' . $tag . '.*?>.*?<\/' . $tag . '>'; 591 } 592 593 /** 594 * Return the content of a tag 595 * 596 * <math>Content</math> 597 * @param $match 598 * @return string the content 599 */ 600 public 601 static function getTagContent($match) 602 { 603// From the first > 604 $start = strpos($match, ">"); 605 if ($start == false) { 606 LogUtility::msg("The match does not contain any opening tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 607 return ""; 608 } 609 $match = substr($match, $start + 1); 610// If this is the last character, we get a false 611 if ($match == false) { 612 LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 613 return ""; 614 } 615 616 $end = strrpos($match, "</"); 617 if ($end == false) { 618 LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 619 return ""; 620 } 621 622 return substr($match, 0, $end); 623 } 624 625 /** 626 * 627 * Check if a HTML tag was already added for a request 628 * The request id is just the timestamp 629 * An indicator array should be provided 630 * @return string 631 */ 632 public 633 static function getRequestId() 634 { 635 636 if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { 637// since php 5.4 638 $requestTime = $_SERVER['REQUEST_TIME_FLOAT']; 639 } else { 640// DokuWiki test framework use this 641 $requestTime = $_SERVER['REQUEST_TIME']; 642 } 643 $keyPrefix = 'combo_'; 644 645 global $ID; 646 return $keyPrefix . hash('crc32b', $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . $requestTime . $ID); 647 648 } 649 650 /** 651 * 652 * Return the requested wiki id (known also as page id) 653 * 654 * If the code is rendering a sidebar, it will not return the id of the sidebar 655 * but the requested wiki id 656 * 657 * @return string 658 * @throws ExceptionNotFound 659 * @deprecated use {@link ExecutionContext::getRequestedPath()} 660 */ 661 public static function getRequestedWikiId(): string 662 { 663 664 return ExecutionContext::getActualOrCreateFromEnv()->getRequestedPath()->getWikiId(); 665 666 } 667 668 public static function xmlEncode($text) 669 { 670 /** 671 * {@link htmlentities } 672 */ 673 return htmlentities($text, ENT_XML1); 674 } 675 676 677 /** 678 * Add a class 679 * @param $classValue 680 * @param array $attributes 681 */ 682 public 683 static function addClass2Attributes($classValue, array &$attributes) 684 { 685 self::addAttributeValue("class", $classValue, $attributes); 686 } 687 688 /** 689 * Add a style property to the attributes 690 * @param $property 691 * @param $value 692 * @param array $attributes 693 * @deprecated use {@link TagAttributes::addStyleDeclarationIfNotSet()} instead 694 */ 695 public 696 static function addStyleProperty($property, $value, array &$attributes) 697 { 698 if (isset($attributes["style"])) { 699 $attributes["style"] .= ";$property:$value"; 700 } else { 701 $attributes["style"] = "$property:$value"; 702 } 703 704 } 705 706 /** 707 * Add default border attributes 708 * to see a border 709 * Doc 710 * https://combostrap.com/styling/color#border_color 711 * @param TagAttributes $tagAttributes 712 */ 713 private 714 static function checkDefaultBorderColorAttributes(&$tagAttributes) 715 { 716 /** 717 * border color was set without the width 718 * setting the width 719 */ 720 if (!( 721 $tagAttributes->hasStyleDeclaration("border") 722 || 723 $tagAttributes->hasStyleDeclaration("border-width") 724 ) 725 ) { 726 $tagAttributes->addStyleDeclarationIfNotSet("border-width", "1px"); 727 } 728 /** 729 * border color was set without the style 730 * setting the style 731 */ 732 if (! 733 ( 734 $tagAttributes->hasStyleDeclaration("border") 735 || 736 $tagAttributes->hasStyleDeclaration("border-style") 737 ) 738 ) { 739 $tagAttributes->addStyleDeclarationIfNotSet("border-style", "solid"); 740 741 } 742 if (!$tagAttributes->hasStyleDeclaration("border-radius")) { 743 $tagAttributes->addStyleDeclarationIfNotSet("border-radius", ".25rem"); 744 } 745 746 } 747 748 /** 749 * @param $match 750 * @return null|string - return the tag name or null if not found 751 */ 752 public 753 static function getMarkupTag($match): ?string 754 { 755 756 // Until the first > 757 $pos = strpos($match, ">"); 758 if (!$pos) { 759 LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 760 return null; 761 } 762 $match = substr($match, 0, $pos); 763 764 // if this is a empty tag with / at the end we delete it 765 if ($match[strlen($match) - 1] == "/") { 766 $match = substr($match, 0, -1); 767 } 768 769 // Suppress the < 770 if ($match[0] == "<") { 771 $match = substr($match, 1); 772 // closing tag 773 if ($match[0] == "/") { 774 $match = substr($match, 1); 775 } 776 } else { 777 LogUtility::msg("This is not a text tag because it does not start with the character `>`"); 778 } 779 780 // Suppress the tag name (ie until the first blank) 781 $spacePosition = strpos($match, " "); 782 if (!$spacePosition) { 783 // No space, meaning this is only the tag name 784 return $match; 785 } else { 786 return substr($match, 0, $spacePosition); 787 } 788 789 } 790 791 792 public 793 static function getComponentName($tag): string 794 { 795 return strtolower(PluginUtility::PLUGIN_BASE_NAME) . "_" . $tag; 796 } 797 798 public 799 static function addAttributeValue($attribute, $value, array &$attributes) 800 { 801 if (array_key_exists($attribute, $attributes) && $attributes[$attribute] !== "") { 802 $attributes[$attribute] .= " {$value}"; 803 } else { 804 $attributes[$attribute] = "{$value}"; 805 } 806 } 807 808 /** 809 * Plugin Utility is available to all plugin, 810 * this is a convenient way to the the snippet manager 811 * @return SnippetSystem 812 */ 813 public 814 static function getSnippetManager(): SnippetSystem 815 { 816 return SnippetSystem::getFromContext(); 817 } 818 819 820 /** 821 * Function used in a render 822 * @param $data - the data from {@link PluginUtility::handleAndReturnUnmatchedData()} 823 * @return string 824 * 825 * 826 */ 827 public 828 static function renderUnmatched($data): string 829 { 830 /** 831 * Attributes 832 */ 833 $attributes = $data[PluginUtility::ATTRIBUTES] ?? []; 834 $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 835 836 /** 837 * Display 838 */ 839 $display = $tagAttributes->getValueAndRemoveIfPresent(Display::DISPLAY); 840 if ($display === "none") { 841 return ""; 842 } 843 844 $payload = $data[self::PAYLOAD] ?? null; 845 $previousTagDisplayType = $data[self::CONTEXT] ?? null; 846 if ($previousTagDisplayType !== Call::INLINE_DISPLAY) { 847 // Delete the eol at the beginning and end 848 // otherwise we get a big block 849 $payload = ltrim($payload); 850 } 851 return Html::encode($payload); 852 853 } 854 855 public 856 static function renderUnmatchedXml($data) 857 { 858 $payload = $data[self::PAYLOAD]; 859 $previousTagDisplayType = $data[self::CONTEXT]; 860 if ($previousTagDisplayType !== Call::INLINE_DISPLAY) { 861 $payload = ltrim($payload); 862 } 863 return PluginUtility::xmlEncode($payload); 864 865 } 866 867 /** 868 * Function used in a handle function of a syntax plugin for 869 * unmatched context 870 * @param $tagName 871 * @param $match 872 * @param \Doku_Handler $handler 873 * @return array 874 */ 875 public 876 static function handleAndReturnUnmatchedData($tagName, $match, \Doku_Handler $handler): array 877 { 878 $callStack = CallStack::createFromHandler($handler); 879 $sibling = $callStack->previous(); 880 $context = null; 881 if (!empty($sibling)) { 882 $context = $sibling->getDisplay(); 883 } 884 return array( 885 PluginUtility::STATE => DOKU_LEXER_UNMATCHED, 886 PluginUtility::PAYLOAD => $match, 887 PluginUtility::CONTEXT => $context 888 ); 889 } 890 891 /** 892 * Utility methodPreprocess a start tag to be able to extract the name 893 * and the attributes easily 894 * 895 * It will delete: 896 * * the characters <> and the /> if present 897 * * and trim 898 * 899 * It will remain the tagname and its attributes 900 * @param $match 901 * @return false|string|null 902 */ 903 private 904 static function getPreprocessEnterTag($match) 905 { 906// Until the first > 907 $pos = strpos($match, ">"); 908 if (!$pos) { 909 LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_WARNING); 910 return null; 911 } 912 $match = substr($match, 0, $pos); 913 914 915// Trim to start clean 916 $match = trim($match); 917 918// Suppress the < 919 if ($match[0] == "<") { 920 $match = substr($match, 1); 921 } 922 923// Suppress the / for a leaf tag 924 if ($match[strlen($match) - 1] == "/") { 925 $match = substr($match, 0, strlen($match) - 1); 926 } 927 return $match; 928 } 929 930 /** 931 * Retrieve the tag name used in the text document 932 * @param $match 933 * @return false|string|null 934 */ 935 public 936 static function getSyntaxTagNameFromMatch($match) 937 { 938 $preprocessMatch = PluginUtility::getPreprocessEnterTag($match); 939 940// Tag name (ie until the first blank) 941 $spacePosition = strpos($match, " "); 942 if (!$spacePosition) { 943// No space, meaning this is only the tag name 944 return $preprocessMatch; 945 } else { 946 return trim(substr(0, $spacePosition)); 947 } 948 949 } 950 951 /** 952 * Add an enter call to the stack 953 * @param \Doku_Handler $handler 954 * @param $tagName 955 * @param array $callStackArray 956 */ 957 public 958 static function addEnterCall( 959 \Doku_Handler &$handler, 960 $tagName, 961 $callStackArray = array() 962 ) 963 { 964 $pluginName = PluginUtility::getComponentName($tagName); 965 $handler->addPluginCall( 966 $pluginName, 967 $callStackArray, 968 DOKU_LEXER_ENTER, 969 null, 970 null 971 ); 972 } 973 974 /** 975 * Add an end call dynamically 976 * @param \Doku_Handler $handler 977 * @param $tagName 978 * @param array $callStackArray 979 */ 980 public 981 static function addEndCall(\Doku_Handler $handler, $tagName, $callStackArray = array()) 982 { 983 $pluginName = PluginUtility::getComponentName($tagName); 984 $handler->addPluginCall( 985 $pluginName, 986 $callStackArray, 987 DOKU_LEXER_EXIT, 988 null, 989 null 990 ); 991 } 992 993 /** 994 * General Debug 995 */ 996 public 997 static function isDebug() 998 { 999 global $conf; 1000 return $conf["allowdebug"] === 1; 1001 1002 } 1003 1004 1005 /** 1006 * 1007 * See also dev.md file 1008 */ 1009 public static function isDevOrTest() 1010 { 1011 if (self::isDev()) { 1012 return true; 1013 } 1014 return self::isTest(); 1015 } 1016 1017 /** 1018 * Is this a dev environment (ie laptop where the dev is working) 1019 * @return bool 1020 */ 1021 public static function isDev(): bool 1022 { 1023 global $_SERVER; 1024 $remoteAddr = $_SERVER["REMOTE_ADDR"] ?? null; 1025 if ($remoteAddr == "127.0.0.1") { 1026 return true; 1027 } 1028 $computerName = $_SERVER["COMPUTERNAME"] ?? null; 1029 if ($computerName === "NICO") { 1030 return true; 1031 } 1032 return false; 1033 } 1034 1035 public static function getInstructions($markiCode) 1036 { 1037 return p_get_instructions($markiCode); 1038 } 1039 1040 public static function getInstructionsWithoutRoot($markiCode) 1041 { 1042 return MarkupRenderUtility::getInstructionsAndStripPEventually($markiCode); 1043 } 1044 1045 public static function isTest(): bool 1046 { 1047 return defined('DOKU_UNITTEST'); 1048 } 1049 1050 1051 public static function getCacheManager(): CacheManager 1052 { 1053 return CacheManager::getFromContextExecution(); 1054 } 1055 1056 public static function getModeFromPluginName($name) 1057 { 1058 return "plugin_$name"; 1059 } 1060 1061 public static function isCi(): bool 1062 { 1063 // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables 1064 // https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables 1065 return getenv("CI") === "true"; 1066 } 1067 1068 1069 /** 1070 * @throws ExceptionCompile 1071 */ 1072 public static function renderInstructionsToXhtml($callStackHeaderInstructions): ?string 1073 { 1074 return MarkupRenderUtility::renderInstructionsToXhtml($callStackHeaderInstructions); 1075 } 1076 1077 /** 1078 * @deprecated for {@link ExecutionContext::getExecutingWikiId()} 1079 */ 1080 public static function getCurrentSlotId(): string 1081 { 1082 return ExecutionContext::getActualOrCreateFromEnv()->getExecutingWikiId(); 1083 } 1084 1085 1086} 1087 1088PluginUtility::init(); 1089