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]; 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 $value = $COMBO[$name]; 504 } 505 if ($value == null) { 506 return $default; 507 } else { 508 return $value; 509 } 510 511 } 512 513 /** 514 * Create an URL to the documentation website 515 * @param $canonical - canonical id or slug 516 * @param $label - the text of the link 517 * @param bool $withIcon - used to break the recursion with the message in the {@link IconDownloader} 518 * @return string - an url 519 */ 520 public 521 static function getDocumentationHyperLink($canonical, $label, bool $withIcon = true, $tooltip = ""): string 522 { 523 524 $xhtmlIcon = ""; 525 if ($withIcon) { 526 527 $logoPath = WikiPath::createComboResource("images:logo.svg"); 528 try { 529 $fetchImage = FetcherSvg::createSvgFromPath($logoPath); 530 $fetchImage->setRequestedType(FetcherSvg::ICON_TYPE) 531 ->setRequestedWidth(20); 532 $xhtmlIcon = SvgImageLink::createFromFetcher($fetchImage) 533 ->renderMediaTag(); 534 } catch (ExceptionCompile $e) { 535 /** 536 * We don't throw because this function 537 * is also used by: 538 * * the log functionality to show link to the documentation creating a loop 539 * * inside the configuration description crashing the page 540 */ 541 if (PluginUtility::isDevOrTest()) { 542// shows errors in the html only on dev/test 543 $xhtmlIcon = "Error: {$e->getMessage()}"; 544 } 545 } 546 547 } 548 $urlApex = self::$URL_APEX; 549 $path = str_replace(":", "/", $canonical); 550 if (empty($tooltip)) { 551 $title = $label; 552 } else { 553 $title = $tooltip; 554 } 555 $htmlToolTip = ""; 556 if (!empty($tooltip)) { 557 $dataAttributeNamespace = Bootstrap::getDataNamespace(); 558 $htmlToolTip = "data{$dataAttributeNamespace}-toggle=\"tooltip\""; 559 } 560 return "$xhtmlIcon<a href=\"$urlApex/$path\" title=\"$title\" $htmlToolTip style=\"text-decoration:none;\">$label</a>"; 561 } 562 563 /** 564 * An utility function to not search every time which array should be first 565 * @param array $inlineAttributes - the component inline attributes 566 * @param array $defaultAttributes - the default configuration attributes 567 * @return array - a merged array 568 */ 569 public 570 static function mergeAttributes(array $inlineAttributes, array $defaultAttributes = array()) 571 { 572 return array_merge($defaultAttributes, $inlineAttributes); 573 } 574 575 /** 576 * A pattern for a container tag 577 * that needs to catch the content 578 * 579 * Use as a special pattern (substition) 580 * 581 * The {@link \syntax_plugin_combo_math} use it 582 * @param $tag 583 * @return string - a pattern 584 */ 585 public 586 static function getLeafContainerTagPattern($tag) 587 { 588 return '<' . $tag . '.*?>.*?<\/' . $tag . '>'; 589 } 590 591 /** 592 * Return the content of a tag 593 * 594 * <math>Content</math> 595 * @param $match 596 * @return string the content 597 */ 598 public 599 static function getTagContent($match) 600 { 601// From the first > 602 $start = strpos($match, ">"); 603 if ($start == false) { 604 LogUtility::msg("The match does not contain any opening tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 605 return ""; 606 } 607 $match = substr($match, $start + 1); 608// If this is the last character, we get a false 609 if ($match == false) { 610 LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 611 return ""; 612 } 613 614 $end = strrpos($match, "</"); 615 if ($end == false) { 616 LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 617 return ""; 618 } 619 620 return substr($match, 0, $end); 621 } 622 623 /** 624 * 625 * Check if a HTML tag was already added for a request 626 * The request id is just the timestamp 627 * An indicator array should be provided 628 * @return string 629 */ 630 public 631 static function getRequestId() 632 { 633 634 if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { 635// since php 5.4 636 $requestTime = $_SERVER['REQUEST_TIME_FLOAT']; 637 } else { 638// DokuWiki test framework use this 639 $requestTime = $_SERVER['REQUEST_TIME']; 640 } 641 $keyPrefix = 'combo_'; 642 643 global $ID; 644 return $keyPrefix . hash('crc32b', $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . $requestTime . $ID); 645 646 } 647 648 /** 649 * 650 * Return the requested wiki id (known also as page id) 651 * 652 * If the code is rendering a sidebar, it will not return the id of the sidebar 653 * but the requested wiki id 654 * 655 * @return string 656 * @throws ExceptionNotFound 657 * @deprecated use {@link ExecutionContext::getRequestedPath()} 658 */ 659 public static function getRequestedWikiId(): string 660 { 661 662 return ExecutionContext::getActualOrCreateFromEnv()->getRequestedPath()->getWikiId(); 663 664 } 665 666 public static function xmlEncode($text) 667 { 668 /** 669 * {@link htmlentities } 670 */ 671 return htmlentities($text, ENT_XML1); 672 } 673 674 675 /** 676 * Add a class 677 * @param $classValue 678 * @param array $attributes 679 */ 680 public 681 static function addClass2Attributes($classValue, array &$attributes) 682 { 683 self::addAttributeValue("class", $classValue, $attributes); 684 } 685 686 /** 687 * Add a style property to the attributes 688 * @param $property 689 * @param $value 690 * @param array $attributes 691 * @deprecated use {@link TagAttributes::addStyleDeclarationIfNotSet()} instead 692 */ 693 public 694 static function addStyleProperty($property, $value, array &$attributes) 695 { 696 if (isset($attributes["style"])) { 697 $attributes["style"] .= ";$property:$value"; 698 } else { 699 $attributes["style"] = "$property:$value"; 700 } 701 702 } 703 704 /** 705 * Add default border attributes 706 * to see a border 707 * Doc 708 * https://combostrap.com/styling/color#border_color 709 * @param TagAttributes $tagAttributes 710 */ 711 private 712 static function checkDefaultBorderColorAttributes(&$tagAttributes) 713 { 714 /** 715 * border color was set without the width 716 * setting the width 717 */ 718 if (!( 719 $tagAttributes->hasStyleDeclaration("border") 720 || 721 $tagAttributes->hasStyleDeclaration("border-width") 722 ) 723 ) { 724 $tagAttributes->addStyleDeclarationIfNotSet("border-width", "1px"); 725 } 726 /** 727 * border color was set without the style 728 * setting the style 729 */ 730 if (! 731 ( 732 $tagAttributes->hasStyleDeclaration("border") 733 || 734 $tagAttributes->hasStyleDeclaration("border-style") 735 ) 736 ) { 737 $tagAttributes->addStyleDeclarationIfNotSet("border-style", "solid"); 738 739 } 740 if (!$tagAttributes->hasStyleDeclaration("border-radius")) { 741 $tagAttributes->addStyleDeclarationIfNotSet("border-radius", ".25rem"); 742 } 743 744 } 745 746 /** 747 * @param $match 748 * @return null|string - return the tag name or null if not found 749 */ 750 public 751 static function getMarkupTag($match): ?string 752 { 753 754 // Until the first > 755 $pos = strpos($match, ">"); 756 if (!$pos) { 757 LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 758 return null; 759 } 760 $match = substr($match, 0, $pos); 761 762 // if this is a empty tag with / at the end we delete it 763 if ($match[strlen($match) - 1] == "/") { 764 $match = substr($match, 0, -1); 765 } 766 767 // Suppress the < 768 if ($match[0] == "<") { 769 $match = substr($match, 1); 770 // closing tag 771 if ($match[0] == "/") { 772 $match = substr($match, 1); 773 } 774 } else { 775 LogUtility::msg("This is not a text tag because it does not start with the character `>`"); 776 } 777 778 // Suppress the tag name (ie until the first blank) 779 $spacePosition = strpos($match, " "); 780 if (!$spacePosition) { 781 // No space, meaning this is only the tag name 782 return $match; 783 } else { 784 return substr($match, 0, $spacePosition); 785 } 786 787 } 788 789 790 public 791 static function getComponentName($tag): string 792 { 793 return strtolower(PluginUtility::PLUGIN_BASE_NAME) . "_" . $tag; 794 } 795 796 public 797 static function addAttributeValue($attribute, $value, array &$attributes) 798 { 799 if (array_key_exists($attribute, $attributes) && $attributes[$attribute] !== "") { 800 $attributes[$attribute] .= " {$value}"; 801 } else { 802 $attributes[$attribute] = "{$value}"; 803 } 804 } 805 806 /** 807 * Plugin Utility is available to all plugin, 808 * this is a convenient way to the the snippet manager 809 * @return SnippetSystem 810 */ 811 public 812 static function getSnippetManager(): SnippetSystem 813 { 814 return SnippetSystem::getFromContext(); 815 } 816 817 818 /** 819 * Function used in a render 820 * @param $data - the data from {@link PluginUtility::handleAndReturnUnmatchedData()} 821 * @return string 822 * 823 * 824 */ 825 public 826 static function renderUnmatched($data): string 827 { 828 /** 829 * Attributes 830 */ 831 $attributes = $data[PluginUtility::ATTRIBUTES] ?? []; 832 $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 833 834 /** 835 * Display 836 */ 837 $display = $tagAttributes->getValueAndRemoveIfPresent(Display::DISPLAY); 838 if ($display === "none") { 839 return ""; 840 } 841 842 $payload = $data[self::PAYLOAD]; 843 $previousTagDisplayType = $data[self::CONTEXT]; 844 if ($previousTagDisplayType !== Call::INLINE_DISPLAY) { 845 // Delete the eol at the beginning and end 846 // otherwise we get a big block 847 $payload = ltrim($payload); 848 } 849 return Html::encode($payload); 850 851 } 852 853 public 854 static function renderUnmatchedXml($data) 855 { 856 $payload = $data[self::PAYLOAD]; 857 $previousTagDisplayType = $data[self::CONTEXT]; 858 if ($previousTagDisplayType !== Call::INLINE_DISPLAY) { 859 $payload = ltrim($payload); 860 } 861 return PluginUtility::xmlEncode($payload); 862 863 } 864 865 /** 866 * Function used in a handle function of a syntax plugin for 867 * unmatched context 868 * @param $tagName 869 * @param $match 870 * @param \Doku_Handler $handler 871 * @return array 872 */ 873 public 874 static function handleAndReturnUnmatchedData($tagName, $match, \Doku_Handler $handler): array 875 { 876 $callStack = CallStack::createFromHandler($handler); 877 $sibling = $callStack->previous(); 878 $context = null; 879 if (!empty($sibling)) { 880 $context = $sibling->getDisplay(); 881 } 882 return array( 883 PluginUtility::STATE => DOKU_LEXER_UNMATCHED, 884 PluginUtility::PAYLOAD => $match, 885 PluginUtility::CONTEXT => $context 886 ); 887 } 888 889 /** 890 * Utility methodPreprocess a start tag to be able to extract the name 891 * and the attributes easily 892 * 893 * It will delete: 894 * * the characters <> and the /> if present 895 * * and trim 896 * 897 * It will remain the tagname and its attributes 898 * @param $match 899 * @return false|string|null 900 */ 901 private 902 static function getPreprocessEnterTag($match) 903 { 904// Until the first > 905 $pos = strpos($match, ">"); 906 if (!$pos) { 907 LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_WARNING); 908 return null; 909 } 910 $match = substr($match, 0, $pos); 911 912 913// Trim to start clean 914 $match = trim($match); 915 916// Suppress the < 917 if ($match[0] == "<") { 918 $match = substr($match, 1); 919 } 920 921// Suppress the / for a leaf tag 922 if ($match[strlen($match) - 1] == "/") { 923 $match = substr($match, 0, strlen($match) - 1); 924 } 925 return $match; 926 } 927 928 /** 929 * Retrieve the tag name used in the text document 930 * @param $match 931 * @return false|string|null 932 */ 933 public 934 static function getSyntaxTagNameFromMatch($match) 935 { 936 $preprocessMatch = PluginUtility::getPreprocessEnterTag($match); 937 938// Tag name (ie until the first blank) 939 $spacePosition = strpos($match, " "); 940 if (!$spacePosition) { 941// No space, meaning this is only the tag name 942 return $preprocessMatch; 943 } else { 944 return trim(substr(0, $spacePosition)); 945 } 946 947 } 948 949 /** 950 * Add an enter call to the stack 951 * @param \Doku_Handler $handler 952 * @param $tagName 953 * @param array $callStackArray 954 */ 955 public 956 static function addEnterCall( 957 \Doku_Handler &$handler, 958 $tagName, 959 $callStackArray = array() 960 ) 961 { 962 $pluginName = PluginUtility::getComponentName($tagName); 963 $handler->addPluginCall( 964 $pluginName, 965 $callStackArray, 966 DOKU_LEXER_ENTER, 967 null, 968 null 969 ); 970 } 971 972 /** 973 * Add an end call dynamically 974 * @param \Doku_Handler $handler 975 * @param $tagName 976 * @param array $callStackArray 977 */ 978 public 979 static function addEndCall(\Doku_Handler $handler, $tagName, $callStackArray = array()) 980 { 981 $pluginName = PluginUtility::getComponentName($tagName); 982 $handler->addPluginCall( 983 $pluginName, 984 $callStackArray, 985 DOKU_LEXER_EXIT, 986 null, 987 null 988 ); 989 } 990 991 /** 992 * General Debug 993 */ 994 public 995 static function isDebug() 996 { 997 global $conf; 998 return $conf["allowdebug"] === 1; 999 1000 } 1001 1002 1003 /** 1004 * 1005 * See also dev.md file 1006 */ 1007 public static function isDevOrTest() 1008 { 1009 if (self::isDev()) { 1010 return true; 1011 } 1012 return self::isTest(); 1013 } 1014 1015 /** 1016 * Is this a dev environment (ie laptop where the dev is working) 1017 * @return bool 1018 */ 1019 public static function isDev(): bool 1020 { 1021 global $_SERVER; 1022 if ($_SERVER["REMOTE_ADDR"] == "127.0.0.1") { 1023 return true; 1024 } 1025 if ($_SERVER["COMPUTERNAME"] === "NICO") { 1026 return true; 1027 } 1028 return false; 1029 } 1030 1031 public static function getInstructions($markiCode) 1032 { 1033 return p_get_instructions($markiCode); 1034 } 1035 1036 public static function getInstructionsWithoutRoot($markiCode) 1037 { 1038 return MarkupRenderUtility::getInstructionsAndStripPEventually($markiCode); 1039 } 1040 1041 public static function isTest(): bool 1042 { 1043 return defined('DOKU_UNITTEST'); 1044 } 1045 1046 1047 public static function getCacheManager(): CacheManager 1048 { 1049 return CacheManager::getFromContextExecution(); 1050 } 1051 1052 public static function getModeFromPluginName($name) 1053 { 1054 return "plugin_$name"; 1055 } 1056 1057 public static function isCi(): bool 1058 { 1059 // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables 1060 // https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables 1061 return getenv("CI") === "true"; 1062 } 1063 1064 1065 /** 1066 * @throws ExceptionCompile 1067 */ 1068 public static function renderInstructionsToXhtml($callStackHeaderInstructions): ?string 1069 { 1070 return MarkupRenderUtility::renderInstructionsToXhtml($callStackHeaderInstructions); 1071 } 1072 1073 /** 1074 * @deprecated for {@link ExecutionContext::getExecutingWikiId()} 1075 */ 1076 public static function getCurrentSlotId(): string 1077 { 1078 return ExecutionContext::getActualOrCreateFromEnv()->getExecutingWikiId(); 1079 } 1080 1081 1082} 1083 1084PluginUtility::init(); 1085