1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeau 4*37748cd8SNickeaunamespace ComboStrap; 5*37748cd8SNickeau 6*37748cd8SNickeau 7*37748cd8SNickeauuse dokuwiki\Extension\Plugin; 8*37748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin; 9*37748cd8SNickeau 10*37748cd8SNickeaurequire_once(__DIR__ . '/../vendor/autoload.php'); 11*37748cd8SNickeau 12*37748cd8SNickeau/** 13*37748cd8SNickeau * Plugin Utility is added in all Dokuwiki extension 14*37748cd8SNickeau * and 15*37748cd8SNickeau * all classes are added in plugin utility 16*37748cd8SNickeau * 17*37748cd8SNickeau * This is an utility master and the class loader 18*37748cd8SNickeau * 19*37748cd8SNickeau * If the load is relative, the load path is used 20*37748cd8SNickeau * and the bad php file may be loaded 21*37748cd8SNickeau * Furthermore, the absolute path helps 22*37748cd8SNickeau * the IDE when refactoring 23*37748cd8SNickeau */ 24*37748cd8SNickeaurequire_once(__DIR__ . '/AdsUtility.php'); 25*37748cd8SNickeaurequire_once(__DIR__ . '/Align.php'); 26*37748cd8SNickeaurequire_once(__DIR__ . '/Analytics.php'); 27*37748cd8SNickeaurequire_once(__DIR__ . '/AnalyticsMenuItem.php'); 28*37748cd8SNickeaurequire_once(__DIR__ . '/Animation.php'); 29*37748cd8SNickeaurequire_once(__DIR__ . '/ArrayCaseInsensitive.php'); 30*37748cd8SNickeaurequire_once(__DIR__ . '/ArrayUtility.php'); 31*37748cd8SNickeaurequire_once(__DIR__ . '/Background.php'); 32*37748cd8SNickeaurequire_once(__DIR__ . '/Boldness.php'); 33*37748cd8SNickeaurequire_once(__DIR__ . '/Bootstrap.php'); 34*37748cd8SNickeaurequire_once(__DIR__ . '/BreadcrumbHierarchical.php'); 35*37748cd8SNickeaurequire_once(__DIR__ . '/CacheByLogicalKey.php'); 36*37748cd8SNickeaurequire_once(__DIR__ . '/CacheInstructionsByLogicalKey.php'); 37*37748cd8SNickeaurequire_once(__DIR__ . '/CacheManager.php'); 38*37748cd8SNickeaurequire_once(__DIR__ . '/CacheMedia.php'); 39*37748cd8SNickeaurequire_once(__DIR__ . '/Call.php'); 40*37748cd8SNickeaurequire_once(__DIR__ . '/CallStack.php'); 41*37748cd8SNickeaurequire_once(__DIR__ . '/ColorUtility.php'); 42*37748cd8SNickeaurequire_once(__DIR__ . '/ConditionalValue.php'); 43*37748cd8SNickeaurequire_once(__DIR__ . '/ConfUtility.php'); 44*37748cd8SNickeaurequire_once(__DIR__ . '/Dimension.php'); 45*37748cd8SNickeaurequire_once(__DIR__ . '/DokuPath.php'); 46*37748cd8SNickeaurequire_once(__DIR__ . '/DokuwikiUrl.php'); 47*37748cd8SNickeaurequire_once(__DIR__ . '/File.php'); 48*37748cd8SNickeaurequire_once(__DIR__ . '/FloatAttribute.php'); 49*37748cd8SNickeaurequire_once(__DIR__ . '/FontSize.php'); 50*37748cd8SNickeaurequire_once(__DIR__ . '/FsWikiUtility.php'); 51*37748cd8SNickeaurequire_once(__DIR__ . '/HeaderUtility.php'); 52*37748cd8SNickeaurequire_once(__DIR__ . '/HistoricalBreadcrumbMenuItem.php'); 53*37748cd8SNickeaurequire_once(__DIR__ . '/Hover.php'); 54*37748cd8SNickeaurequire_once(__DIR__ . '/Http.php'); 55*37748cd8SNickeaurequire_once(__DIR__ . '/Icon.php'); 56*37748cd8SNickeaurequire_once(__DIR__ . '/Identity.php'); 57*37748cd8SNickeaurequire_once(__DIR__ . '/Iso8601Date.php'); 58*37748cd8SNickeaurequire_once(__DIR__ . '/Lang.php'); 59*37748cd8SNickeaurequire_once(__DIR__ . '/LineSpacing.php'); 60*37748cd8SNickeaurequire_once(__DIR__ . '/LogUtility.php'); 61*37748cd8SNickeaurequire_once(__DIR__ . '/LowQualityPage.php'); 62*37748cd8SNickeaurequire_once(__DIR__ . '/MediaLink.php'); 63*37748cd8SNickeaurequire_once(__DIR__ . '/Message.php'); 64*37748cd8SNickeaurequire_once(__DIR__ . '/MetadataUtility.php'); 65*37748cd8SNickeaurequire_once(__DIR__ . '/NavBarUtility.php'); 66*37748cd8SNickeaurequire_once(__DIR__ . '/Opacity.php'); 67*37748cd8SNickeaurequire_once(__DIR__ . '/Page.php'); 68*37748cd8SNickeaurequire_once(__DIR__ . '/PageProtection.php'); 69*37748cd8SNickeaurequire_once(__DIR__ . '/PageRules.php'); 70*37748cd8SNickeaurequire_once(__DIR__ . '/PageSql.php'); 71*37748cd8SNickeaurequire_once(__DIR__ . '/PageSqlParser/PageSqlLexer.php'); 72*37748cd8SNickeaurequire_once(__DIR__ . '/PageSqlParser/PageSqlParser.php'); 73*37748cd8SNickeaurequire_once(__DIR__ . '/PageSqlTreeListener.php'); 74*37748cd8SNickeaurequire_once(__DIR__ . '/PagesIndex.php'); 75*37748cd8SNickeaurequire_once(__DIR__ . '/PipelineUtility.php'); 76*37748cd8SNickeaurequire_once(__DIR__ . '/Position.php'); 77*37748cd8SNickeaurequire_once(__DIR__ . '/Prism.php'); 78*37748cd8SNickeaurequire_once(__DIR__ . '/Publication.php'); 79*37748cd8SNickeaurequire_once(__DIR__ . '/RasterImageLink.php'); 80*37748cd8SNickeaurequire_once(__DIR__ . '/RenderUtility.php'); 81*37748cd8SNickeaurequire_once(__DIR__ . '/Resources.php'); 82*37748cd8SNickeaurequire_once(__DIR__ . '/Shadow.php'); 83*37748cd8SNickeaurequire_once(__DIR__ . '/Site.php'); 84*37748cd8SNickeaurequire_once(__DIR__ . '/Skin.php'); 85*37748cd8SNickeaurequire_once(__DIR__ . '/Snippet.php'); 86*37748cd8SNickeaurequire_once(__DIR__ . '/SnippetManager.php'); 87*37748cd8SNickeaurequire_once(__DIR__ . '/Spacing.php'); 88*37748cd8SNickeaurequire_once(__DIR__ . '/Sqlite.php'); 89*37748cd8SNickeaurequire_once(__DIR__ . '/StringUtility.php'); 90*37748cd8SNickeaurequire_once(__DIR__ . '/StyleUtility.php'); 91*37748cd8SNickeaurequire_once(__DIR__ . '/SvgDocument.php'); 92*37748cd8SNickeaurequire_once(__DIR__ . '/SvgImageLink.php'); 93*37748cd8SNickeaurequire_once(__DIR__ . '/Syntax.php'); 94*37748cd8SNickeaurequire_once(__DIR__ . '/TableUtility.php'); 95*37748cd8SNickeaurequire_once(__DIR__ . '/Tag.php'); 96*37748cd8SNickeaurequire_once(__DIR__ . '/TagAttributes.php'); 97*37748cd8SNickeaurequire_once(__DIR__ . '/Template.php'); 98*37748cd8SNickeaurequire_once(__DIR__ . '/TemplateUtility.php'); 99*37748cd8SNickeaurequire_once(__DIR__ . '/TextAlign.php'); 100*37748cd8SNickeaurequire_once(__DIR__ . '/TextColor.php'); 101*37748cd8SNickeaurequire_once(__DIR__ . '/ThirdMediaLink.php'); 102*37748cd8SNickeaurequire_once(__DIR__ . '/ThirdPartyPlugins.php'); 103*37748cd8SNickeaurequire_once(__DIR__ . '/TocUtility.php'); 104*37748cd8SNickeaurequire_once(__DIR__ . '/Toggle.php'); 105*37748cd8SNickeaurequire_once(__DIR__ . '/Underline.php'); 106*37748cd8SNickeaurequire_once(__DIR__ . '/Unit.php'); 107*37748cd8SNickeaurequire_once(__DIR__ . '/UrlManagerBestEndPage.php'); 108*37748cd8SNickeaurequire_once(__DIR__ . '/UrlUtility.php'); 109*37748cd8SNickeaurequire_once(__DIR__ . '/XhtmlUtility.php'); 110*37748cd8SNickeaurequire_once(__DIR__ . '/XmlDocument.php'); 111*37748cd8SNickeaurequire_once(__DIR__ . '/XmlUtility.php'); 112*37748cd8SNickeau 113*37748cd8SNickeau 114*37748cd8SNickeau/** 115*37748cd8SNickeau * Class url static 116*37748cd8SNickeau * List of static utilities 117*37748cd8SNickeau */ 118*37748cd8SNickeauclass PluginUtility 119*37748cd8SNickeau{ 120*37748cd8SNickeau 121*37748cd8SNickeau const DOKU_DATA_DIR = '/dokudata/pages'; 122*37748cd8SNickeau const DOKU_CACHE_DIR = '/dokudata/cache'; 123*37748cd8SNickeau 124*37748cd8SNickeau /** 125*37748cd8SNickeau * Key in the data array between the handle and render function 126*37748cd8SNickeau */ 127*37748cd8SNickeau const STATE = "state"; 128*37748cd8SNickeau const PAYLOAD = "payload"; // The html or text 129*37748cd8SNickeau const ATTRIBUTES = "attributes"; 130*37748cd8SNickeau // The context is generally the parent tag but it may be also the grandfather. 131*37748cd8SNickeau // It permits to determine the HTML that is outputted 132*37748cd8SNickeau const CONTEXT = 'context'; 133*37748cd8SNickeau const TAG = "tag"; 134*37748cd8SNickeau 135*37748cd8SNickeau /** 136*37748cd8SNickeau * The name of the hidden/private namespace 137*37748cd8SNickeau * where the icon and other artifactory are stored 138*37748cd8SNickeau */ 139*37748cd8SNickeau const COMBOSTRAP_NAMESPACE_NAME = "combostrap"; 140*37748cd8SNickeau 141*37748cd8SNickeau const PARENT = "parent"; 142*37748cd8SNickeau const POSITION = "position"; 143*37748cd8SNickeau 144*37748cd8SNickeau /** 145*37748cd8SNickeau * Class to center an element 146*37748cd8SNickeau */ 147*37748cd8SNickeau const CENTER_CLASS = "mx-auto"; 148*37748cd8SNickeau 149*37748cd8SNickeau 150*37748cd8SNickeau const EDIT_SECTION_TARGET = 'section'; 151*37748cd8SNickeau const ERROR_MESSAGE = "errorAtt"; 152*37748cd8SNickeau 153*37748cd8SNickeau /** 154*37748cd8SNickeau * The URL base of the documentation 155*37748cd8SNickeau */ 156*37748cd8SNickeau static $URL_BASE; 157*37748cd8SNickeau 158*37748cd8SNickeau 159*37748cd8SNickeau /** 160*37748cd8SNickeau * @var string - the plugin base name (ie the directory) 161*37748cd8SNickeau * ie $INFO_PLUGIN['base']; 162*37748cd8SNickeau * This is a constant because it permits code analytics 163*37748cd8SNickeau * such as verification of a path 164*37748cd8SNickeau */ 165*37748cd8SNickeau const PLUGIN_BASE_NAME = "combo"; 166*37748cd8SNickeau 167*37748cd8SNickeau /** 168*37748cd8SNickeau * The name of the template plugin 169*37748cd8SNickeau */ 170*37748cd8SNickeau const TEMPLATE_STRAP_NAME = "strap"; 171*37748cd8SNickeau 172*37748cd8SNickeau /** 173*37748cd8SNickeau * @var array 174*37748cd8SNickeau */ 175*37748cd8SNickeau static $INFO_PLUGIN; 176*37748cd8SNickeau 177*37748cd8SNickeau static $PLUGIN_LANG; 178*37748cd8SNickeau 179*37748cd8SNickeau /** 180*37748cd8SNickeau * The plugin name 181*37748cd8SNickeau * (not the same than the base as it's not related to the directory 182*37748cd8SNickeau * @var string 183*37748cd8SNickeau */ 184*37748cd8SNickeau public static $PLUGIN_NAME; 185*37748cd8SNickeau /** 186*37748cd8SNickeau * @var mixed the version 187*37748cd8SNickeau */ 188*37748cd8SNickeau private static $VERSION; 189*37748cd8SNickeau 190*37748cd8SNickeau 191*37748cd8SNickeau /** 192*37748cd8SNickeau * Initiate the static variable 193*37748cd8SNickeau * See the call after this class 194*37748cd8SNickeau */ 195*37748cd8SNickeau static function init() 196*37748cd8SNickeau { 197*37748cd8SNickeau 198*37748cd8SNickeau $pluginInfoFile = __DIR__ . '/../plugin.info.txt'; 199*37748cd8SNickeau self::$INFO_PLUGIN = confToHash($pluginInfoFile); 200*37748cd8SNickeau self::$PLUGIN_NAME = 'ComboStrap'; 201*37748cd8SNickeau global $lang; 202*37748cd8SNickeau self::$PLUGIN_LANG = $lang[self::PLUGIN_BASE_NAME]; 203*37748cd8SNickeau self::$URL_BASE = "https://" . parse_url(self::$INFO_PLUGIN['url'], PHP_URL_HOST); 204*37748cd8SNickeau self::$VERSION = self::$INFO_PLUGIN['version']; 205*37748cd8SNickeau 206*37748cd8SNickeau PluginUtility::initStaticManager(); 207*37748cd8SNickeau 208*37748cd8SNickeau } 209*37748cd8SNickeau 210*37748cd8SNickeau /** 211*37748cd8SNickeau * @param $inputExpression 212*37748cd8SNickeau * @return false|int 1|0 213*37748cd8SNickeau * returns: 214*37748cd8SNickeau * - 1 if the input expression is a pattern, 215*37748cd8SNickeau * - 0 if not, 216*37748cd8SNickeau * - FALSE if an error occurred. 217*37748cd8SNickeau */ 218*37748cd8SNickeau static function isRegularExpression($inputExpression) 219*37748cd8SNickeau { 220*37748cd8SNickeau 221*37748cd8SNickeau $regularExpressionPattern = "/(\\/.*\\/[gmixXsuUAJ]?)/"; 222*37748cd8SNickeau return preg_match($regularExpressionPattern, $inputExpression); 223*37748cd8SNickeau 224*37748cd8SNickeau } 225*37748cd8SNickeau 226*37748cd8SNickeau /** 227*37748cd8SNickeau * Return a mode from a tag (ie from a {@link Plugin::getPluginComponent()} 228*37748cd8SNickeau * @param $tag 229*37748cd8SNickeau * @return string 230*37748cd8SNickeau * 231*37748cd8SNickeau * A mode is just a name for a class 232*37748cd8SNickeau * Example: $Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock()); 233*37748cd8SNickeau */ 234*37748cd8SNickeau public static function getModeFromTag($tag) 235*37748cd8SNickeau { 236*37748cd8SNickeau return "plugin_" . self::getComponentName($tag); 237*37748cd8SNickeau } 238*37748cd8SNickeau 239*37748cd8SNickeau /** 240*37748cd8SNickeau * @param $tag 241*37748cd8SNickeau * @return string 242*37748cd8SNickeau * 243*37748cd8SNickeau * Create a lookahead pattern for a container tag used to enter in a mode 244*37748cd8SNickeau */ 245*37748cd8SNickeau public static function getContainerTagPattern($tag) 246*37748cd8SNickeau { 247*37748cd8SNickeau // this pattern ensure that the tag 248*37748cd8SNickeau // `accordion` will not intercept also the tag `accordionitem` 249*37748cd8SNickeau // where: 250*37748cd8SNickeau // ?: means non capturing group (to not capture the last >) 251*37748cd8SNickeau // (\s.*?): is a capturing group that starts with a space 252*37748cd8SNickeau $pattern = "(?:\s.*?>|>)"; 253*37748cd8SNickeau return '<' . $tag . $pattern . '(?=.*?<\/' . $tag . '>)'; 254*37748cd8SNickeau } 255*37748cd8SNickeau 256*37748cd8SNickeau /** 257*37748cd8SNickeau * This pattern allows space after the tag name 258*37748cd8SNickeau * for an end tag 259*37748cd8SNickeau * As XHTML (https://www.w3.org/TR/REC-xml/#dt-etag) 260*37748cd8SNickeau * @param $tag 261*37748cd8SNickeau * @return string 262*37748cd8SNickeau */ 263*37748cd8SNickeau public static function getEndTagPattern($tag) 264*37748cd8SNickeau { 265*37748cd8SNickeau return "</$tag\s*>"; 266*37748cd8SNickeau } 267*37748cd8SNickeau 268*37748cd8SNickeau /** 269*37748cd8SNickeau * @param $tag 270*37748cd8SNickeau * @return string 271*37748cd8SNickeau * 272*37748cd8SNickeau * Create a open tag pattern without lookahead. 273*37748cd8SNickeau * Used for https://dev.w3.org/html5/html-author/#void-elements-0 274*37748cd8SNickeau */ 275*37748cd8SNickeau public static function getVoidElementTagPattern($tag) 276*37748cd8SNickeau { 277*37748cd8SNickeau return '<' . $tag . '.*?>'; 278*37748cd8SNickeau } 279*37748cd8SNickeau 280*37748cd8SNickeau 281*37748cd8SNickeau /** 282*37748cd8SNickeau * Take an array where the key is the attribute name 283*37748cd8SNickeau * and return a HTML tag string 284*37748cd8SNickeau * 285*37748cd8SNickeau * The attribute name and value are escaped 286*37748cd8SNickeau * 287*37748cd8SNickeau * @param $attributes - combo attributes 288*37748cd8SNickeau * @return string 289*37748cd8SNickeau * @deprecated to allowed background and other metadata, use {@link TagAttributes::toHtmlEnterTag()} 290*37748cd8SNickeau */ 291*37748cd8SNickeau public static function array2HTMLAttributesAsString($attributes) 292*37748cd8SNickeau { 293*37748cd8SNickeau 294*37748cd8SNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 295*37748cd8SNickeau return $tagAttributes->toHTMLAttributeString(); 296*37748cd8SNickeau 297*37748cd8SNickeau } 298*37748cd8SNickeau 299*37748cd8SNickeau /** 300*37748cd8SNickeau * 301*37748cd8SNickeau * Parse the attributes part of a match 302*37748cd8SNickeau * 303*37748cd8SNickeau * Example: 304*37748cd8SNickeau * line-numbers="value" 305*37748cd8SNickeau * line-numbers='value' 306*37748cd8SNickeau * 307*37748cd8SNickeau * This value may be in: 308*37748cd8SNickeau * * configuration value 309*37748cd8SNickeau * * as well as in the match of a {@link SyntaxPlugin} 310*37748cd8SNickeau * 311*37748cd8SNickeau * @param $string 312*37748cd8SNickeau * @return array 313*37748cd8SNickeau * 314*37748cd8SNickeau * To parse a match, use {@link PluginUtility::getTagAttributes()} 315*37748cd8SNickeau * 316*37748cd8SNickeau * 317*37748cd8SNickeau */ 318*37748cd8SNickeau public static function parseAttributes($string) 319*37748cd8SNickeau { 320*37748cd8SNickeau 321*37748cd8SNickeau $parameters = array(); 322*37748cd8SNickeau 323*37748cd8SNickeau // Rules 324*37748cd8SNickeau // * name may be alone (ie true boolean attribute) 325*37748cd8SNickeau // * a name may get a `-` 326*37748cd8SNickeau // * there may be space every everywhere when the value is enclosed with a quote 327*37748cd8SNickeau // * there may be no space in the value and between the equal sign when the value is not enclosed 328*37748cd8SNickeau // 329*37748cd8SNickeau // /i not case sensitive 330*37748cd8SNickeau $attributePattern = '\s*([-\w]+)\s*(?:=(\s*[\'"]([^`"]*)[\'"]\s*|[^\s]*))?'; 331*37748cd8SNickeau $result = preg_match_all('/' . $attributePattern . '/i', $string, $matches); 332*37748cd8SNickeau if ($result != 0) { 333*37748cd8SNickeau foreach ($matches[1] as $key => $parameterKey) { 334*37748cd8SNickeau 335*37748cd8SNickeau // group 3 (ie the value between quotes) 336*37748cd8SNickeau $value = $matches[3][$key]; 337*37748cd8SNickeau if ($value == "") { 338*37748cd8SNickeau // check the value without quotes 339*37748cd8SNickeau $value = $matches[2][$key]; 340*37748cd8SNickeau } 341*37748cd8SNickeau // if there is no value, this is a boolean 342*37748cd8SNickeau if ($value == "") { 343*37748cd8SNickeau $value = true; 344*37748cd8SNickeau } else { 345*37748cd8SNickeau $value = hsc($value); 346*37748cd8SNickeau } 347*37748cd8SNickeau $parameters[hsc(strtolower($parameterKey))] = $value; 348*37748cd8SNickeau } 349*37748cd8SNickeau } 350*37748cd8SNickeau return $parameters; 351*37748cd8SNickeau 352*37748cd8SNickeau } 353*37748cd8SNickeau 354*37748cd8SNickeau public static function getTagAttributes($match) 355*37748cd8SNickeau { 356*37748cd8SNickeau return self::getQualifiedTagAttributes($match, false, ""); 357*37748cd8SNickeau } 358*37748cd8SNickeau 359*37748cd8SNickeau /** 360*37748cd8SNickeau * Return the attribute of a tag 361*37748cd8SNickeau * Because they are users input, they are all escaped 362*37748cd8SNickeau * @param $match 363*37748cd8SNickeau * @param $hasThirdValue - if true, the third parameter is treated as value, not a property and returned in the `third` key 364*37748cd8SNickeau * use for the code/file/console where they accept a name as third value 365*37748cd8SNickeau * @param $keyThirdArgument - if a third argument is found, return it with this key 366*37748cd8SNickeau * @return array 367*37748cd8SNickeau */ 368*37748cd8SNickeau public static function getQualifiedTagAttributes($match, $hasThirdValue, $keyThirdArgument) 369*37748cd8SNickeau { 370*37748cd8SNickeau 371*37748cd8SNickeau $match = PluginUtility::getPreprocessEnterTag($match); 372*37748cd8SNickeau 373*37748cd8SNickeau // Suppress the tag name (ie until the first blank) 374*37748cd8SNickeau $spacePosition = strpos($match, " "); 375*37748cd8SNickeau if (!$spacePosition) { 376*37748cd8SNickeau // No space, meaning this is only the tag name 377*37748cd8SNickeau return array(); 378*37748cd8SNickeau } 379*37748cd8SNickeau $match = trim(substr($match, $spacePosition)); 380*37748cd8SNickeau if ($match == "") { 381*37748cd8SNickeau return array(); 382*37748cd8SNickeau } 383*37748cd8SNickeau 384*37748cd8SNickeau // Do we have a type as first argument ? 385*37748cd8SNickeau $attributes = array(); 386*37748cd8SNickeau $spacePosition = strpos($match, " "); 387*37748cd8SNickeau if ($spacePosition) { 388*37748cd8SNickeau $nextArgument = substr($match, 0, $spacePosition); 389*37748cd8SNickeau } else { 390*37748cd8SNickeau $nextArgument = $match; 391*37748cd8SNickeau } 392*37748cd8SNickeau if (!strpos($nextArgument, "=")) { 393*37748cd8SNickeau $attributes["type"] = $nextArgument; 394*37748cd8SNickeau // Suppress the type 395*37748cd8SNickeau $match = substr($match, strlen($nextArgument)); 396*37748cd8SNickeau $match = trim($match); 397*37748cd8SNickeau 398*37748cd8SNickeau // Do we have a value as first argument ? 399*37748cd8SNickeau if (!empty($hasThirdValue)) { 400*37748cd8SNickeau $spacePosition = strpos($match, " "); 401*37748cd8SNickeau if ($spacePosition) { 402*37748cd8SNickeau $nextArgument = substr($match, 0, $spacePosition); 403*37748cd8SNickeau } else { 404*37748cd8SNickeau $nextArgument = $match; 405*37748cd8SNickeau } 406*37748cd8SNickeau if (!strpos($nextArgument, "=") && !empty($nextArgument)) { 407*37748cd8SNickeau $attributes[$keyThirdArgument] = $nextArgument; 408*37748cd8SNickeau // Suppress the third argument 409*37748cd8SNickeau $match = substr($match, strlen($nextArgument)); 410*37748cd8SNickeau $match = trim($match); 411*37748cd8SNickeau } 412*37748cd8SNickeau } 413*37748cd8SNickeau } 414*37748cd8SNickeau 415*37748cd8SNickeau // Parse the remaining attributes 416*37748cd8SNickeau $parsedAttributes = self::parseAttributes($match); 417*37748cd8SNickeau 418*37748cd8SNickeau // Merge 419*37748cd8SNickeau $attributes = array_merge($attributes, $parsedAttributes);; 420*37748cd8SNickeau 421*37748cd8SNickeau return $attributes; 422*37748cd8SNickeau 423*37748cd8SNickeau } 424*37748cd8SNickeau 425*37748cd8SNickeau /** 426*37748cd8SNickeau * @param array $styleProperties - an array of CSS properties with key, value 427*37748cd8SNickeau * @return string - the value for the style attribute (ie all rules where joined with the comma) 428*37748cd8SNickeau */ 429*37748cd8SNickeau public static function array2InlineStyle(array $styleProperties) 430*37748cd8SNickeau { 431*37748cd8SNickeau $inlineCss = ""; 432*37748cd8SNickeau foreach ($styleProperties as $key => $value) { 433*37748cd8SNickeau $inlineCss .= "$key:$value;"; 434*37748cd8SNickeau } 435*37748cd8SNickeau // Suppress the last ; 436*37748cd8SNickeau if ($inlineCss[strlen($inlineCss) - 1] == ";") { 437*37748cd8SNickeau $inlineCss = substr($inlineCss, 0, -1); 438*37748cd8SNickeau } 439*37748cd8SNickeau return $inlineCss; 440*37748cd8SNickeau } 441*37748cd8SNickeau 442*37748cd8SNickeau /** 443*37748cd8SNickeau * @param $tag 444*37748cd8SNickeau * @return string 445*37748cd8SNickeau * Create a pattern used where the tag is not a container. 446*37748cd8SNickeau * ie 447*37748cd8SNickeau * <br/> 448*37748cd8SNickeau * <icon/> 449*37748cd8SNickeau * This is generally used with a subtition plugin 450*37748cd8SNickeau * and a {@link Lexer::addSpecialPattern} state 451*37748cd8SNickeau * where the tag is just replaced 452*37748cd8SNickeau */ 453*37748cd8SNickeau public static function getEmptyTagPattern($tag) 454*37748cd8SNickeau { 455*37748cd8SNickeau return '<' . $tag . '.*?/>'; 456*37748cd8SNickeau } 457*37748cd8SNickeau 458*37748cd8SNickeau /** 459*37748cd8SNickeau * Just call this function from a class like that 460*37748cd8SNickeau * getTageName(get_called_class()) 461*37748cd8SNickeau * to get the tag name (ie the component plugin) 462*37748cd8SNickeau * of a syntax plugin 463*37748cd8SNickeau * 464*37748cd8SNickeau * @param $get_called_class 465*37748cd8SNickeau * @return string 466*37748cd8SNickeau */ 467*37748cd8SNickeau public static function getTagName($get_called_class) 468*37748cd8SNickeau { 469*37748cd8SNickeau list(/* $t */, /* $p */, /* $n */, $c) = explode('_', $get_called_class, 4); 470*37748cd8SNickeau return (isset($c) ? $c : ''); 471*37748cd8SNickeau } 472*37748cd8SNickeau 473*37748cd8SNickeau /** 474*37748cd8SNickeau * Just call this function from a class like that 475*37748cd8SNickeau * getAdminPageName(get_called_class()) 476*37748cd8SNickeau * to get the page name of a admin plugin 477*37748cd8SNickeau * 478*37748cd8SNickeau * @param $get_called_class 479*37748cd8SNickeau * @return string - the admin page name 480*37748cd8SNickeau */ 481*37748cd8SNickeau public static function getAdminPageName($get_called_class) 482*37748cd8SNickeau { 483*37748cd8SNickeau $names = explode('_', $get_called_class); 484*37748cd8SNickeau $names = array_slice($names, -2); 485*37748cd8SNickeau return implode('_', $names); 486*37748cd8SNickeau } 487*37748cd8SNickeau 488*37748cd8SNickeau public static function getNameSpace() 489*37748cd8SNickeau { 490*37748cd8SNickeau // No : at the begin of the namespace please 491*37748cd8SNickeau return self::PLUGIN_BASE_NAME . ':'; 492*37748cd8SNickeau } 493*37748cd8SNickeau 494*37748cd8SNickeau /** 495*37748cd8SNickeau * @param $get_called_class - the plugin class 496*37748cd8SNickeau * @return array 497*37748cd8SNickeau */ 498*37748cd8SNickeau public static function getTags($get_called_class) 499*37748cd8SNickeau { 500*37748cd8SNickeau $elements = array(); 501*37748cd8SNickeau $elementName = PluginUtility::getTagName($get_called_class); 502*37748cd8SNickeau $elements[] = $elementName; 503*37748cd8SNickeau $elements[] = strtoupper($elementName); 504*37748cd8SNickeau return $elements; 505*37748cd8SNickeau } 506*37748cd8SNickeau 507*37748cd8SNickeau /** 508*37748cd8SNickeau * Render a text 509*37748cd8SNickeau * @param $pageContent 510*37748cd8SNickeau * @return string|null 511*37748cd8SNickeau */ 512*37748cd8SNickeau public static function render($pageContent) 513*37748cd8SNickeau { 514*37748cd8SNickeau return RenderUtility::renderText2XhtmlAndStripPEventually($pageContent, false); 515*37748cd8SNickeau } 516*37748cd8SNickeau 517*37748cd8SNickeau 518*37748cd8SNickeau /** 519*37748cd8SNickeau * This method will takes attributes 520*37748cd8SNickeau * and process the plugin styling attribute such as width and height 521*37748cd8SNickeau * to put them in a style HTML attribute 522*37748cd8SNickeau * @param TagAttributes $attributes 523*37748cd8SNickeau */ 524*37748cd8SNickeau public static function processStyle(&$attributes) 525*37748cd8SNickeau { 526*37748cd8SNickeau // Style 527*37748cd8SNickeau $styleAttributeName = "style"; 528*37748cd8SNickeau if ($attributes->hasComponentAttribute($styleAttributeName)) { 529*37748cd8SNickeau $properties = explode(";", $attributes->getValueAndRemove($styleAttributeName)); 530*37748cd8SNickeau foreach ($properties as $property) { 531*37748cd8SNickeau list($key, $value) = explode(":", $property); 532*37748cd8SNickeau if ($key != "") { 533*37748cd8SNickeau $attributes->addStyleDeclaration($key, $value); 534*37748cd8SNickeau } 535*37748cd8SNickeau } 536*37748cd8SNickeau } 537*37748cd8SNickeau 538*37748cd8SNickeau 539*37748cd8SNickeau /** 540*37748cd8SNickeau * Border Color 541*37748cd8SNickeau * For background color, see {@link TagAttributes::processBackground()} 542*37748cd8SNickeau * For text color, see {@link TextColor} 543*37748cd8SNickeau */ 544*37748cd8SNickeau 545*37748cd8SNickeau if ($attributes->hasComponentAttribute(ColorUtility::BORDER_COLOR)) { 546*37748cd8SNickeau $colorValue = $attributes->getValueAndRemove(ColorUtility::BORDER_COLOR); 547*37748cd8SNickeau $attributes->addStyleDeclaration(ColorUtility::BORDER_COLOR, ColorUtility::getColorValue($colorValue)); 548*37748cd8SNickeau self::checkDefaultBorderColorAttributes($attributes); 549*37748cd8SNickeau } 550*37748cd8SNickeau 551*37748cd8SNickeau 552*37748cd8SNickeau } 553*37748cd8SNickeau 554*37748cd8SNickeau /** 555*37748cd8SNickeau * Return the name of the requested script 556*37748cd8SNickeau */ 557*37748cd8SNickeau public 558*37748cd8SNickeau static function getRequestScript() 559*37748cd8SNickeau { 560*37748cd8SNickeau $scriptPath = null; 561*37748cd8SNickeau $testPropertyValue = self::getPropertyValue("SCRIPT_NAME"); 562*37748cd8SNickeau if (defined('DOKU_UNITTEST') && $testPropertyValue != null) { 563*37748cd8SNickeau return $testPropertyValue; 564*37748cd8SNickeau } 565*37748cd8SNickeau if (array_key_exists("DOCUMENT_URI", $_SERVER)) { 566*37748cd8SNickeau $scriptPath = $_SERVER["DOCUMENT_URI"]; 567*37748cd8SNickeau } 568*37748cd8SNickeau if ($scriptPath == null && array_key_exists("SCRIPT_NAME", $_SERVER)) { 569*37748cd8SNickeau $scriptPath = $_SERVER["SCRIPT_NAME"]; 570*37748cd8SNickeau } 571*37748cd8SNickeau if ($scriptPath == null) { 572*37748cd8SNickeau msg("Unable to find the main script", LogUtility::LVL_MSG_ERROR); 573*37748cd8SNickeau } 574*37748cd8SNickeau $path_parts = pathinfo($scriptPath); 575*37748cd8SNickeau return $path_parts['basename']; 576*37748cd8SNickeau } 577*37748cd8SNickeau 578*37748cd8SNickeau /** 579*37748cd8SNickeau * 580*37748cd8SNickeau * @param $name 581*37748cd8SNickeau * @param $default 582*37748cd8SNickeau * @return string - the value of a query string property or if in test mode, the value of a test variable 583*37748cd8SNickeau * set with {@link self::setTestProperty} 584*37748cd8SNickeau * This is used to test script that are not supported by the dokuwiki test framework 585*37748cd8SNickeau * such as css.php 586*37748cd8SNickeau */ 587*37748cd8SNickeau public 588*37748cd8SNickeau static function getPropertyValue($name, $default = null) 589*37748cd8SNickeau { 590*37748cd8SNickeau global $INPUT; 591*37748cd8SNickeau $value = $INPUT->str($name); 592*37748cd8SNickeau if ($value == null && defined('DOKU_UNITTEST')) { 593*37748cd8SNickeau global $COMBO; 594*37748cd8SNickeau $value = $COMBO[$name]; 595*37748cd8SNickeau } 596*37748cd8SNickeau if ($value == null) { 597*37748cd8SNickeau return $default; 598*37748cd8SNickeau } else { 599*37748cd8SNickeau return $value; 600*37748cd8SNickeau } 601*37748cd8SNickeau 602*37748cd8SNickeau } 603*37748cd8SNickeau 604*37748cd8SNickeau /** 605*37748cd8SNickeau * Create an URL to the documentation website 606*37748cd8SNickeau * @param $canonical - canonical id or slug 607*37748cd8SNickeau * @param $text - the text of the link 608*37748cd8SNickeau * @param bool $withIcon - used to break the recursion with the message in the {@link Icon} 609*37748cd8SNickeau * @return string - an url 610*37748cd8SNickeau */ 611*37748cd8SNickeau public 612*37748cd8SNickeau static function getUrl($canonical, $text, $withIcon = true) 613*37748cd8SNickeau { 614*37748cd8SNickeau /** @noinspection SpellCheckingInspection */ 615*37748cd8SNickeau 616*37748cd8SNickeau $xhtmlIcon = ""; 617*37748cd8SNickeau if ($withIcon) { 618*37748cd8SNickeau 619*37748cd8SNickeau /** 620*37748cd8SNickeau * We don't include it as an external resource via url 621*37748cd8SNickeau * because it then make a http request for every logo 622*37748cd8SNickeau * in the configuration page and makes it really slow 623*37748cd8SNickeau */ 624*37748cd8SNickeau $path = File::createFromPath(Resources::getImagesDirectory() . "/logo.svg"); 625*37748cd8SNickeau $tagAttributes = TagAttributes::createEmpty(SvgImageLink::CANONICAL); 626*37748cd8SNickeau $tagAttributes->addComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ICON_TYPE); 627*37748cd8SNickeau $tagAttributes->addComponentAttributeValue(Dimension::WIDTH_KEY, "20"); 628*37748cd8SNickeau $cache = new CacheMedia($path, $tagAttributes); 629*37748cd8SNickeau if (!$cache->isCacheUsable()) { 630*37748cd8SNickeau $xhtmlIcon = SvgDocument::createFromPath($path) 631*37748cd8SNickeau ->setShouldBeOptimized(true) 632*37748cd8SNickeau ->getXmlText($tagAttributes); 633*37748cd8SNickeau $cache->storeCache($xhtmlIcon); 634*37748cd8SNickeau } 635*37748cd8SNickeau $xhtmlIcon = file_get_contents($cache->getFile()->getFileSystemPath()); 636*37748cd8SNickeau 637*37748cd8SNickeau 638*37748cd8SNickeau } 639*37748cd8SNickeau return $xhtmlIcon . ' <a href="' . self::$URL_BASE . '/' . str_replace(":", "/", $canonical) . '" title="' . $text . '">' . $text . '</a>'; 640*37748cd8SNickeau } 641*37748cd8SNickeau 642*37748cd8SNickeau /** 643*37748cd8SNickeau * An utility function to not search every time which array should be first 644*37748cd8SNickeau * @param array $inlineAttributes - the component inline attributes 645*37748cd8SNickeau * @param array $defaultAttributes - the default configuration attributes 646*37748cd8SNickeau * @return array - a merged array 647*37748cd8SNickeau */ 648*37748cd8SNickeau public 649*37748cd8SNickeau static function mergeAttributes(array $inlineAttributes, array $defaultAttributes = array()) 650*37748cd8SNickeau { 651*37748cd8SNickeau return array_merge($defaultAttributes, $inlineAttributes); 652*37748cd8SNickeau } 653*37748cd8SNickeau 654*37748cd8SNickeau /** 655*37748cd8SNickeau * A pattern for a container tag 656*37748cd8SNickeau * that needs to catch the content 657*37748cd8SNickeau * 658*37748cd8SNickeau * Use as a special pattern (substition) 659*37748cd8SNickeau * 660*37748cd8SNickeau * The {@link \syntax_plugin_combo_math} use it 661*37748cd8SNickeau * @param $tag 662*37748cd8SNickeau * @return string - a pattern 663*37748cd8SNickeau */ 664*37748cd8SNickeau public 665*37748cd8SNickeau static function getLeafContainerTagPattern($tag) 666*37748cd8SNickeau { 667*37748cd8SNickeau return '<' . $tag . '.*?>.*?<\/' . $tag . '>'; 668*37748cd8SNickeau } 669*37748cd8SNickeau 670*37748cd8SNickeau /** 671*37748cd8SNickeau * Return the content of a tag 672*37748cd8SNickeau * <math>Content</math> 673*37748cd8SNickeau * @param $match 674*37748cd8SNickeau * @return string the content 675*37748cd8SNickeau */ 676*37748cd8SNickeau public 677*37748cd8SNickeau static function getTagContent($match) 678*37748cd8SNickeau { 679*37748cd8SNickeau // From the first > 680*37748cd8SNickeau $start = strpos($match, ">"); 681*37748cd8SNickeau if ($start == false) { 682*37748cd8SNickeau LogUtility::msg("The match does not contain any opening tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 683*37748cd8SNickeau return ""; 684*37748cd8SNickeau } 685*37748cd8SNickeau $match = substr($match, $start + 1); 686*37748cd8SNickeau // If this is the last character, we get a false 687*37748cd8SNickeau if ($match == false) { 688*37748cd8SNickeau LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 689*37748cd8SNickeau return ""; 690*37748cd8SNickeau } 691*37748cd8SNickeau 692*37748cd8SNickeau $end = strrpos($match, "</"); 693*37748cd8SNickeau if ($end == false) { 694*37748cd8SNickeau LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 695*37748cd8SNickeau return ""; 696*37748cd8SNickeau } 697*37748cd8SNickeau 698*37748cd8SNickeau return substr($match, 0, $end); 699*37748cd8SNickeau } 700*37748cd8SNickeau 701*37748cd8SNickeau /** 702*37748cd8SNickeau * 703*37748cd8SNickeau * Check if a HTML tag was already added for a request 704*37748cd8SNickeau * The request id is just the timestamp 705*37748cd8SNickeau * An indicator array should be provided 706*37748cd8SNickeau * @return string 707*37748cd8SNickeau */ 708*37748cd8SNickeau public 709*37748cd8SNickeau static function getRequestId() 710*37748cd8SNickeau { 711*37748cd8SNickeau 712*37748cd8SNickeau if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { 713*37748cd8SNickeau // since php 5.4 714*37748cd8SNickeau $requestTime = $_SERVER['REQUEST_TIME_FLOAT']; 715*37748cd8SNickeau } else { 716*37748cd8SNickeau // DokuWiki test framework use this 717*37748cd8SNickeau $requestTime = $_SERVER['REQUEST_TIME']; 718*37748cd8SNickeau } 719*37748cd8SNickeau $keyPrefix = 'combo_'; 720*37748cd8SNickeau 721*37748cd8SNickeau global $ID; 722*37748cd8SNickeau return $keyPrefix . hash('crc32b', $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . $requestTime . $ID); 723*37748cd8SNickeau 724*37748cd8SNickeau } 725*37748cd8SNickeau 726*37748cd8SNickeau /** 727*37748cd8SNickeau * Get the page id 728*37748cd8SNickeau * If the page is a sidebar, it will not return the id of the sidebar 729*37748cd8SNickeau * but the one of the page 730*37748cd8SNickeau * @return string 731*37748cd8SNickeau */ 732*37748cd8SNickeau public 733*37748cd8SNickeau static function getPageId() 734*37748cd8SNickeau { 735*37748cd8SNickeau return FsWikiUtility::getMainPageId(); 736*37748cd8SNickeau } 737*37748cd8SNickeau 738*37748cd8SNickeau /** 739*37748cd8SNickeau * Transform special HTML characters to entity 740*37748cd8SNickeau * Example: 741*37748cd8SNickeau * <hello>world</hello> 742*37748cd8SNickeau * to 743*37748cd8SNickeau * "<hello>world</hello>" 744*37748cd8SNickeau * 745*37748cd8SNickeau * @param $text 746*37748cd8SNickeau * @return string 747*37748cd8SNickeau */ 748*37748cd8SNickeau public 749*37748cd8SNickeau static function htmlEncode($text) 750*37748cd8SNickeau { 751*37748cd8SNickeau /** 752*37748cd8SNickeau * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344 753*37748cd8SNickeau * {@link htmlentities } 754*37748cd8SNickeau */ 755*37748cd8SNickeau //return htmlspecialchars($text, ENT_QUOTES); 756*37748cd8SNickeau return htmlentities($text); 757*37748cd8SNickeau } 758*37748cd8SNickeau 759*37748cd8SNickeau 760*37748cd8SNickeau /** 761*37748cd8SNickeau * Add a class 762*37748cd8SNickeau * @param $classValue 763*37748cd8SNickeau * @param array $attributes 764*37748cd8SNickeau */ 765*37748cd8SNickeau public 766*37748cd8SNickeau static function addClass2Attributes($classValue, array &$attributes) 767*37748cd8SNickeau { 768*37748cd8SNickeau self::addAttributeValue("class", $classValue, $attributes); 769*37748cd8SNickeau } 770*37748cd8SNickeau 771*37748cd8SNickeau /** 772*37748cd8SNickeau * Add a style property to the attributes 773*37748cd8SNickeau * @param $property 774*37748cd8SNickeau * @param $value 775*37748cd8SNickeau * @param array $attributes 776*37748cd8SNickeau * @deprecated use {@link TagAttributes::addStyleDeclaration()} instead 777*37748cd8SNickeau */ 778*37748cd8SNickeau public 779*37748cd8SNickeau static function addStyleProperty($property, $value, array &$attributes) 780*37748cd8SNickeau { 781*37748cd8SNickeau if (isset($attributes["style"])) { 782*37748cd8SNickeau $attributes["style"] .= ";$property:$value"; 783*37748cd8SNickeau } else { 784*37748cd8SNickeau $attributes["style"] = "$property:$value"; 785*37748cd8SNickeau } 786*37748cd8SNickeau 787*37748cd8SNickeau } 788*37748cd8SNickeau 789*37748cd8SNickeau /** 790*37748cd8SNickeau * Add default border attributes 791*37748cd8SNickeau * to see a border 792*37748cd8SNickeau * Doc 793*37748cd8SNickeau * https://combostrap.com/styling/color#border_color 794*37748cd8SNickeau * @param TagAttributes $tagAttributes 795*37748cd8SNickeau */ 796*37748cd8SNickeau private 797*37748cd8SNickeau static function checkDefaultBorderColorAttributes(&$tagAttributes) 798*37748cd8SNickeau { 799*37748cd8SNickeau /** 800*37748cd8SNickeau * border color was set without the width 801*37748cd8SNickeau * setting the width 802*37748cd8SNickeau */ 803*37748cd8SNickeau if (!( 804*37748cd8SNickeau $tagAttributes->hasStyleDeclaration("border") 805*37748cd8SNickeau || 806*37748cd8SNickeau $tagAttributes->hasStyleDeclaration("border-width") 807*37748cd8SNickeau ) 808*37748cd8SNickeau ) { 809*37748cd8SNickeau $tagAttributes->addStyleDeclaration("border-width", "1px"); 810*37748cd8SNickeau } 811*37748cd8SNickeau /** 812*37748cd8SNickeau * border color was set without the style 813*37748cd8SNickeau * setting the style 814*37748cd8SNickeau */ 815*37748cd8SNickeau if (! 816*37748cd8SNickeau ( 817*37748cd8SNickeau $tagAttributes->hasStyleDeclaration("border") 818*37748cd8SNickeau || 819*37748cd8SNickeau $tagAttributes->hasStyleDeclaration("border-style") 820*37748cd8SNickeau ) 821*37748cd8SNickeau ) { 822*37748cd8SNickeau $tagAttributes->addStyleDeclaration("border-style", "solid"); 823*37748cd8SNickeau 824*37748cd8SNickeau } 825*37748cd8SNickeau if (!$tagAttributes->hasStyleDeclaration("border-radius")) { 826*37748cd8SNickeau $tagAttributes->addStyleDeclaration("border-radius", ".25rem"); 827*37748cd8SNickeau } 828*37748cd8SNickeau 829*37748cd8SNickeau } 830*37748cd8SNickeau 831*37748cd8SNickeau public 832*37748cd8SNickeau static function getConfValue($confName, $defaultValue = null) 833*37748cd8SNickeau { 834*37748cd8SNickeau global $conf; 835*37748cd8SNickeau if (isset($conf['plugin'][PluginUtility::PLUGIN_BASE_NAME][$confName])) { 836*37748cd8SNickeau return $conf['plugin'][PluginUtility::PLUGIN_BASE_NAME][$confName]; 837*37748cd8SNickeau } else { 838*37748cd8SNickeau return $defaultValue; 839*37748cd8SNickeau } 840*37748cd8SNickeau } 841*37748cd8SNickeau 842*37748cd8SNickeau /** 843*37748cd8SNickeau * @param $match 844*37748cd8SNickeau * @return null|string - return the tag name or null if not found 845*37748cd8SNickeau */ 846*37748cd8SNickeau public 847*37748cd8SNickeau static function getTag($match) 848*37748cd8SNickeau { 849*37748cd8SNickeau 850*37748cd8SNickeau // Trim to start clean 851*37748cd8SNickeau $match = trim($match); 852*37748cd8SNickeau 853*37748cd8SNickeau // Until the first > 854*37748cd8SNickeau $pos = strpos($match, ">"); 855*37748cd8SNickeau if ($pos == false) { 856*37748cd8SNickeau LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 857*37748cd8SNickeau return null; 858*37748cd8SNickeau } 859*37748cd8SNickeau $match = substr($match, 0, $pos); 860*37748cd8SNickeau 861*37748cd8SNickeau // Suppress the < 862*37748cd8SNickeau if ($match[0] == "<") { 863*37748cd8SNickeau $match = substr($match, 1); 864*37748cd8SNickeau } else { 865*37748cd8SNickeau LogUtility::msg("This is not a text tag because it does not start with the character `>`"); 866*37748cd8SNickeau } 867*37748cd8SNickeau 868*37748cd8SNickeau // Suppress the tag name (ie until the first blank) 869*37748cd8SNickeau $spacePosition = strpos($match, " "); 870*37748cd8SNickeau if (!$spacePosition) { 871*37748cd8SNickeau // No space, meaning this is only the tag name 872*37748cd8SNickeau return $match; 873*37748cd8SNickeau } else { 874*37748cd8SNickeau return substr($match, 0, $spacePosition); 875*37748cd8SNickeau } 876*37748cd8SNickeau 877*37748cd8SNickeau } 878*37748cd8SNickeau 879*37748cd8SNickeau 880*37748cd8SNickeau /** 881*37748cd8SNickeau * @param string $string add a command into HTML 882*37748cd8SNickeau */ 883*37748cd8SNickeau public 884*37748cd8SNickeau static function addAsHtmlComment($string) 885*37748cd8SNickeau { 886*37748cd8SNickeau print_r('<!-- ' . self::htmlEncode($string) . '-->'); 887*37748cd8SNickeau } 888*37748cd8SNickeau 889*37748cd8SNickeau public 890*37748cd8SNickeau static function getResourceBaseUrl() 891*37748cd8SNickeau { 892*37748cd8SNickeau return DOKU_URL . 'lib/plugins/' . PluginUtility::PLUGIN_BASE_NAME . '/resources'; 893*37748cd8SNickeau } 894*37748cd8SNickeau 895*37748cd8SNickeau /** 896*37748cd8SNickeau * @param $TAG - the name of the tag that should correspond to the name of the css file in the style directory 897*37748cd8SNickeau * @return string - a inline style element to inject in the page or blank if no file exists 898*37748cd8SNickeau */ 899*37748cd8SNickeau public 900*37748cd8SNickeau static function getTagStyle($TAG) 901*37748cd8SNickeau { 902*37748cd8SNickeau $script = self::getCssRules($TAG); 903*37748cd8SNickeau if (!empty($script)) { 904*37748cd8SNickeau return "<style>" . $script . "</style>"; 905*37748cd8SNickeau } else { 906*37748cd8SNickeau return ""; 907*37748cd8SNickeau } 908*37748cd8SNickeau 909*37748cd8SNickeau } 910*37748cd8SNickeau 911*37748cd8SNickeau 912*37748cd8SNickeau public 913*37748cd8SNickeau static function getComponentName($tag) 914*37748cd8SNickeau { 915*37748cd8SNickeau return strtolower(PluginUtility::PLUGIN_BASE_NAME) . "_" . $tag; 916*37748cd8SNickeau } 917*37748cd8SNickeau 918*37748cd8SNickeau public 919*37748cd8SNickeau static function addAttributeValue($attribute, $value, array &$attributes) 920*37748cd8SNickeau { 921*37748cd8SNickeau if (array_key_exists($attribute, $attributes) && $attributes[$attribute] !== "") { 922*37748cd8SNickeau $attributes[$attribute] .= " {$value}"; 923*37748cd8SNickeau } else { 924*37748cd8SNickeau $attributes[$attribute] = "{$value}"; 925*37748cd8SNickeau } 926*37748cd8SNickeau } 927*37748cd8SNickeau 928*37748cd8SNickeau /** 929*37748cd8SNickeau * Plugin Utility is available to all plugin, 930*37748cd8SNickeau * this is a convenient way to the the snippet manager 931*37748cd8SNickeau * @return SnippetManager 932*37748cd8SNickeau */ 933*37748cd8SNickeau public 934*37748cd8SNickeau static function getSnippetManager() 935*37748cd8SNickeau { 936*37748cd8SNickeau return SnippetManager::get(); 937*37748cd8SNickeau } 938*37748cd8SNickeau 939*37748cd8SNickeau public 940*37748cd8SNickeau static function initStaticManager() 941*37748cd8SNickeau { 942*37748cd8SNickeau CacheManager::init(); 943*37748cd8SNickeau SnippetManager::init(); 944*37748cd8SNickeau } 945*37748cd8SNickeau 946*37748cd8SNickeau /** 947*37748cd8SNickeau * Function used in a render 948*37748cd8SNickeau * @param $data - the data from {@link PluginUtility::handleAndReturnUnmatchedData()} 949*37748cd8SNickeau * @return string 950*37748cd8SNickeau */ 951*37748cd8SNickeau public 952*37748cd8SNickeau static function renderUnmatched($data) 953*37748cd8SNickeau { 954*37748cd8SNickeau /** 955*37748cd8SNickeau * Attributes 956*37748cd8SNickeau */ 957*37748cd8SNickeau if (isset($data[PluginUtility::ATTRIBUTES])) { 958*37748cd8SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 959*37748cd8SNickeau } else { 960*37748cd8SNickeau $attributes = []; 961*37748cd8SNickeau } 962*37748cd8SNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 963*37748cd8SNickeau $display = $tagAttributes->getValue(TagAttributes::DISPLAY); 964*37748cd8SNickeau if ($display != "none") { 965*37748cd8SNickeau $payload = $data[self::PAYLOAD]; 966*37748cd8SNickeau $context = $data[self::CONTEXT]; 967*37748cd8SNickeau if (!in_array($context, Call::INLINE_DOKUWIKI_COMPONENTS)) { 968*37748cd8SNickeau $payload = ltrim($payload); 969*37748cd8SNickeau } 970*37748cd8SNickeau return PluginUtility::htmlEncode($payload); 971*37748cd8SNickeau } else { 972*37748cd8SNickeau return ""; 973*37748cd8SNickeau } 974*37748cd8SNickeau } 975*37748cd8SNickeau 976*37748cd8SNickeau /** 977*37748cd8SNickeau * Function used in a handle function of a syntax plugin for 978*37748cd8SNickeau * unmatched context 979*37748cd8SNickeau * @param $tagName 980*37748cd8SNickeau * @param $match 981*37748cd8SNickeau * @param \Doku_Handler $handler 982*37748cd8SNickeau * @return array 983*37748cd8SNickeau */ 984*37748cd8SNickeau public 985*37748cd8SNickeau static function handleAndReturnUnmatchedData($tagName, $match, \Doku_Handler $handler) 986*37748cd8SNickeau { 987*37748cd8SNickeau $tag = new Tag($tagName, array(), DOKU_LEXER_UNMATCHED, $handler); 988*37748cd8SNickeau $sibling = $tag->getPreviousSibling(); 989*37748cd8SNickeau $context = null; 990*37748cd8SNickeau if (!empty($sibling)) { 991*37748cd8SNickeau $context = $sibling->getName(); 992*37748cd8SNickeau } 993*37748cd8SNickeau return array( 994*37748cd8SNickeau PluginUtility::STATE => DOKU_LEXER_UNMATCHED, 995*37748cd8SNickeau PluginUtility::PAYLOAD => $match, 996*37748cd8SNickeau PluginUtility::CONTEXT => $context 997*37748cd8SNickeau ); 998*37748cd8SNickeau } 999*37748cd8SNickeau 1000*37748cd8SNickeau public 1001*37748cd8SNickeau static function setConf($key, $value, $namespace = 'plugin') 1002*37748cd8SNickeau { 1003*37748cd8SNickeau global $conf; 1004*37748cd8SNickeau if ($namespace != null) { 1005*37748cd8SNickeau $conf[$namespace][PluginUtility::PLUGIN_BASE_NAME][$key] = $value; 1006*37748cd8SNickeau } else { 1007*37748cd8SNickeau $conf[$key] = $value; 1008*37748cd8SNickeau } 1009*37748cd8SNickeau 1010*37748cd8SNickeau } 1011*37748cd8SNickeau 1012*37748cd8SNickeau /** 1013*37748cd8SNickeau * Utility methodPreprocess a start tag to be able to extract the name 1014*37748cd8SNickeau * and the attributes easily 1015*37748cd8SNickeau * 1016*37748cd8SNickeau * It will delete: 1017*37748cd8SNickeau * * the characters <> and the /> if present 1018*37748cd8SNickeau * * and trim 1019*37748cd8SNickeau * 1020*37748cd8SNickeau * It will remain the tagname and its attributes 1021*37748cd8SNickeau * @param $match 1022*37748cd8SNickeau * @return false|string|null 1023*37748cd8SNickeau */ 1024*37748cd8SNickeau private 1025*37748cd8SNickeau static function getPreprocessEnterTag($match) 1026*37748cd8SNickeau { 1027*37748cd8SNickeau // Until the first > 1028*37748cd8SNickeau $pos = strpos($match, ">"); 1029*37748cd8SNickeau if ($pos == false) { 1030*37748cd8SNickeau LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_WARNING); 1031*37748cd8SNickeau return null; 1032*37748cd8SNickeau } 1033*37748cd8SNickeau $match = substr($match, 0, $pos); 1034*37748cd8SNickeau 1035*37748cd8SNickeau 1036*37748cd8SNickeau // Trim to start clean 1037*37748cd8SNickeau $match = trim($match); 1038*37748cd8SNickeau 1039*37748cd8SNickeau // Suppress the < 1040*37748cd8SNickeau if ($match[0] == "<") { 1041*37748cd8SNickeau $match = substr($match, 1); 1042*37748cd8SNickeau } 1043*37748cd8SNickeau 1044*37748cd8SNickeau // Suppress the / for a leaf tag 1045*37748cd8SNickeau if ($match[strlen($match) - 1] == "/") { 1046*37748cd8SNickeau $match = substr($match, 0, strlen($match) - 1); 1047*37748cd8SNickeau } 1048*37748cd8SNickeau return $match; 1049*37748cd8SNickeau } 1050*37748cd8SNickeau 1051*37748cd8SNickeau /** 1052*37748cd8SNickeau * Retrieve the tag name used in the text document 1053*37748cd8SNickeau * @param $match 1054*37748cd8SNickeau * @return false|string|null 1055*37748cd8SNickeau */ 1056*37748cd8SNickeau public 1057*37748cd8SNickeau static function getSyntaxTagNameFromMatch($match) 1058*37748cd8SNickeau { 1059*37748cd8SNickeau $preprocessMatch = PluginUtility::getPreprocessEnterTag($match); 1060*37748cd8SNickeau 1061*37748cd8SNickeau // Tag name (ie until the first blank) 1062*37748cd8SNickeau $spacePosition = strpos($match, " "); 1063*37748cd8SNickeau if (!$spacePosition) { 1064*37748cd8SNickeau // No space, meaning this is only the tag name 1065*37748cd8SNickeau return $preprocessMatch; 1066*37748cd8SNickeau } else { 1067*37748cd8SNickeau return trim(substr(0, $spacePosition)); 1068*37748cd8SNickeau } 1069*37748cd8SNickeau 1070*37748cd8SNickeau } 1071*37748cd8SNickeau 1072*37748cd8SNickeau /** 1073*37748cd8SNickeau * @param \Doku_Renderer_xhtml $renderer 1074*37748cd8SNickeau * @param $position 1075*37748cd8SNickeau * @param $name 1076*37748cd8SNickeau */ 1077*37748cd8SNickeau public 1078*37748cd8SNickeau static function startSection($renderer, $position, $name) 1079*37748cd8SNickeau { 1080*37748cd8SNickeau 1081*37748cd8SNickeau 1082*37748cd8SNickeau if (empty($position)) { 1083*37748cd8SNickeau LogUtility::msg("The position for a start section should not be empty", LogUtility::LVL_MSG_ERROR, "support"); 1084*37748cd8SNickeau } 1085*37748cd8SNickeau if (empty($name)) { 1086*37748cd8SNickeau LogUtility::msg("The name for a start section should not be empty", LogUtility::LVL_MSG_ERROR, "support"); 1087*37748cd8SNickeau } 1088*37748cd8SNickeau 1089*37748cd8SNickeau /** 1090*37748cd8SNickeau * New Dokuwiki Version 1091*37748cd8SNickeau * for DokuWiki Greebo and more recent versions 1092*37748cd8SNickeau */ 1093*37748cd8SNickeau if (defined('SEC_EDIT_PATTERN')) { 1094*37748cd8SNickeau $renderer->startSectionEdit($position, array('target' => self::EDIT_SECTION_TARGET, 'name' => $name)); 1095*37748cd8SNickeau } else { 1096*37748cd8SNickeau /** 1097*37748cd8SNickeau * Old version 1098*37748cd8SNickeau */ 1099*37748cd8SNickeau /** @noinspection PhpParamsInspection */ 1100*37748cd8SNickeau $renderer->startSectionEdit($position, self::EDIT_SECTION_TARGET, $name); 1101*37748cd8SNickeau } 1102*37748cd8SNickeau } 1103*37748cd8SNickeau 1104*37748cd8SNickeau /** 1105*37748cd8SNickeau * Add an enter call to the stack 1106*37748cd8SNickeau * @param \Doku_Handler $handler 1107*37748cd8SNickeau * @param $tagName 1108*37748cd8SNickeau * @param array $callStackArray 1109*37748cd8SNickeau */ 1110*37748cd8SNickeau public 1111*37748cd8SNickeau static function addEnterCall( 1112*37748cd8SNickeau \Doku_Handler &$handler, 1113*37748cd8SNickeau $tagName, 1114*37748cd8SNickeau $callStackArray = array() 1115*37748cd8SNickeau ) 1116*37748cd8SNickeau { 1117*37748cd8SNickeau $pluginName = PluginUtility::getComponentName($tagName); 1118*37748cd8SNickeau $handler->addPluginCall( 1119*37748cd8SNickeau $pluginName, 1120*37748cd8SNickeau $callStackArray, 1121*37748cd8SNickeau DOKU_LEXER_ENTER, 1122*37748cd8SNickeau null, 1123*37748cd8SNickeau null 1124*37748cd8SNickeau ); 1125*37748cd8SNickeau } 1126*37748cd8SNickeau 1127*37748cd8SNickeau /** 1128*37748cd8SNickeau * Add an end call dynamically 1129*37748cd8SNickeau * @param \Doku_Handler $handler 1130*37748cd8SNickeau * @param $tagName 1131*37748cd8SNickeau * @param array $callStackArray 1132*37748cd8SNickeau */ 1133*37748cd8SNickeau public 1134*37748cd8SNickeau static function addEndCall(\Doku_Handler $handler, $tagName, $callStackArray = array()) 1135*37748cd8SNickeau { 1136*37748cd8SNickeau $pluginName = PluginUtility::getComponentName($tagName); 1137*37748cd8SNickeau $handler->addPluginCall( 1138*37748cd8SNickeau $pluginName, 1139*37748cd8SNickeau $callStackArray, 1140*37748cd8SNickeau DOKU_LEXER_END, 1141*37748cd8SNickeau null, 1142*37748cd8SNickeau null 1143*37748cd8SNickeau ); 1144*37748cd8SNickeau } 1145*37748cd8SNickeau 1146*37748cd8SNickeau /** 1147*37748cd8SNickeau * General Debug 1148*37748cd8SNickeau */ 1149*37748cd8SNickeau public 1150*37748cd8SNickeau static function isDebug() 1151*37748cd8SNickeau { 1152*37748cd8SNickeau global $conf; 1153*37748cd8SNickeau return $conf["allowdebug"] === 1; 1154*37748cd8SNickeau 1155*37748cd8SNickeau } 1156*37748cd8SNickeau 1157*37748cd8SNickeau /** 1158*37748cd8SNickeau * @return bool true if loaded, false otherwise 1159*37748cd8SNickeau * Strap is loaded only if this is the same version 1160*37748cd8SNickeau * to avoid function, class, or members that does not exist 1161*37748cd8SNickeau */ 1162*37748cd8SNickeau public 1163*37748cd8SNickeau static function loadStrapUtilityTemplateIfPresentAndSameVersion() 1164*37748cd8SNickeau { 1165*37748cd8SNickeau $templateUtilityFile = __DIR__ . '/../../../tpl/strap/class/TplUtility.php'; 1166*37748cd8SNickeau if (file_exists($templateUtilityFile)) { 1167*37748cd8SNickeau /** 1168*37748cd8SNickeau * Check the version 1169*37748cd8SNickeau */ 1170*37748cd8SNickeau $templateInfo = confToHash(__DIR__ . '/../../../tpl/strap/template.info.txt'); 1171*37748cd8SNickeau $templateVersion = $templateInfo['version']; 1172*37748cd8SNickeau $comboVersion = self::$INFO_PLUGIN['version']; 1173*37748cd8SNickeau if ($templateVersion != $comboVersion) { 1174*37748cd8SNickeau if ($comboVersion > $templateVersion) { 1175*37748cd8SNickeau LogUtility::msg("You should upgrade <a href=\"https://www.dokuwiki.org/template:strap\">strap</a> to the latest version to get a fully functional experience. The version of Combo is ($comboVersion) while the version of Strap is ($templateVersion)."); 1176*37748cd8SNickeau } else { 1177*37748cd8SNickeau LogUtility::msg("You should upgrade <a href=\"https://www.dokuwiki.org/plugin:combo\">combo</a> to the latest version to get a fully functional experience. The version of Combo is ($comboVersion) while the version of Strap is ($templateVersion)."); 1178*37748cd8SNickeau } 1179*37748cd8SNickeau return false; 1180*37748cd8SNickeau } else { 1181*37748cd8SNickeau /** @noinspection PhpIncludeInspection */ 1182*37748cd8SNickeau require_once($templateUtilityFile); 1183*37748cd8SNickeau return true; 1184*37748cd8SNickeau } 1185*37748cd8SNickeau } else { 1186*37748cd8SNickeau $level = LogUtility::LVL_MSG_DEBUG; 1187*37748cd8SNickeau if (defined('DOKU_UNITTEST')) { 1188*37748cd8SNickeau // fail 1189*37748cd8SNickeau $level = LogUtility::LVL_MSG_ERROR; 1190*37748cd8SNickeau } 1191*37748cd8SNickeau if (Site::getTemplate() != "strap") { 1192*37748cd8SNickeau LogUtility::msg("The strap template is not installed", $level); 1193*37748cd8SNickeau } else { 1194*37748cd8SNickeau LogUtility::msg("The file ($templateUtilityFile) was not found", $level); 1195*37748cd8SNickeau } 1196*37748cd8SNickeau return false; 1197*37748cd8SNickeau } 1198*37748cd8SNickeau } 1199*37748cd8SNickeau 1200*37748cd8SNickeau 1201*37748cd8SNickeau /** 1202*37748cd8SNickeau * 1203*37748cd8SNickeau * See also dev.md file 1204*37748cd8SNickeau */ 1205*37748cd8SNickeau public static function isDevOrTest() 1206*37748cd8SNickeau { 1207*37748cd8SNickeau if (self::isDev()) { 1208*37748cd8SNickeau return true; 1209*37748cd8SNickeau } 1210*37748cd8SNickeau return self::isTest(); 1211*37748cd8SNickeau } 1212*37748cd8SNickeau 1213*37748cd8SNickeau public static function isDev() 1214*37748cd8SNickeau { 1215*37748cd8SNickeau global $_SERVER; 1216*37748cd8SNickeau if ($_SERVER["REMOTE_ADDR"] == "127.0.0.1") { 1217*37748cd8SNickeau return true; 1218*37748cd8SNickeau } 1219*37748cd8SNickeau return false; 1220*37748cd8SNickeau } 1221*37748cd8SNickeau 1222*37748cd8SNickeau public static function getInstructions($markiCode) 1223*37748cd8SNickeau { 1224*37748cd8SNickeau return p_get_instructions($markiCode); 1225*37748cd8SNickeau } 1226*37748cd8SNickeau 1227*37748cd8SNickeau public static function getInstructionsWithoutRoot($markiCode) 1228*37748cd8SNickeau { 1229*37748cd8SNickeau return RenderUtility::getInstructionsAndStripPEventually($markiCode); 1230*37748cd8SNickeau } 1231*37748cd8SNickeau 1232*37748cd8SNickeau /** 1233*37748cd8SNickeau * Transform a text into a valid HTML id 1234*37748cd8SNickeau * @param $string 1235*37748cd8SNickeau * @return string 1236*37748cd8SNickeau */ 1237*37748cd8SNickeau public static function toHtmlId($string) 1238*37748cd8SNickeau { 1239*37748cd8SNickeau /** 1240*37748cd8SNickeau * sectionId calls cleanID 1241*37748cd8SNickeau * cleanID delete all things before a ':' 1242*37748cd8SNickeau * we do then the replace before to not 1243*37748cd8SNickeau * lost a minus '-' separator 1244*37748cd8SNickeau */ 1245*37748cd8SNickeau $string = str_replace(array(':', '.'), '', $string); 1246*37748cd8SNickeau return sectionID($string, $check); 1247*37748cd8SNickeau } 1248*37748cd8SNickeau 1249*37748cd8SNickeau public static function isTest() 1250*37748cd8SNickeau { 1251*37748cd8SNickeau return defined('DOKU_UNITTEST'); 1252*37748cd8SNickeau } 1253*37748cd8SNickeau 1254*37748cd8SNickeau 1255*37748cd8SNickeau public static function getCacheManager() 1256*37748cd8SNickeau { 1257*37748cd8SNickeau return CacheManager::get(); 1258*37748cd8SNickeau } 1259*37748cd8SNickeau 1260*37748cd8SNickeau public static function getModeFromPluginName($name) 1261*37748cd8SNickeau { 1262*37748cd8SNickeau return "plugin_$name"; 1263*37748cd8SNickeau } 1264*37748cd8SNickeau 1265*37748cd8SNickeau public static function isCi(): bool 1266*37748cd8SNickeau { 1267*37748cd8SNickeau // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables 1268*37748cd8SNickeau return getenv("CI")==="true"; 1269*37748cd8SNickeau } 1270*37748cd8SNickeau 1271*37748cd8SNickeau 1272*37748cd8SNickeau} 1273*37748cd8SNickeau 1274*37748cd8SNickeauPluginUtility::init(); 1275