137748cd8SNickeau<?php 237748cd8SNickeau 337748cd8SNickeau 437748cd8SNickeaunamespace ComboStrap; 537748cd8SNickeau 637748cd8SNickeau 737748cd8SNickeauuse dokuwiki\Extension\Plugin; 837748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin; 937748cd8SNickeau 1037748cd8SNickeaurequire_once(__DIR__ . '/../vendor/autoload.php'); 1137748cd8SNickeau 1237748cd8SNickeau/** 13*1fa8c418SNickeau * Parent in th hierarchy should be first 14*1fa8c418SNickeau * Ie before {@link ImageLink, SvgImageLink, RasterImageLink) 15*1fa8c418SNickeau */ 16*1fa8c418SNickeaurequire_once(__DIR__ . '/DokuPath.php'); 17*1fa8c418SNickeaurequire_once(__DIR__ . '/Media.php'); 18*1fa8c418SNickeaurequire_once(__DIR__ . '/MediaLink.php'); 19*1fa8c418SNickeau 20*1fa8c418SNickeau/** 2137748cd8SNickeau * Plugin Utility is added in all Dokuwiki extension 2237748cd8SNickeau * and 2337748cd8SNickeau * all classes are added in plugin utility 2437748cd8SNickeau * 2537748cd8SNickeau * This is an utility master and the class loader 2637748cd8SNickeau * 2737748cd8SNickeau * If the load is relative, the load path is used 2837748cd8SNickeau * and the bad php file may be loaded 2937748cd8SNickeau * Furthermore, the absolute path helps 3037748cd8SNickeau * the IDE when refactoring 3137748cd8SNickeau */ 3237748cd8SNickeaurequire_once(__DIR__ . '/AdsUtility.php'); 3337748cd8SNickeaurequire_once(__DIR__ . '/Align.php'); 3437748cd8SNickeaurequire_once(__DIR__ . '/Analytics.php'); 3537748cd8SNickeaurequire_once(__DIR__ . '/AnalyticsMenuItem.php'); 3637748cd8SNickeaurequire_once(__DIR__ . '/Animation.php'); 3737748cd8SNickeaurequire_once(__DIR__ . '/ArrayCaseInsensitive.php'); 3837748cd8SNickeaurequire_once(__DIR__ . '/ArrayUtility.php'); 3937748cd8SNickeaurequire_once(__DIR__ . '/Background.php'); 4037748cd8SNickeaurequire_once(__DIR__ . '/Boldness.php'); 4137748cd8SNickeaurequire_once(__DIR__ . '/Bootstrap.php'); 4237748cd8SNickeaurequire_once(__DIR__ . '/BreadcrumbHierarchical.php'); 4337748cd8SNickeaurequire_once(__DIR__ . '/CacheByLogicalKey.php'); 4437748cd8SNickeaurequire_once(__DIR__ . '/CacheInstructionsByLogicalKey.php'); 4537748cd8SNickeaurequire_once(__DIR__ . '/CacheManager.php'); 4637748cd8SNickeaurequire_once(__DIR__ . '/CacheMedia.php'); 4737748cd8SNickeaurequire_once(__DIR__ . '/Call.php'); 4837748cd8SNickeaurequire_once(__DIR__ . '/CallStack.php'); 4937748cd8SNickeaurequire_once(__DIR__ . '/ColorUtility.php'); 5037748cd8SNickeaurequire_once(__DIR__ . '/ConditionalValue.php'); 5137748cd8SNickeaurequire_once(__DIR__ . '/ConfUtility.php'); 5237748cd8SNickeaurequire_once(__DIR__ . '/Dimension.php'); 5337748cd8SNickeaurequire_once(__DIR__ . '/DokuwikiUrl.php'); 54*1fa8c418SNickeaurequire_once(__DIR__ . '/ExitException.php'); 5537748cd8SNickeaurequire_once(__DIR__ . '/File.php'); 5637748cd8SNickeaurequire_once(__DIR__ . '/FloatAttribute.php'); 5737748cd8SNickeaurequire_once(__DIR__ . '/FontSize.php'); 5837748cd8SNickeaurequire_once(__DIR__ . '/FsWikiUtility.php'); 5937748cd8SNickeaurequire_once(__DIR__ . '/HeaderUtility.php'); 6037748cd8SNickeaurequire_once(__DIR__ . '/HistoricalBreadcrumbMenuItem.php'); 6137748cd8SNickeaurequire_once(__DIR__ . '/Hover.php'); 6237748cd8SNickeaurequire_once(__DIR__ . '/Http.php'); 6337748cd8SNickeaurequire_once(__DIR__ . '/Icon.php'); 6437748cd8SNickeaurequire_once(__DIR__ . '/Identity.php'); 65*1fa8c418SNickeaurequire_once(__DIR__ . '/Image.php'); 66*1fa8c418SNickeaurequire_once(__DIR__ . '/ImageLink.php'); 67*1fa8c418SNickeaurequire_once(__DIR__ . '/ImageRaster.php'); 68*1fa8c418SNickeaurequire_once(__DIR__ . '/ImageSvg.php'); 6937748cd8SNickeaurequire_once(__DIR__ . '/Iso8601Date.php'); 70*1fa8c418SNickeaurequire_once(__DIR__ . '/Json.php'); 7137748cd8SNickeaurequire_once(__DIR__ . '/Lang.php'); 7237748cd8SNickeaurequire_once(__DIR__ . '/LineSpacing.php'); 73*1fa8c418SNickeaurequire_once(__DIR__ . '/LogException.php'); 7437748cd8SNickeaurequire_once(__DIR__ . '/LogUtility.php'); 7537748cd8SNickeaurequire_once(__DIR__ . '/LowQualityPage.php'); 7637748cd8SNickeaurequire_once(__DIR__ . '/MetadataUtility.php'); 77*1fa8c418SNickeaurequire_once(__DIR__ . '/Message.php'); 78*1fa8c418SNickeaurequire_once(__DIR__ . '/Mermaid.php'); 7937748cd8SNickeaurequire_once(__DIR__ . '/NavBarUtility.php'); 8037748cd8SNickeaurequire_once(__DIR__ . '/Opacity.php'); 81*1fa8c418SNickeaurequire_once(__DIR__ . '/Os.php'); 8237748cd8SNickeaurequire_once(__DIR__ . '/Page.php'); 8337748cd8SNickeaurequire_once(__DIR__ . '/PageProtection.php'); 8437748cd8SNickeaurequire_once(__DIR__ . '/PageRules.php'); 8537748cd8SNickeaurequire_once(__DIR__ . '/PageSql.php'); 8637748cd8SNickeaurequire_once(__DIR__ . '/PageSqlParser/PageSqlLexer.php'); 8737748cd8SNickeaurequire_once(__DIR__ . '/PageSqlParser/PageSqlParser.php'); 8837748cd8SNickeaurequire_once(__DIR__ . '/PageSqlTreeListener.php'); 8937748cd8SNickeaurequire_once(__DIR__ . '/PagesIndex.php'); 9037748cd8SNickeaurequire_once(__DIR__ . '/PipelineUtility.php'); 9137748cd8SNickeaurequire_once(__DIR__ . '/Position.php'); 9237748cd8SNickeaurequire_once(__DIR__ . '/Prism.php'); 9337748cd8SNickeaurequire_once(__DIR__ . '/Publication.php'); 9437748cd8SNickeaurequire_once(__DIR__ . '/RasterImageLink.php'); 9537748cd8SNickeaurequire_once(__DIR__ . '/RenderUtility.php'); 9637748cd8SNickeaurequire_once(__DIR__ . '/Resources.php'); 97*1fa8c418SNickeaurequire_once(__DIR__ . '/Sanitizer.php'); 9837748cd8SNickeaurequire_once(__DIR__ . '/Shadow.php'); 9937748cd8SNickeaurequire_once(__DIR__ . '/Site.php'); 10037748cd8SNickeaurequire_once(__DIR__ . '/Skin.php'); 10137748cd8SNickeaurequire_once(__DIR__ . '/Snippet.php'); 10237748cd8SNickeaurequire_once(__DIR__ . '/SnippetManager.php'); 10337748cd8SNickeaurequire_once(__DIR__ . '/Spacing.php'); 10437748cd8SNickeaurequire_once(__DIR__ . '/Sqlite.php'); 10537748cd8SNickeaurequire_once(__DIR__ . '/StringUtility.php'); 10637748cd8SNickeaurequire_once(__DIR__ . '/StyleUtility.php'); 10737748cd8SNickeaurequire_once(__DIR__ . '/SvgDocument.php'); 10837748cd8SNickeaurequire_once(__DIR__ . '/SvgImageLink.php'); 10937748cd8SNickeaurequire_once(__DIR__ . '/Syntax.php'); 11037748cd8SNickeaurequire_once(__DIR__ . '/TableUtility.php'); 11137748cd8SNickeaurequire_once(__DIR__ . '/Tag.php'); 11237748cd8SNickeaurequire_once(__DIR__ . '/TagAttributes.php'); 11337748cd8SNickeaurequire_once(__DIR__ . '/Template.php'); 11437748cd8SNickeaurequire_once(__DIR__ . '/TemplateUtility.php'); 11537748cd8SNickeaurequire_once(__DIR__ . '/TextAlign.php'); 11637748cd8SNickeaurequire_once(__DIR__ . '/TextColor.php'); 11737748cd8SNickeaurequire_once(__DIR__ . '/ThirdMediaLink.php'); 11837748cd8SNickeaurequire_once(__DIR__ . '/ThirdPartyPlugins.php'); 11937748cd8SNickeaurequire_once(__DIR__ . '/TocUtility.php'); 12037748cd8SNickeaurequire_once(__DIR__ . '/Toggle.php'); 12137748cd8SNickeaurequire_once(__DIR__ . '/Underline.php'); 12237748cd8SNickeaurequire_once(__DIR__ . '/Unit.php'); 12337748cd8SNickeaurequire_once(__DIR__ . '/UrlManagerBestEndPage.php'); 12437748cd8SNickeaurequire_once(__DIR__ . '/UrlUtility.php'); 12537748cd8SNickeaurequire_once(__DIR__ . '/XhtmlUtility.php'); 12637748cd8SNickeaurequire_once(__DIR__ . '/XmlDocument.php'); 12737748cd8SNickeaurequire_once(__DIR__ . '/XmlUtility.php'); 12837748cd8SNickeau 12937748cd8SNickeau 13037748cd8SNickeau/** 13137748cd8SNickeau * Class url static 13237748cd8SNickeau * List of static utilities 13337748cd8SNickeau */ 13437748cd8SNickeauclass PluginUtility 13537748cd8SNickeau{ 13637748cd8SNickeau 13737748cd8SNickeau const DOKU_DATA_DIR = '/dokudata/pages'; 13837748cd8SNickeau const DOKU_CACHE_DIR = '/dokudata/cache'; 13937748cd8SNickeau 14037748cd8SNickeau /** 14137748cd8SNickeau * Key in the data array between the handle and render function 14237748cd8SNickeau */ 14337748cd8SNickeau const STATE = "state"; 14437748cd8SNickeau const PAYLOAD = "payload"; // The html or text 14537748cd8SNickeau const ATTRIBUTES = "attributes"; 14637748cd8SNickeau // The context is generally the parent tag but it may be also the grandfather. 14737748cd8SNickeau // It permits to determine the HTML that is outputted 14837748cd8SNickeau const CONTEXT = 'context'; 14937748cd8SNickeau const TAG = "tag"; 15037748cd8SNickeau 15137748cd8SNickeau /** 15237748cd8SNickeau * The name of the hidden/private namespace 15337748cd8SNickeau * where the icon and other artifactory are stored 15437748cd8SNickeau */ 15537748cd8SNickeau const COMBOSTRAP_NAMESPACE_NAME = "combostrap"; 15637748cd8SNickeau 15737748cd8SNickeau const PARENT = "parent"; 15837748cd8SNickeau const POSITION = "position"; 15937748cd8SNickeau 16037748cd8SNickeau /** 16137748cd8SNickeau * Class to center an element 16237748cd8SNickeau */ 16337748cd8SNickeau const CENTER_CLASS = "mx-auto"; 16437748cd8SNickeau 16537748cd8SNickeau 16637748cd8SNickeau const EDIT_SECTION_TARGET = 'section'; 16737748cd8SNickeau const ERROR_MESSAGE = "errorAtt"; 168*1fa8c418SNickeau const ERROR_LEVEL = "errorLevel"; 169*1fa8c418SNickeau const DISPLAY = "display"; 17037748cd8SNickeau 17137748cd8SNickeau /** 17237748cd8SNickeau * The URL base of the documentation 17337748cd8SNickeau */ 17437748cd8SNickeau static $URL_BASE; 17537748cd8SNickeau 17637748cd8SNickeau 17737748cd8SNickeau /** 17837748cd8SNickeau * @var string - the plugin base name (ie the directory) 17937748cd8SNickeau * ie $INFO_PLUGIN['base']; 18037748cd8SNickeau * This is a constant because it permits code analytics 18137748cd8SNickeau * such as verification of a path 18237748cd8SNickeau */ 18337748cd8SNickeau const PLUGIN_BASE_NAME = "combo"; 18437748cd8SNickeau 18537748cd8SNickeau /** 18637748cd8SNickeau * The name of the template plugin 18737748cd8SNickeau */ 18837748cd8SNickeau const TEMPLATE_STRAP_NAME = "strap"; 18937748cd8SNickeau 19037748cd8SNickeau /** 19137748cd8SNickeau * @var array 19237748cd8SNickeau */ 19337748cd8SNickeau static $INFO_PLUGIN; 19437748cd8SNickeau 19537748cd8SNickeau static $PLUGIN_LANG; 19637748cd8SNickeau 19737748cd8SNickeau /** 19837748cd8SNickeau * The plugin name 19937748cd8SNickeau * (not the same than the base as it's not related to the directory 20037748cd8SNickeau * @var string 20137748cd8SNickeau */ 20237748cd8SNickeau public static $PLUGIN_NAME; 20337748cd8SNickeau /** 20437748cd8SNickeau * @var mixed the version 20537748cd8SNickeau */ 20637748cd8SNickeau private static $VERSION; 20737748cd8SNickeau 20837748cd8SNickeau 20937748cd8SNickeau /** 21037748cd8SNickeau * Initiate the static variable 21137748cd8SNickeau * See the call after this class 21237748cd8SNickeau */ 21337748cd8SNickeau static function init() 21437748cd8SNickeau { 21537748cd8SNickeau 21637748cd8SNickeau $pluginInfoFile = __DIR__ . '/../plugin.info.txt'; 21737748cd8SNickeau self::$INFO_PLUGIN = confToHash($pluginInfoFile); 21837748cd8SNickeau self::$PLUGIN_NAME = 'ComboStrap'; 21937748cd8SNickeau global $lang; 22037748cd8SNickeau self::$PLUGIN_LANG = $lang[self::PLUGIN_BASE_NAME]; 22137748cd8SNickeau self::$URL_BASE = "https://" . parse_url(self::$INFO_PLUGIN['url'], PHP_URL_HOST); 22237748cd8SNickeau self::$VERSION = self::$INFO_PLUGIN['version']; 22337748cd8SNickeau 22437748cd8SNickeau PluginUtility::initStaticManager(); 22537748cd8SNickeau 22637748cd8SNickeau } 22737748cd8SNickeau 22837748cd8SNickeau /** 22937748cd8SNickeau * @param $inputExpression 23037748cd8SNickeau * @return false|int 1|0 23137748cd8SNickeau * returns: 23237748cd8SNickeau * - 1 if the input expression is a pattern, 23337748cd8SNickeau * - 0 if not, 23437748cd8SNickeau * - FALSE if an error occurred. 23537748cd8SNickeau */ 23637748cd8SNickeau static function isRegularExpression($inputExpression) 23737748cd8SNickeau { 23837748cd8SNickeau 23937748cd8SNickeau $regularExpressionPattern = "/(\\/.*\\/[gmixXsuUAJ]?)/"; 24037748cd8SNickeau return preg_match($regularExpressionPattern, $inputExpression); 24137748cd8SNickeau 24237748cd8SNickeau } 24337748cd8SNickeau 24437748cd8SNickeau /** 24537748cd8SNickeau * Return a mode from a tag (ie from a {@link Plugin::getPluginComponent()} 24637748cd8SNickeau * @param $tag 24737748cd8SNickeau * @return string 24837748cd8SNickeau * 24937748cd8SNickeau * A mode is just a name for a class 25037748cd8SNickeau * Example: $Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock()); 25137748cd8SNickeau */ 25237748cd8SNickeau public static function getModeFromTag($tag) 25337748cd8SNickeau { 25437748cd8SNickeau return "plugin_" . self::getComponentName($tag); 25537748cd8SNickeau } 25637748cd8SNickeau 25737748cd8SNickeau /** 25837748cd8SNickeau * @param $tag 25937748cd8SNickeau * @return string 26037748cd8SNickeau * 26137748cd8SNickeau * Create a lookahead pattern for a container tag used to enter in a mode 26237748cd8SNickeau */ 26337748cd8SNickeau public static function getContainerTagPattern($tag) 26437748cd8SNickeau { 26537748cd8SNickeau // this pattern ensure that the tag 26637748cd8SNickeau // `accordion` will not intercept also the tag `accordionitem` 26737748cd8SNickeau // where: 26837748cd8SNickeau // ?: means non capturing group (to not capture the last >) 26937748cd8SNickeau // (\s.*?): is a capturing group that starts with a space 27037748cd8SNickeau $pattern = "(?:\s.*?>|>)"; 27137748cd8SNickeau return '<' . $tag . $pattern . '(?=.*?<\/' . $tag . '>)'; 27237748cd8SNickeau } 27337748cd8SNickeau 27437748cd8SNickeau /** 27537748cd8SNickeau * This pattern allows space after the tag name 27637748cd8SNickeau * for an end tag 27737748cd8SNickeau * As XHTML (https://www.w3.org/TR/REC-xml/#dt-etag) 27837748cd8SNickeau * @param $tag 27937748cd8SNickeau * @return string 28037748cd8SNickeau */ 28137748cd8SNickeau public static function getEndTagPattern($tag) 28237748cd8SNickeau { 28337748cd8SNickeau return "</$tag\s*>"; 28437748cd8SNickeau } 28537748cd8SNickeau 28637748cd8SNickeau /** 28737748cd8SNickeau * @param $tag 28837748cd8SNickeau * @return string 28937748cd8SNickeau * 29037748cd8SNickeau * Create a open tag pattern without lookahead. 29137748cd8SNickeau * Used for https://dev.w3.org/html5/html-author/#void-elements-0 29237748cd8SNickeau */ 29337748cd8SNickeau public static function getVoidElementTagPattern($tag) 29437748cd8SNickeau { 29537748cd8SNickeau return '<' . $tag . '.*?>'; 29637748cd8SNickeau } 29737748cd8SNickeau 29837748cd8SNickeau 29937748cd8SNickeau /** 30037748cd8SNickeau * Take an array where the key is the attribute name 30137748cd8SNickeau * and return a HTML tag string 30237748cd8SNickeau * 30337748cd8SNickeau * The attribute name and value are escaped 30437748cd8SNickeau * 30537748cd8SNickeau * @param $attributes - combo attributes 30637748cd8SNickeau * @return string 30737748cd8SNickeau * @deprecated to allowed background and other metadata, use {@link TagAttributes::toHtmlEnterTag()} 30837748cd8SNickeau */ 30937748cd8SNickeau public static function array2HTMLAttributesAsString($attributes) 31037748cd8SNickeau { 31137748cd8SNickeau 31237748cd8SNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 31337748cd8SNickeau return $tagAttributes->toHTMLAttributeString(); 31437748cd8SNickeau 31537748cd8SNickeau } 31637748cd8SNickeau 31737748cd8SNickeau /** 31837748cd8SNickeau * 31937748cd8SNickeau * Parse the attributes part of a match 32037748cd8SNickeau * 32137748cd8SNickeau * Example: 32237748cd8SNickeau * line-numbers="value" 32337748cd8SNickeau * line-numbers='value' 32437748cd8SNickeau * 32537748cd8SNickeau * This value may be in: 32637748cd8SNickeau * * configuration value 32737748cd8SNickeau * * as well as in the match of a {@link SyntaxPlugin} 32837748cd8SNickeau * 32937748cd8SNickeau * @param $string 33037748cd8SNickeau * @return array 33137748cd8SNickeau * 33237748cd8SNickeau * To parse a match, use {@link PluginUtility::getTagAttributes()} 33337748cd8SNickeau * 33437748cd8SNickeau * 33537748cd8SNickeau */ 33637748cd8SNickeau public static function parseAttributes($string) 33737748cd8SNickeau { 33837748cd8SNickeau 33937748cd8SNickeau $parameters = array(); 34037748cd8SNickeau 34137748cd8SNickeau // Rules 34237748cd8SNickeau // * name may be alone (ie true boolean attribute) 34337748cd8SNickeau // * a name may get a `-` 34437748cd8SNickeau // * there may be space every everywhere when the value is enclosed with a quote 34537748cd8SNickeau // * there may be no space in the value and between the equal sign when the value is not enclosed 34637748cd8SNickeau // 34737748cd8SNickeau // /i not case sensitive 34837748cd8SNickeau $attributePattern = '\s*([-\w]+)\s*(?:=(\s*[\'"]([^`"]*)[\'"]\s*|[^\s]*))?'; 34937748cd8SNickeau $result = preg_match_all('/' . $attributePattern . '/i', $string, $matches); 35037748cd8SNickeau if ($result != 0) { 35137748cd8SNickeau foreach ($matches[1] as $key => $parameterKey) { 35237748cd8SNickeau 35337748cd8SNickeau // group 3 (ie the value between quotes) 35437748cd8SNickeau $value = $matches[3][$key]; 35537748cd8SNickeau if ($value == "") { 35637748cd8SNickeau // check the value without quotes 35737748cd8SNickeau $value = $matches[2][$key]; 35837748cd8SNickeau } 35937748cd8SNickeau // if there is no value, this is a boolean 36037748cd8SNickeau if ($value == "") { 36137748cd8SNickeau $value = true; 36237748cd8SNickeau } else { 36337748cd8SNickeau $value = hsc($value); 36437748cd8SNickeau } 36537748cd8SNickeau $parameters[hsc(strtolower($parameterKey))] = $value; 36637748cd8SNickeau } 36737748cd8SNickeau } 36837748cd8SNickeau return $parameters; 36937748cd8SNickeau 37037748cd8SNickeau } 37137748cd8SNickeau 37237748cd8SNickeau public static function getTagAttributes($match) 37337748cd8SNickeau { 37437748cd8SNickeau return self::getQualifiedTagAttributes($match, false, ""); 37537748cd8SNickeau } 37637748cd8SNickeau 37737748cd8SNickeau /** 37837748cd8SNickeau * Return the attribute of a tag 37937748cd8SNickeau * Because they are users input, they are all escaped 38037748cd8SNickeau * @param $match 38137748cd8SNickeau * @param $hasThirdValue - if true, the third parameter is treated as value, not a property and returned in the `third` key 38237748cd8SNickeau * use for the code/file/console where they accept a name as third value 38337748cd8SNickeau * @param $keyThirdArgument - if a third argument is found, return it with this key 38437748cd8SNickeau * @return array 38537748cd8SNickeau */ 38637748cd8SNickeau public static function getQualifiedTagAttributes($match, $hasThirdValue, $keyThirdArgument) 38737748cd8SNickeau { 38837748cd8SNickeau 38937748cd8SNickeau $match = PluginUtility::getPreprocessEnterTag($match); 39037748cd8SNickeau 39137748cd8SNickeau // Suppress the tag name (ie until the first blank) 39237748cd8SNickeau $spacePosition = strpos($match, " "); 39337748cd8SNickeau if (!$spacePosition) { 39437748cd8SNickeau // No space, meaning this is only the tag name 39537748cd8SNickeau return array(); 39637748cd8SNickeau } 39737748cd8SNickeau $match = trim(substr($match, $spacePosition)); 39837748cd8SNickeau if ($match == "") { 39937748cd8SNickeau return array(); 40037748cd8SNickeau } 40137748cd8SNickeau 40237748cd8SNickeau // Do we have a type as first argument ? 40337748cd8SNickeau $attributes = array(); 40437748cd8SNickeau $spacePosition = strpos($match, " "); 40537748cd8SNickeau if ($spacePosition) { 40637748cd8SNickeau $nextArgument = substr($match, 0, $spacePosition); 40737748cd8SNickeau } else { 40837748cd8SNickeau $nextArgument = $match; 40937748cd8SNickeau } 41037748cd8SNickeau if (!strpos($nextArgument, "=")) { 41137748cd8SNickeau $attributes["type"] = $nextArgument; 41237748cd8SNickeau // Suppress the type 41337748cd8SNickeau $match = substr($match, strlen($nextArgument)); 41437748cd8SNickeau $match = trim($match); 41537748cd8SNickeau 41637748cd8SNickeau // Do we have a value as first argument ? 41737748cd8SNickeau if (!empty($hasThirdValue)) { 41837748cd8SNickeau $spacePosition = strpos($match, " "); 41937748cd8SNickeau if ($spacePosition) { 42037748cd8SNickeau $nextArgument = substr($match, 0, $spacePosition); 42137748cd8SNickeau } else { 42237748cd8SNickeau $nextArgument = $match; 42337748cd8SNickeau } 42437748cd8SNickeau if (!strpos($nextArgument, "=") && !empty($nextArgument)) { 42537748cd8SNickeau $attributes[$keyThirdArgument] = $nextArgument; 42637748cd8SNickeau // Suppress the third argument 42737748cd8SNickeau $match = substr($match, strlen($nextArgument)); 42837748cd8SNickeau $match = trim($match); 42937748cd8SNickeau } 43037748cd8SNickeau } 43137748cd8SNickeau } 43237748cd8SNickeau 43337748cd8SNickeau // Parse the remaining attributes 43437748cd8SNickeau $parsedAttributes = self::parseAttributes($match); 43537748cd8SNickeau 43637748cd8SNickeau // Merge 43737748cd8SNickeau $attributes = array_merge($attributes, $parsedAttributes);; 43837748cd8SNickeau 43937748cd8SNickeau return $attributes; 44037748cd8SNickeau 44137748cd8SNickeau } 44237748cd8SNickeau 44337748cd8SNickeau /** 44437748cd8SNickeau * @param array $styleProperties - an array of CSS properties with key, value 44537748cd8SNickeau * @return string - the value for the style attribute (ie all rules where joined with the comma) 44637748cd8SNickeau */ 44737748cd8SNickeau public static function array2InlineStyle(array $styleProperties) 44837748cd8SNickeau { 44937748cd8SNickeau $inlineCss = ""; 45037748cd8SNickeau foreach ($styleProperties as $key => $value) { 45137748cd8SNickeau $inlineCss .= "$key:$value;"; 45237748cd8SNickeau } 45337748cd8SNickeau // Suppress the last ; 45437748cd8SNickeau if ($inlineCss[strlen($inlineCss) - 1] == ";") { 45537748cd8SNickeau $inlineCss = substr($inlineCss, 0, -1); 45637748cd8SNickeau } 45737748cd8SNickeau return $inlineCss; 45837748cd8SNickeau } 45937748cd8SNickeau 46037748cd8SNickeau /** 46137748cd8SNickeau * @param $tag 46237748cd8SNickeau * @return string 46337748cd8SNickeau * Create a pattern used where the tag is not a container. 46437748cd8SNickeau * ie 46537748cd8SNickeau * <br/> 46637748cd8SNickeau * <icon/> 46737748cd8SNickeau * This is generally used with a subtition plugin 46837748cd8SNickeau * and a {@link Lexer::addSpecialPattern} state 46937748cd8SNickeau * where the tag is just replaced 47037748cd8SNickeau */ 47137748cd8SNickeau public static function getEmptyTagPattern($tag) 47237748cd8SNickeau { 47337748cd8SNickeau return '<' . $tag . '.*?/>'; 47437748cd8SNickeau } 47537748cd8SNickeau 47637748cd8SNickeau /** 47737748cd8SNickeau * Just call this function from a class like that 47837748cd8SNickeau * getTageName(get_called_class()) 47937748cd8SNickeau * to get the tag name (ie the component plugin) 48037748cd8SNickeau * of a syntax plugin 48137748cd8SNickeau * 48237748cd8SNickeau * @param $get_called_class 48337748cd8SNickeau * @return string 48437748cd8SNickeau */ 48537748cd8SNickeau public static function getTagName($get_called_class) 48637748cd8SNickeau { 48737748cd8SNickeau list(/* $t */, /* $p */, /* $n */, $c) = explode('_', $get_called_class, 4); 48837748cd8SNickeau return (isset($c) ? $c : ''); 48937748cd8SNickeau } 49037748cd8SNickeau 49137748cd8SNickeau /** 49237748cd8SNickeau * Just call this function from a class like that 49337748cd8SNickeau * getAdminPageName(get_called_class()) 49437748cd8SNickeau * to get the page name of a admin plugin 49537748cd8SNickeau * 49637748cd8SNickeau * @param $get_called_class 49737748cd8SNickeau * @return string - the admin page name 49837748cd8SNickeau */ 49937748cd8SNickeau public static function getAdminPageName($get_called_class) 50037748cd8SNickeau { 50137748cd8SNickeau $names = explode('_', $get_called_class); 50237748cd8SNickeau $names = array_slice($names, -2); 50337748cd8SNickeau return implode('_', $names); 50437748cd8SNickeau } 50537748cd8SNickeau 50637748cd8SNickeau public static function getNameSpace() 50737748cd8SNickeau { 50837748cd8SNickeau // No : at the begin of the namespace please 50937748cd8SNickeau return self::PLUGIN_BASE_NAME . ':'; 51037748cd8SNickeau } 51137748cd8SNickeau 51237748cd8SNickeau /** 51337748cd8SNickeau * @param $get_called_class - the plugin class 51437748cd8SNickeau * @return array 51537748cd8SNickeau */ 51637748cd8SNickeau public static function getTags($get_called_class) 51737748cd8SNickeau { 51837748cd8SNickeau $elements = array(); 51937748cd8SNickeau $elementName = PluginUtility::getTagName($get_called_class); 52037748cd8SNickeau $elements[] = $elementName; 52137748cd8SNickeau $elements[] = strtoupper($elementName); 52237748cd8SNickeau return $elements; 52337748cd8SNickeau } 52437748cd8SNickeau 52537748cd8SNickeau /** 52637748cd8SNickeau * Render a text 52737748cd8SNickeau * @param $pageContent 52837748cd8SNickeau * @return string|null 52937748cd8SNickeau */ 53037748cd8SNickeau public static function render($pageContent) 53137748cd8SNickeau { 53237748cd8SNickeau return RenderUtility::renderText2XhtmlAndStripPEventually($pageContent, false); 53337748cd8SNickeau } 53437748cd8SNickeau 53537748cd8SNickeau 53637748cd8SNickeau /** 53737748cd8SNickeau * This method will takes attributes 53837748cd8SNickeau * and process the plugin styling attribute such as width and height 53937748cd8SNickeau * to put them in a style HTML attribute 54037748cd8SNickeau * @param TagAttributes $attributes 54137748cd8SNickeau */ 54237748cd8SNickeau public static function processStyle(&$attributes) 54337748cd8SNickeau { 54437748cd8SNickeau // Style 54537748cd8SNickeau $styleAttributeName = "style"; 54637748cd8SNickeau if ($attributes->hasComponentAttribute($styleAttributeName)) { 54737748cd8SNickeau $properties = explode(";", $attributes->getValueAndRemove($styleAttributeName)); 54837748cd8SNickeau foreach ($properties as $property) { 54937748cd8SNickeau list($key, $value) = explode(":", $property); 55037748cd8SNickeau if ($key != "") { 55137748cd8SNickeau $attributes->addStyleDeclaration($key, $value); 55237748cd8SNickeau } 55337748cd8SNickeau } 55437748cd8SNickeau } 55537748cd8SNickeau 55637748cd8SNickeau 55737748cd8SNickeau /** 55837748cd8SNickeau * Border Color 55937748cd8SNickeau * For background color, see {@link TagAttributes::processBackground()} 56037748cd8SNickeau * For text color, see {@link TextColor} 56137748cd8SNickeau */ 56237748cd8SNickeau 56337748cd8SNickeau if ($attributes->hasComponentAttribute(ColorUtility::BORDER_COLOR)) { 56437748cd8SNickeau $colorValue = $attributes->getValueAndRemove(ColorUtility::BORDER_COLOR); 56537748cd8SNickeau $attributes->addStyleDeclaration(ColorUtility::BORDER_COLOR, ColorUtility::getColorValue($colorValue)); 56637748cd8SNickeau self::checkDefaultBorderColorAttributes($attributes); 56737748cd8SNickeau } 56837748cd8SNickeau 56937748cd8SNickeau 57037748cd8SNickeau } 57137748cd8SNickeau 57237748cd8SNickeau /** 57337748cd8SNickeau * Return the name of the requested script 57437748cd8SNickeau */ 57537748cd8SNickeau public 57637748cd8SNickeau static function getRequestScript() 57737748cd8SNickeau { 57837748cd8SNickeau $scriptPath = null; 57937748cd8SNickeau $testPropertyValue = self::getPropertyValue("SCRIPT_NAME"); 58037748cd8SNickeau if (defined('DOKU_UNITTEST') && $testPropertyValue != null) { 58137748cd8SNickeau return $testPropertyValue; 58237748cd8SNickeau } 58337748cd8SNickeau if (array_key_exists("DOCUMENT_URI", $_SERVER)) { 58437748cd8SNickeau $scriptPath = $_SERVER["DOCUMENT_URI"]; 58537748cd8SNickeau } 58637748cd8SNickeau if ($scriptPath == null && array_key_exists("SCRIPT_NAME", $_SERVER)) { 58737748cd8SNickeau $scriptPath = $_SERVER["SCRIPT_NAME"]; 58837748cd8SNickeau } 58937748cd8SNickeau if ($scriptPath == null) { 59037748cd8SNickeau msg("Unable to find the main script", LogUtility::LVL_MSG_ERROR); 59137748cd8SNickeau } 59237748cd8SNickeau $path_parts = pathinfo($scriptPath); 59337748cd8SNickeau return $path_parts['basename']; 59437748cd8SNickeau } 59537748cd8SNickeau 59637748cd8SNickeau /** 59737748cd8SNickeau * 59837748cd8SNickeau * @param $name 59937748cd8SNickeau * @param $default 60037748cd8SNickeau * @return string - the value of a query string property or if in test mode, the value of a test variable 60137748cd8SNickeau * set with {@link self::setTestProperty} 60237748cd8SNickeau * This is used to test script that are not supported by the dokuwiki test framework 60337748cd8SNickeau * such as css.php 60437748cd8SNickeau */ 60537748cd8SNickeau public 60637748cd8SNickeau static function getPropertyValue($name, $default = null) 60737748cd8SNickeau { 60837748cd8SNickeau global $INPUT; 60937748cd8SNickeau $value = $INPUT->str($name); 61037748cd8SNickeau if ($value == null && defined('DOKU_UNITTEST')) { 61137748cd8SNickeau global $COMBO; 61237748cd8SNickeau $value = $COMBO[$name]; 61337748cd8SNickeau } 61437748cd8SNickeau if ($value == null) { 61537748cd8SNickeau return $default; 61637748cd8SNickeau } else { 61737748cd8SNickeau return $value; 61837748cd8SNickeau } 61937748cd8SNickeau 62037748cd8SNickeau } 62137748cd8SNickeau 62237748cd8SNickeau /** 62337748cd8SNickeau * Create an URL to the documentation website 62437748cd8SNickeau * @param $canonical - canonical id or slug 62537748cd8SNickeau * @param $text - the text of the link 62637748cd8SNickeau * @param bool $withIcon - used to break the recursion with the message in the {@link Icon} 62737748cd8SNickeau * @return string - an url 62837748cd8SNickeau */ 62937748cd8SNickeau public 63037748cd8SNickeau static function getUrl($canonical, $text, $withIcon = true) 63137748cd8SNickeau { 63237748cd8SNickeau /** @noinspection SpellCheckingInspection */ 63337748cd8SNickeau 63437748cd8SNickeau $xhtmlIcon = ""; 63537748cd8SNickeau if ($withIcon) { 63637748cd8SNickeau 63737748cd8SNickeau /** 63837748cd8SNickeau * We don't include it as an external resource via url 63937748cd8SNickeau * because it then make a http request for every logo 64037748cd8SNickeau * in the configuration page and makes it really slow 64137748cd8SNickeau */ 64237748cd8SNickeau $path = File::createFromPath(Resources::getImagesDirectory() . "/logo.svg"); 64337748cd8SNickeau $tagAttributes = TagAttributes::createEmpty(SvgImageLink::CANONICAL); 64437748cd8SNickeau $tagAttributes->addComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ICON_TYPE); 64537748cd8SNickeau $tagAttributes->addComponentAttributeValue(Dimension::WIDTH_KEY, "20"); 64637748cd8SNickeau $cache = new CacheMedia($path, $tagAttributes); 64737748cd8SNickeau if (!$cache->isCacheUsable()) { 64837748cd8SNickeau $xhtmlIcon = SvgDocument::createFromPath($path) 64937748cd8SNickeau ->setShouldBeOptimized(true) 65037748cd8SNickeau ->getXmlText($tagAttributes); 65137748cd8SNickeau $cache->storeCache($xhtmlIcon); 65237748cd8SNickeau } 65337748cd8SNickeau $xhtmlIcon = file_get_contents($cache->getFile()->getFileSystemPath()); 65437748cd8SNickeau 65537748cd8SNickeau 65637748cd8SNickeau } 65737748cd8SNickeau return $xhtmlIcon . ' <a href="' . self::$URL_BASE . '/' . str_replace(":", "/", $canonical) . '" title="' . $text . '">' . $text . '</a>'; 65837748cd8SNickeau } 65937748cd8SNickeau 66037748cd8SNickeau /** 66137748cd8SNickeau * An utility function to not search every time which array should be first 66237748cd8SNickeau * @param array $inlineAttributes - the component inline attributes 66337748cd8SNickeau * @param array $defaultAttributes - the default configuration attributes 66437748cd8SNickeau * @return array - a merged array 66537748cd8SNickeau */ 66637748cd8SNickeau public 66737748cd8SNickeau static function mergeAttributes(array $inlineAttributes, array $defaultAttributes = array()) 66837748cd8SNickeau { 66937748cd8SNickeau return array_merge($defaultAttributes, $inlineAttributes); 67037748cd8SNickeau } 67137748cd8SNickeau 67237748cd8SNickeau /** 67337748cd8SNickeau * A pattern for a container tag 67437748cd8SNickeau * that needs to catch the content 67537748cd8SNickeau * 67637748cd8SNickeau * Use as a special pattern (substition) 67737748cd8SNickeau * 67837748cd8SNickeau * The {@link \syntax_plugin_combo_math} use it 67937748cd8SNickeau * @param $tag 68037748cd8SNickeau * @return string - a pattern 68137748cd8SNickeau */ 68237748cd8SNickeau public 68337748cd8SNickeau static function getLeafContainerTagPattern($tag) 68437748cd8SNickeau { 68537748cd8SNickeau return '<' . $tag . '.*?>.*?<\/' . $tag . '>'; 68637748cd8SNickeau } 68737748cd8SNickeau 68837748cd8SNickeau /** 68937748cd8SNickeau * Return the content of a tag 69037748cd8SNickeau * <math>Content</math> 69137748cd8SNickeau * @param $match 69237748cd8SNickeau * @return string the content 69337748cd8SNickeau */ 69437748cd8SNickeau public 69537748cd8SNickeau static function getTagContent($match) 69637748cd8SNickeau { 69737748cd8SNickeau // From the first > 69837748cd8SNickeau $start = strpos($match, ">"); 69937748cd8SNickeau if ($start == false) { 70037748cd8SNickeau LogUtility::msg("The match does not contain any opening tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 70137748cd8SNickeau return ""; 70237748cd8SNickeau } 70337748cd8SNickeau $match = substr($match, $start + 1); 70437748cd8SNickeau // If this is the last character, we get a false 70537748cd8SNickeau if ($match == false) { 70637748cd8SNickeau LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 70737748cd8SNickeau return ""; 70837748cd8SNickeau } 70937748cd8SNickeau 71037748cd8SNickeau $end = strrpos($match, "</"); 71137748cd8SNickeau if ($end == false) { 71237748cd8SNickeau LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 71337748cd8SNickeau return ""; 71437748cd8SNickeau } 71537748cd8SNickeau 71637748cd8SNickeau return substr($match, 0, $end); 71737748cd8SNickeau } 71837748cd8SNickeau 71937748cd8SNickeau /** 72037748cd8SNickeau * 72137748cd8SNickeau * Check if a HTML tag was already added for a request 72237748cd8SNickeau * The request id is just the timestamp 72337748cd8SNickeau * An indicator array should be provided 72437748cd8SNickeau * @return string 72537748cd8SNickeau */ 72637748cd8SNickeau public 72737748cd8SNickeau static function getRequestId() 72837748cd8SNickeau { 72937748cd8SNickeau 73037748cd8SNickeau if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { 73137748cd8SNickeau // since php 5.4 73237748cd8SNickeau $requestTime = $_SERVER['REQUEST_TIME_FLOAT']; 73337748cd8SNickeau } else { 73437748cd8SNickeau // DokuWiki test framework use this 73537748cd8SNickeau $requestTime = $_SERVER['REQUEST_TIME']; 73637748cd8SNickeau } 73737748cd8SNickeau $keyPrefix = 'combo_'; 73837748cd8SNickeau 73937748cd8SNickeau global $ID; 74037748cd8SNickeau return $keyPrefix . hash('crc32b', $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . $requestTime . $ID); 74137748cd8SNickeau 74237748cd8SNickeau } 74337748cd8SNickeau 74437748cd8SNickeau /** 74537748cd8SNickeau * Get the page id 74637748cd8SNickeau * If the page is a sidebar, it will not return the id of the sidebar 74737748cd8SNickeau * but the one of the page 74837748cd8SNickeau * @return string 74937748cd8SNickeau */ 75037748cd8SNickeau public 75137748cd8SNickeau static function getPageId() 75237748cd8SNickeau { 75337748cd8SNickeau return FsWikiUtility::getMainPageId(); 75437748cd8SNickeau } 75537748cd8SNickeau 75637748cd8SNickeau /** 75737748cd8SNickeau * Transform special HTML characters to entity 75837748cd8SNickeau * Example: 75937748cd8SNickeau * <hello>world</hello> 76037748cd8SNickeau * to 76137748cd8SNickeau * "<hello>world</hello>" 76237748cd8SNickeau * 76337748cd8SNickeau * @param $text 76437748cd8SNickeau * @return string 76537748cd8SNickeau */ 76637748cd8SNickeau public 76737748cd8SNickeau static function htmlEncode($text) 76837748cd8SNickeau { 76937748cd8SNickeau /** 77037748cd8SNickeau * See https://stackoverflow.com/questions/46483/htmlentities-vs-htmlspecialchars/3614344 77137748cd8SNickeau * {@link htmlentities } 77237748cd8SNickeau */ 77337748cd8SNickeau //return htmlspecialchars($text, ENT_QUOTES); 77437748cd8SNickeau return htmlentities($text); 77537748cd8SNickeau } 77637748cd8SNickeau 77737748cd8SNickeau 77837748cd8SNickeau /** 77937748cd8SNickeau * Add a class 78037748cd8SNickeau * @param $classValue 78137748cd8SNickeau * @param array $attributes 78237748cd8SNickeau */ 78337748cd8SNickeau public 78437748cd8SNickeau static function addClass2Attributes($classValue, array &$attributes) 78537748cd8SNickeau { 78637748cd8SNickeau self::addAttributeValue("class", $classValue, $attributes); 78737748cd8SNickeau } 78837748cd8SNickeau 78937748cd8SNickeau /** 79037748cd8SNickeau * Add a style property to the attributes 79137748cd8SNickeau * @param $property 79237748cd8SNickeau * @param $value 79337748cd8SNickeau * @param array $attributes 79437748cd8SNickeau * @deprecated use {@link TagAttributes::addStyleDeclaration()} instead 79537748cd8SNickeau */ 79637748cd8SNickeau public 79737748cd8SNickeau static function addStyleProperty($property, $value, array &$attributes) 79837748cd8SNickeau { 79937748cd8SNickeau if (isset($attributes["style"])) { 80037748cd8SNickeau $attributes["style"] .= ";$property:$value"; 80137748cd8SNickeau } else { 80237748cd8SNickeau $attributes["style"] = "$property:$value"; 80337748cd8SNickeau } 80437748cd8SNickeau 80537748cd8SNickeau } 80637748cd8SNickeau 80737748cd8SNickeau /** 80837748cd8SNickeau * Add default border attributes 80937748cd8SNickeau * to see a border 81037748cd8SNickeau * Doc 81137748cd8SNickeau * https://combostrap.com/styling/color#border_color 81237748cd8SNickeau * @param TagAttributes $tagAttributes 81337748cd8SNickeau */ 81437748cd8SNickeau private 81537748cd8SNickeau static function checkDefaultBorderColorAttributes(&$tagAttributes) 81637748cd8SNickeau { 81737748cd8SNickeau /** 81837748cd8SNickeau * border color was set without the width 81937748cd8SNickeau * setting the width 82037748cd8SNickeau */ 82137748cd8SNickeau if (!( 82237748cd8SNickeau $tagAttributes->hasStyleDeclaration("border") 82337748cd8SNickeau || 82437748cd8SNickeau $tagAttributes->hasStyleDeclaration("border-width") 82537748cd8SNickeau ) 82637748cd8SNickeau ) { 82737748cd8SNickeau $tagAttributes->addStyleDeclaration("border-width", "1px"); 82837748cd8SNickeau } 82937748cd8SNickeau /** 83037748cd8SNickeau * border color was set without the style 83137748cd8SNickeau * setting the style 83237748cd8SNickeau */ 83337748cd8SNickeau if (! 83437748cd8SNickeau ( 83537748cd8SNickeau $tagAttributes->hasStyleDeclaration("border") 83637748cd8SNickeau || 83737748cd8SNickeau $tagAttributes->hasStyleDeclaration("border-style") 83837748cd8SNickeau ) 83937748cd8SNickeau ) { 84037748cd8SNickeau $tagAttributes->addStyleDeclaration("border-style", "solid"); 84137748cd8SNickeau 84237748cd8SNickeau } 84337748cd8SNickeau if (!$tagAttributes->hasStyleDeclaration("border-radius")) { 84437748cd8SNickeau $tagAttributes->addStyleDeclaration("border-radius", ".25rem"); 84537748cd8SNickeau } 84637748cd8SNickeau 84737748cd8SNickeau } 84837748cd8SNickeau 84937748cd8SNickeau public 85037748cd8SNickeau static function getConfValue($confName, $defaultValue = null) 85137748cd8SNickeau { 85237748cd8SNickeau global $conf; 85337748cd8SNickeau if (isset($conf['plugin'][PluginUtility::PLUGIN_BASE_NAME][$confName])) { 85437748cd8SNickeau return $conf['plugin'][PluginUtility::PLUGIN_BASE_NAME][$confName]; 85537748cd8SNickeau } else { 85637748cd8SNickeau return $defaultValue; 85737748cd8SNickeau } 85837748cd8SNickeau } 85937748cd8SNickeau 86037748cd8SNickeau /** 86137748cd8SNickeau * @param $match 86237748cd8SNickeau * @return null|string - return the tag name or null if not found 86337748cd8SNickeau */ 86437748cd8SNickeau public 86537748cd8SNickeau static function getTag($match) 86637748cd8SNickeau { 86737748cd8SNickeau 86837748cd8SNickeau // Trim to start clean 86937748cd8SNickeau $match = trim($match); 87037748cd8SNickeau 87137748cd8SNickeau // Until the first > 87237748cd8SNickeau $pos = strpos($match, ">"); 87337748cd8SNickeau if ($pos == false) { 87437748cd8SNickeau LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_ERROR); 87537748cd8SNickeau return null; 87637748cd8SNickeau } 87737748cd8SNickeau $match = substr($match, 0, $pos); 87837748cd8SNickeau 87937748cd8SNickeau // Suppress the < 88037748cd8SNickeau if ($match[0] == "<") { 88137748cd8SNickeau $match = substr($match, 1); 88237748cd8SNickeau } else { 88337748cd8SNickeau LogUtility::msg("This is not a text tag because it does not start with the character `>`"); 88437748cd8SNickeau } 88537748cd8SNickeau 88637748cd8SNickeau // Suppress the tag name (ie until the first blank) 88737748cd8SNickeau $spacePosition = strpos($match, " "); 88837748cd8SNickeau if (!$spacePosition) { 88937748cd8SNickeau // No space, meaning this is only the tag name 89037748cd8SNickeau return $match; 89137748cd8SNickeau } else { 89237748cd8SNickeau return substr($match, 0, $spacePosition); 89337748cd8SNickeau } 89437748cd8SNickeau 89537748cd8SNickeau } 89637748cd8SNickeau 89737748cd8SNickeau 89837748cd8SNickeau /** 89937748cd8SNickeau * @param string $string add a command into HTML 90037748cd8SNickeau */ 90137748cd8SNickeau public 90237748cd8SNickeau static function addAsHtmlComment($string) 90337748cd8SNickeau { 90437748cd8SNickeau print_r('<!-- ' . self::htmlEncode($string) . '-->'); 90537748cd8SNickeau } 90637748cd8SNickeau 90737748cd8SNickeau public 90837748cd8SNickeau static function getResourceBaseUrl() 90937748cd8SNickeau { 91037748cd8SNickeau return DOKU_URL . 'lib/plugins/' . PluginUtility::PLUGIN_BASE_NAME . '/resources'; 91137748cd8SNickeau } 91237748cd8SNickeau 91337748cd8SNickeau /** 91437748cd8SNickeau * @param $TAG - the name of the tag that should correspond to the name of the css file in the style directory 91537748cd8SNickeau * @return string - a inline style element to inject in the page or blank if no file exists 91637748cd8SNickeau */ 91737748cd8SNickeau public 91837748cd8SNickeau static function getTagStyle($TAG) 91937748cd8SNickeau { 92037748cd8SNickeau $script = self::getCssRules($TAG); 92137748cd8SNickeau if (!empty($script)) { 92237748cd8SNickeau return "<style>" . $script . "</style>"; 92337748cd8SNickeau } else { 92437748cd8SNickeau return ""; 92537748cd8SNickeau } 92637748cd8SNickeau 92737748cd8SNickeau } 92837748cd8SNickeau 92937748cd8SNickeau 93037748cd8SNickeau public 93137748cd8SNickeau static function getComponentName($tag) 93237748cd8SNickeau { 93337748cd8SNickeau return strtolower(PluginUtility::PLUGIN_BASE_NAME) . "_" . $tag; 93437748cd8SNickeau } 93537748cd8SNickeau 93637748cd8SNickeau public 93737748cd8SNickeau static function addAttributeValue($attribute, $value, array &$attributes) 93837748cd8SNickeau { 93937748cd8SNickeau if (array_key_exists($attribute, $attributes) && $attributes[$attribute] !== "") { 94037748cd8SNickeau $attributes[$attribute] .= " {$value}"; 94137748cd8SNickeau } else { 94237748cd8SNickeau $attributes[$attribute] = "{$value}"; 94337748cd8SNickeau } 94437748cd8SNickeau } 94537748cd8SNickeau 94637748cd8SNickeau /** 94737748cd8SNickeau * Plugin Utility is available to all plugin, 94837748cd8SNickeau * this is a convenient way to the the snippet manager 94937748cd8SNickeau * @return SnippetManager 95037748cd8SNickeau */ 95137748cd8SNickeau public 95237748cd8SNickeau static function getSnippetManager() 95337748cd8SNickeau { 95437748cd8SNickeau return SnippetManager::get(); 95537748cd8SNickeau } 95637748cd8SNickeau 95737748cd8SNickeau public 95837748cd8SNickeau static function initStaticManager() 95937748cd8SNickeau { 96037748cd8SNickeau CacheManager::init(); 96137748cd8SNickeau SnippetManager::init(); 96237748cd8SNickeau } 96337748cd8SNickeau 96437748cd8SNickeau /** 96537748cd8SNickeau * Function used in a render 96637748cd8SNickeau * @param $data - the data from {@link PluginUtility::handleAndReturnUnmatchedData()} 96737748cd8SNickeau * @return string 96837748cd8SNickeau */ 96937748cd8SNickeau public 97037748cd8SNickeau static function renderUnmatched($data) 97137748cd8SNickeau { 97237748cd8SNickeau /** 97337748cd8SNickeau * Attributes 97437748cd8SNickeau */ 97537748cd8SNickeau if (isset($data[PluginUtility::ATTRIBUTES])) { 97637748cd8SNickeau $attributes = $data[PluginUtility::ATTRIBUTES]; 97737748cd8SNickeau } else { 97837748cd8SNickeau $attributes = []; 97937748cd8SNickeau } 98037748cd8SNickeau $tagAttributes = TagAttributes::createFromCallStackArray($attributes); 98137748cd8SNickeau $display = $tagAttributes->getValue(TagAttributes::DISPLAY); 98237748cd8SNickeau if ($display != "none") { 98337748cd8SNickeau $payload = $data[self::PAYLOAD]; 984*1fa8c418SNickeau $previousTagDisplayType = $data[self::CONTEXT]; 985*1fa8c418SNickeau if ($previousTagDisplayType !== Call::INLINE_DISPLAY) { 98637748cd8SNickeau $payload = ltrim($payload); 98737748cd8SNickeau } 98837748cd8SNickeau return PluginUtility::htmlEncode($payload); 98937748cd8SNickeau } else { 99037748cd8SNickeau return ""; 99137748cd8SNickeau } 99237748cd8SNickeau } 99337748cd8SNickeau 99437748cd8SNickeau /** 99537748cd8SNickeau * Function used in a handle function of a syntax plugin for 99637748cd8SNickeau * unmatched context 99737748cd8SNickeau * @param $tagName 99837748cd8SNickeau * @param $match 99937748cd8SNickeau * @param \Doku_Handler $handler 100037748cd8SNickeau * @return array 100137748cd8SNickeau */ 100237748cd8SNickeau public 1003*1fa8c418SNickeau static function handleAndReturnUnmatchedData($tagName, $match, \Doku_Handler $handler): array 100437748cd8SNickeau { 1005*1fa8c418SNickeau $callStack = CallStack::createFromHandler($handler); 1006*1fa8c418SNickeau $sibling = $callStack->previous(); 100737748cd8SNickeau $context = null; 100837748cd8SNickeau if (!empty($sibling)) { 1009*1fa8c418SNickeau $context = $sibling->getDisplay(); 101037748cd8SNickeau } 101137748cd8SNickeau return array( 101237748cd8SNickeau PluginUtility::STATE => DOKU_LEXER_UNMATCHED, 101337748cd8SNickeau PluginUtility::PAYLOAD => $match, 101437748cd8SNickeau PluginUtility::CONTEXT => $context 101537748cd8SNickeau ); 101637748cd8SNickeau } 101737748cd8SNickeau 101837748cd8SNickeau public 101937748cd8SNickeau static function setConf($key, $value, $namespace = 'plugin') 102037748cd8SNickeau { 102137748cd8SNickeau global $conf; 102237748cd8SNickeau if ($namespace != null) { 102337748cd8SNickeau $conf[$namespace][PluginUtility::PLUGIN_BASE_NAME][$key] = $value; 102437748cd8SNickeau } else { 102537748cd8SNickeau $conf[$key] = $value; 102637748cd8SNickeau } 102737748cd8SNickeau 102837748cd8SNickeau } 102937748cd8SNickeau 103037748cd8SNickeau /** 103137748cd8SNickeau * Utility methodPreprocess a start tag to be able to extract the name 103237748cd8SNickeau * and the attributes easily 103337748cd8SNickeau * 103437748cd8SNickeau * It will delete: 103537748cd8SNickeau * * the characters <> and the /> if present 103637748cd8SNickeau * * and trim 103737748cd8SNickeau * 103837748cd8SNickeau * It will remain the tagname and its attributes 103937748cd8SNickeau * @param $match 104037748cd8SNickeau * @return false|string|null 104137748cd8SNickeau */ 104237748cd8SNickeau private 104337748cd8SNickeau static function getPreprocessEnterTag($match) 104437748cd8SNickeau { 104537748cd8SNickeau // Until the first > 104637748cd8SNickeau $pos = strpos($match, ">"); 104737748cd8SNickeau if ($pos == false) { 104837748cd8SNickeau LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_WARNING); 104937748cd8SNickeau return null; 105037748cd8SNickeau } 105137748cd8SNickeau $match = substr($match, 0, $pos); 105237748cd8SNickeau 105337748cd8SNickeau 105437748cd8SNickeau // Trim to start clean 105537748cd8SNickeau $match = trim($match); 105637748cd8SNickeau 105737748cd8SNickeau // Suppress the < 105837748cd8SNickeau if ($match[0] == "<") { 105937748cd8SNickeau $match = substr($match, 1); 106037748cd8SNickeau } 106137748cd8SNickeau 106237748cd8SNickeau // Suppress the / for a leaf tag 106337748cd8SNickeau if ($match[strlen($match) - 1] == "/") { 106437748cd8SNickeau $match = substr($match, 0, strlen($match) - 1); 106537748cd8SNickeau } 106637748cd8SNickeau return $match; 106737748cd8SNickeau } 106837748cd8SNickeau 106937748cd8SNickeau /** 107037748cd8SNickeau * Retrieve the tag name used in the text document 107137748cd8SNickeau * @param $match 107237748cd8SNickeau * @return false|string|null 107337748cd8SNickeau */ 107437748cd8SNickeau public 107537748cd8SNickeau static function getSyntaxTagNameFromMatch($match) 107637748cd8SNickeau { 107737748cd8SNickeau $preprocessMatch = PluginUtility::getPreprocessEnterTag($match); 107837748cd8SNickeau 107937748cd8SNickeau // Tag name (ie until the first blank) 108037748cd8SNickeau $spacePosition = strpos($match, " "); 108137748cd8SNickeau if (!$spacePosition) { 108237748cd8SNickeau // No space, meaning this is only the tag name 108337748cd8SNickeau return $preprocessMatch; 108437748cd8SNickeau } else { 108537748cd8SNickeau return trim(substr(0, $spacePosition)); 108637748cd8SNickeau } 108737748cd8SNickeau 108837748cd8SNickeau } 108937748cd8SNickeau 109037748cd8SNickeau /** 109137748cd8SNickeau * @param \Doku_Renderer_xhtml $renderer 109237748cd8SNickeau * @param $position 109337748cd8SNickeau * @param $name 109437748cd8SNickeau */ 109537748cd8SNickeau public 109637748cd8SNickeau static function startSection($renderer, $position, $name) 109737748cd8SNickeau { 109837748cd8SNickeau 109937748cd8SNickeau 110037748cd8SNickeau if (empty($position)) { 110137748cd8SNickeau LogUtility::msg("The position for a start section should not be empty", LogUtility::LVL_MSG_ERROR, "support"); 110237748cd8SNickeau } 110337748cd8SNickeau if (empty($name)) { 110437748cd8SNickeau LogUtility::msg("The name for a start section should not be empty", LogUtility::LVL_MSG_ERROR, "support"); 110537748cd8SNickeau } 110637748cd8SNickeau 110737748cd8SNickeau /** 110837748cd8SNickeau * New Dokuwiki Version 110937748cd8SNickeau * for DokuWiki Greebo and more recent versions 111037748cd8SNickeau */ 111137748cd8SNickeau if (defined('SEC_EDIT_PATTERN')) { 111237748cd8SNickeau $renderer->startSectionEdit($position, array('target' => self::EDIT_SECTION_TARGET, 'name' => $name)); 111337748cd8SNickeau } else { 111437748cd8SNickeau /** 111537748cd8SNickeau * Old version 111637748cd8SNickeau */ 111737748cd8SNickeau /** @noinspection PhpParamsInspection */ 111837748cd8SNickeau $renderer->startSectionEdit($position, self::EDIT_SECTION_TARGET, $name); 111937748cd8SNickeau } 112037748cd8SNickeau } 112137748cd8SNickeau 112237748cd8SNickeau /** 112337748cd8SNickeau * Add an enter call to the stack 112437748cd8SNickeau * @param \Doku_Handler $handler 112537748cd8SNickeau * @param $tagName 112637748cd8SNickeau * @param array $callStackArray 112737748cd8SNickeau */ 112837748cd8SNickeau public 112937748cd8SNickeau static function addEnterCall( 113037748cd8SNickeau \Doku_Handler &$handler, 113137748cd8SNickeau $tagName, 113237748cd8SNickeau $callStackArray = array() 113337748cd8SNickeau ) 113437748cd8SNickeau { 113537748cd8SNickeau $pluginName = PluginUtility::getComponentName($tagName); 113637748cd8SNickeau $handler->addPluginCall( 113737748cd8SNickeau $pluginName, 113837748cd8SNickeau $callStackArray, 113937748cd8SNickeau DOKU_LEXER_ENTER, 114037748cd8SNickeau null, 114137748cd8SNickeau null 114237748cd8SNickeau ); 114337748cd8SNickeau } 114437748cd8SNickeau 114537748cd8SNickeau /** 114637748cd8SNickeau * Add an end call dynamically 114737748cd8SNickeau * @param \Doku_Handler $handler 114837748cd8SNickeau * @param $tagName 114937748cd8SNickeau * @param array $callStackArray 115037748cd8SNickeau */ 115137748cd8SNickeau public 115237748cd8SNickeau static function addEndCall(\Doku_Handler $handler, $tagName, $callStackArray = array()) 115337748cd8SNickeau { 115437748cd8SNickeau $pluginName = PluginUtility::getComponentName($tagName); 115537748cd8SNickeau $handler->addPluginCall( 115637748cd8SNickeau $pluginName, 115737748cd8SNickeau $callStackArray, 115837748cd8SNickeau DOKU_LEXER_END, 115937748cd8SNickeau null, 116037748cd8SNickeau null 116137748cd8SNickeau ); 116237748cd8SNickeau } 116337748cd8SNickeau 116437748cd8SNickeau /** 116537748cd8SNickeau * General Debug 116637748cd8SNickeau */ 116737748cd8SNickeau public 116837748cd8SNickeau static function isDebug() 116937748cd8SNickeau { 117037748cd8SNickeau global $conf; 117137748cd8SNickeau return $conf["allowdebug"] === 1; 117237748cd8SNickeau 117337748cd8SNickeau } 117437748cd8SNickeau 117537748cd8SNickeau /** 117637748cd8SNickeau * @return bool true if loaded, false otherwise 117737748cd8SNickeau * Strap is loaded only if this is the same version 117837748cd8SNickeau * to avoid function, class, or members that does not exist 117937748cd8SNickeau */ 118037748cd8SNickeau public 118137748cd8SNickeau static function loadStrapUtilityTemplateIfPresentAndSameVersion() 118237748cd8SNickeau { 118337748cd8SNickeau $templateUtilityFile = __DIR__ . '/../../../tpl/strap/class/TplUtility.php'; 118437748cd8SNickeau if (file_exists($templateUtilityFile)) { 118537748cd8SNickeau /** 118637748cd8SNickeau * Check the version 118737748cd8SNickeau */ 118837748cd8SNickeau $templateInfo = confToHash(__DIR__ . '/../../../tpl/strap/template.info.txt'); 118937748cd8SNickeau $templateVersion = $templateInfo['version']; 119037748cd8SNickeau $comboVersion = self::$INFO_PLUGIN['version']; 119137748cd8SNickeau if ($templateVersion != $comboVersion) { 119237748cd8SNickeau if ($comboVersion > $templateVersion) { 119337748cd8SNickeau 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)."); 119437748cd8SNickeau } else { 119537748cd8SNickeau 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)."); 119637748cd8SNickeau } 119737748cd8SNickeau return false; 119837748cd8SNickeau } else { 119937748cd8SNickeau /** @noinspection PhpIncludeInspection */ 120037748cd8SNickeau require_once($templateUtilityFile); 120137748cd8SNickeau return true; 120237748cd8SNickeau } 120337748cd8SNickeau } else { 120437748cd8SNickeau $level = LogUtility::LVL_MSG_DEBUG; 120537748cd8SNickeau if (defined('DOKU_UNITTEST')) { 120637748cd8SNickeau // fail 120737748cd8SNickeau $level = LogUtility::LVL_MSG_ERROR; 120837748cd8SNickeau } 120937748cd8SNickeau if (Site::getTemplate() != "strap") { 121037748cd8SNickeau LogUtility::msg("The strap template is not installed", $level); 121137748cd8SNickeau } else { 121237748cd8SNickeau LogUtility::msg("The file ($templateUtilityFile) was not found", $level); 121337748cd8SNickeau } 121437748cd8SNickeau return false; 121537748cd8SNickeau } 121637748cd8SNickeau } 121737748cd8SNickeau 121837748cd8SNickeau 121937748cd8SNickeau /** 122037748cd8SNickeau * 122137748cd8SNickeau * See also dev.md file 122237748cd8SNickeau */ 122337748cd8SNickeau public static function isDevOrTest() 122437748cd8SNickeau { 122537748cd8SNickeau if (self::isDev()) { 122637748cd8SNickeau return true; 122737748cd8SNickeau } 122837748cd8SNickeau return self::isTest(); 122937748cd8SNickeau } 123037748cd8SNickeau 123137748cd8SNickeau public static function isDev() 123237748cd8SNickeau { 123337748cd8SNickeau global $_SERVER; 123437748cd8SNickeau if ($_SERVER["REMOTE_ADDR"] == "127.0.0.1") { 123537748cd8SNickeau return true; 123637748cd8SNickeau } 123737748cd8SNickeau return false; 123837748cd8SNickeau } 123937748cd8SNickeau 124037748cd8SNickeau public static function getInstructions($markiCode) 124137748cd8SNickeau { 124237748cd8SNickeau return p_get_instructions($markiCode); 124337748cd8SNickeau } 124437748cd8SNickeau 124537748cd8SNickeau public static function getInstructionsWithoutRoot($markiCode) 124637748cd8SNickeau { 124737748cd8SNickeau return RenderUtility::getInstructionsAndStripPEventually($markiCode); 124837748cd8SNickeau } 124937748cd8SNickeau 125037748cd8SNickeau /** 125137748cd8SNickeau * Transform a text into a valid HTML id 125237748cd8SNickeau * @param $string 125337748cd8SNickeau * @return string 125437748cd8SNickeau */ 125537748cd8SNickeau public static function toHtmlId($string) 125637748cd8SNickeau { 125737748cd8SNickeau /** 125837748cd8SNickeau * sectionId calls cleanID 125937748cd8SNickeau * cleanID delete all things before a ':' 126037748cd8SNickeau * we do then the replace before to not 126137748cd8SNickeau * lost a minus '-' separator 126237748cd8SNickeau */ 126337748cd8SNickeau $string = str_replace(array(':', '.'), '', $string); 126437748cd8SNickeau return sectionID($string, $check); 126537748cd8SNickeau } 126637748cd8SNickeau 126737748cd8SNickeau public static function isTest() 126837748cd8SNickeau { 126937748cd8SNickeau return defined('DOKU_UNITTEST'); 127037748cd8SNickeau } 127137748cd8SNickeau 127237748cd8SNickeau 127337748cd8SNickeau public static function getCacheManager() 127437748cd8SNickeau { 127537748cd8SNickeau return CacheManager::get(); 127637748cd8SNickeau } 127737748cd8SNickeau 127837748cd8SNickeau public static function getModeFromPluginName($name) 127937748cd8SNickeau { 128037748cd8SNickeau return "plugin_$name"; 128137748cd8SNickeau } 128237748cd8SNickeau 128337748cd8SNickeau public static function isCi(): bool 128437748cd8SNickeau { 128537748cd8SNickeau // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables 128637748cd8SNickeau return getenv("CI") === "true"; 128737748cd8SNickeau } 128837748cd8SNickeau 1289*1fa8c418SNickeau /** 1290*1fa8c418SNickeau * An helper function to not exit when it's a test environment 1291*1fa8c418SNickeau * @param string $message 1292*1fa8c418SNickeau */ 1293*1fa8c418SNickeau public static function softExit($message = null) 1294*1fa8c418SNickeau { 1295*1fa8c418SNickeau 1296*1fa8c418SNickeau if (!PluginUtility::isTest()) { 1297*1fa8c418SNickeau exit; 1298*1fa8c418SNickeau } else { 1299*1fa8c418SNickeau throw new ExitException($message); 1300*1fa8c418SNickeau } 1301*1fa8c418SNickeau 1302*1fa8c418SNickeau } 1303*1fa8c418SNickeau 130437748cd8SNickeau 130537748cd8SNickeau} 130637748cd8SNickeau 130737748cd8SNickeauPluginUtility::init(); 1308