xref: /plugin/combo/ComboStrap/PluginUtility.php (revision d9ddda896750ea9609c87ec1e5756a5df8f34279)
137748cd8SNickeau<?php
237748cd8SNickeau
337748cd8SNickeau
437748cd8SNickeaunamespace ComboStrap;
537748cd8SNickeau
637748cd8SNickeau
704fd306cSNickeauuse ComboStrap\Api\ApiRouter;
837748cd8SNickeauuse dokuwiki\Extension\Plugin;
937748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin;
1037748cd8SNickeau
1137748cd8SNickeau
1237748cd8SNickeau/**
1337748cd8SNickeau * Class url static
1437748cd8SNickeau * List of static utilities
1537748cd8SNickeau */
1637748cd8SNickeauclass PluginUtility
1737748cd8SNickeau{
1837748cd8SNickeau
1937748cd8SNickeau    const DOKU_DATA_DIR = '/dokudata/pages';
2037748cd8SNickeau    const DOKU_CACHE_DIR = '/dokudata/cache';
2137748cd8SNickeau
2237748cd8SNickeau    /**
2337748cd8SNickeau     * Key in the data array between the handle and render function
2437748cd8SNickeau     */
2537748cd8SNickeau    const STATE = "state";
2637748cd8SNickeau    const PAYLOAD = "payload"; // The html or text
2737748cd8SNickeau    const ATTRIBUTES = "attributes";
2837748cd8SNickeau// The context is generally the parent tag but it may be also the grandfather.
2937748cd8SNickeau// It permits to determine the HTML that is outputted
3037748cd8SNickeau    const CONTEXT = 'context';
3137748cd8SNickeau    const TAG = "tag";
3237748cd8SNickeau
3337748cd8SNickeau    /**
3437748cd8SNickeau     * The name of the hidden/private namespace
3537748cd8SNickeau     * where the icon and other artifactory are stored
3637748cd8SNickeau     */
3737748cd8SNickeau    const COMBOSTRAP_NAMESPACE_NAME = "combostrap";
3837748cd8SNickeau
3937748cd8SNickeau    const PARENT = "parent";
4037748cd8SNickeau    const POSITION = "position";
4137748cd8SNickeau
4237748cd8SNickeau
4304fd306cSNickeau    const EXIT_MESSAGE = "exit_message";
444cadd4f8SNickeau    const EXIT_CODE = "exit_code";
4504fd306cSNickeau
461fa8c418SNickeau    const DISPLAY = "display";
4704fd306cSNickeau    const MARKUP_TAG = "markup-tag";
4804fd306cSNickeau
4937748cd8SNickeau
5037748cd8SNickeau    /**
5137748cd8SNickeau     * The URL base of the documentation
5237748cd8SNickeau     */
53c3437056SNickeau    static $URL_APEX;
5437748cd8SNickeau
5537748cd8SNickeau
5637748cd8SNickeau    /**
5737748cd8SNickeau     * @var string - the plugin base name (ie the directory)
5837748cd8SNickeau     * ie $INFO_PLUGIN['base'];
5937748cd8SNickeau     * This is a constant because it permits code analytics
6037748cd8SNickeau     * such as verification of a path
6137748cd8SNickeau     */
6237748cd8SNickeau    const PLUGIN_BASE_NAME = "combo";
6337748cd8SNickeau
6437748cd8SNickeau    /**
6537748cd8SNickeau     * The name of the template plugin
6637748cd8SNickeau     */
6737748cd8SNickeau    const TEMPLATE_STRAP_NAME = "strap";
6837748cd8SNickeau
6937748cd8SNickeau    /**
7037748cd8SNickeau     * @var array
7137748cd8SNickeau     */
7237748cd8SNickeau    static $INFO_PLUGIN;
7337748cd8SNickeau
7437748cd8SNickeau    static $PLUGIN_LANG;
7537748cd8SNickeau
7637748cd8SNickeau    /**
7737748cd8SNickeau     * The plugin name
7837748cd8SNickeau     * (not the same than the base as it's not related to the directory
7937748cd8SNickeau     * @var string
8037748cd8SNickeau     */
8137748cd8SNickeau    public static $PLUGIN_NAME;
8237748cd8SNickeau    /**
8304fd306cSNickeau     * @var LocalPath
8437748cd8SNickeau     */
8504fd306cSNickeau    private static $PLUGIN_INFO_FILE;
8637748cd8SNickeau
8737748cd8SNickeau
8837748cd8SNickeau    /**
8937748cd8SNickeau     * Initiate the static variable
9037748cd8SNickeau     * See the call after this class
9137748cd8SNickeau     */
9237748cd8SNickeau    static function init()
9337748cd8SNickeau    {
9437748cd8SNickeau
9504fd306cSNickeau        $pluginInfoFile = DirectoryLayout::getPluginInfoPath();
9604fd306cSNickeau        self::$INFO_PLUGIN = confToHash($pluginInfoFile->toAbsoluteId());
9737748cd8SNickeau        self::$PLUGIN_NAME = 'ComboStrap';
9837748cd8SNickeau        global $lang;
9937748cd8SNickeau        self::$PLUGIN_LANG = $lang[self::PLUGIN_BASE_NAME];
100c3437056SNickeau        self::$URL_APEX = "https://" . parse_url(self::$INFO_PLUGIN['url'], PHP_URL_HOST);
10104fd306cSNickeau        //self::$VERSION = self::$INFO_PLUGIN['version'];
10237748cd8SNickeau
10337748cd8SNickeau    }
10437748cd8SNickeau
10537748cd8SNickeau    /**
10637748cd8SNickeau     * @param $inputExpression
10737748cd8SNickeau     * @return false|int 1|0
10837748cd8SNickeau     * returns:
10937748cd8SNickeau     *    - 1 if the input expression is a pattern,
11037748cd8SNickeau     *    - 0 if not,
11137748cd8SNickeau     *    - FALSE if an error occurred.
11237748cd8SNickeau     */
11337748cd8SNickeau    static function isRegularExpression($inputExpression)
11437748cd8SNickeau    {
11537748cd8SNickeau
11637748cd8SNickeau        $regularExpressionPattern = "/(\\/.*\\/[gmixXsuUAJ]?)/";
11737748cd8SNickeau        return preg_match($regularExpressionPattern, $inputExpression);
11837748cd8SNickeau
11937748cd8SNickeau    }
12037748cd8SNickeau
12137748cd8SNickeau    /**
12237748cd8SNickeau     * Return a mode from a tag (ie from a {@link Plugin::getPluginComponent()}
12337748cd8SNickeau     * @param $tag
12437748cd8SNickeau     * @return string
12537748cd8SNickeau     *
12637748cd8SNickeau     * A mode is just a name for a class
12737748cd8SNickeau     * Example: $Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock());
12837748cd8SNickeau     */
12937748cd8SNickeau    public static function getModeFromTag($tag)
13037748cd8SNickeau    {
13137748cd8SNickeau        return "plugin_" . self::getComponentName($tag);
13237748cd8SNickeau    }
13337748cd8SNickeau
13437748cd8SNickeau
13537748cd8SNickeau    /**
13637748cd8SNickeau     * This pattern allows space after the tag name
13737748cd8SNickeau     * for an end tag
13837748cd8SNickeau     * As XHTML (https://www.w3.org/TR/REC-xml/#dt-etag)
13937748cd8SNickeau     * @param $tag
14037748cd8SNickeau     * @return string
14137748cd8SNickeau     */
14237748cd8SNickeau    public static function getEndTagPattern($tag)
14337748cd8SNickeau    {
14437748cd8SNickeau        return "</$tag\s*>";
14537748cd8SNickeau    }
14637748cd8SNickeau
14737748cd8SNickeau    /**
14837748cd8SNickeau     * @param $tag
14937748cd8SNickeau     * @return string
15037748cd8SNickeau     *
15137748cd8SNickeau     * Create a open tag pattern without lookahead.
152c3437056SNickeau     * Used for
153c3437056SNickeau     * @link https://dev.w3.org/html5/html-author/#void-elements-0
15437748cd8SNickeau     */
15537748cd8SNickeau    public static function getVoidElementTagPattern($tag)
15637748cd8SNickeau    {
15737748cd8SNickeau        return ' < ' . $tag . ' .*?>';
15837748cd8SNickeau    }
15937748cd8SNickeau
16037748cd8SNickeau
16137748cd8SNickeau    /**
16237748cd8SNickeau     * Take an array  where the key is the attribute name
16337748cd8SNickeau     * and return a HTML tag string
16437748cd8SNickeau     *
16537748cd8SNickeau     * The attribute name and value are escaped
16637748cd8SNickeau     *
16737748cd8SNickeau     * @param $attributes - combo attributes
16837748cd8SNickeau     * @return string
16937748cd8SNickeau     * @deprecated to allowed background and other metadata, use {@link TagAttributes::toHtmlEnterTag()}
17037748cd8SNickeau     */
17137748cd8SNickeau    public static function array2HTMLAttributesAsString($attributes)
17237748cd8SNickeau    {
17337748cd8SNickeau
17437748cd8SNickeau        $tagAttributes = TagAttributes::createFromCallStackArray($attributes);
17537748cd8SNickeau        return $tagAttributes->toHTMLAttributeString();
17637748cd8SNickeau
17737748cd8SNickeau    }
17837748cd8SNickeau
17937748cd8SNickeau    /**
18037748cd8SNickeau     *
18137748cd8SNickeau     * Parse the attributes part of a match
18237748cd8SNickeau     *
18337748cd8SNickeau     * Example:
18437748cd8SNickeau     *   line-numbers="value"
18537748cd8SNickeau     *   line-numbers='value'
18637748cd8SNickeau     *
18737748cd8SNickeau     * This value may be in:
18837748cd8SNickeau     *   * configuration value
18937748cd8SNickeau     *   * as well as in the match of a {@link SyntaxPlugin}
19037748cd8SNickeau     *
19137748cd8SNickeau     * @param $string
19237748cd8SNickeau     * @return array
19337748cd8SNickeau     *
19437748cd8SNickeau     * To parse a match, use {@link PluginUtility::getTagAttributes()}
19537748cd8SNickeau     *
19637748cd8SNickeau     *
19737748cd8SNickeau     */
19837748cd8SNickeau    public static function parseAttributes($string)
19937748cd8SNickeau    {
20037748cd8SNickeau
20137748cd8SNickeau        $parameters = array();
20237748cd8SNickeau
20337748cd8SNickeau// Rules
20437748cd8SNickeau//  * name may be alone (ie true boolean attribute)
20537748cd8SNickeau//  * a name may get a `-`
20637748cd8SNickeau//  * there may be space every everywhere when the value is enclosed with a quote
20737748cd8SNickeau//  * there may be no space in the value and between the equal sign when the value is not enclosed
20837748cd8SNickeau//
20937748cd8SNickeau// /i not case sensitive
21037748cd8SNickeau        $attributePattern = '\s*([-\w]+)\s*(?:=(\s*[\'"]([^`"]*)[\'"]\s*|[^\s]*))?';
21137748cd8SNickeau        $result = preg_match_all('/' . $attributePattern . '/i', $string, $matches);
21237748cd8SNickeau        if ($result != 0) {
21337748cd8SNickeau            foreach ($matches[1] as $key => $parameterKey) {
21437748cd8SNickeau
21537748cd8SNickeau// group 3 (ie the value between quotes)
21637748cd8SNickeau                $value = $matches[3][$key];
21737748cd8SNickeau                if ($value == "") {
21837748cd8SNickeau// check the value without quotes
21937748cd8SNickeau                    $value = $matches[2][$key];
22037748cd8SNickeau                }
22137748cd8SNickeau// if there is no value, this is a boolean
22237748cd8SNickeau                if ($value == "") {
22337748cd8SNickeau                    $value = true;
22437748cd8SNickeau                } else {
22537748cd8SNickeau                    $value = hsc($value);
22637748cd8SNickeau                }
22737748cd8SNickeau                $parameters[hsc(strtolower($parameterKey))] = $value;
22837748cd8SNickeau            }
22937748cd8SNickeau        }
23037748cd8SNickeau        return $parameters;
23137748cd8SNickeau
23237748cd8SNickeau    }
23337748cd8SNickeau
23404fd306cSNickeau    public static function getTagAttributes(string $match, array $knownTypes = [], bool $allowFirstBooleanAttributesAsType = false): array
23537748cd8SNickeau    {
23604fd306cSNickeau        return self::getQualifiedTagAttributes($match, false, "", $knownTypes, $allowFirstBooleanAttributesAsType);
23737748cd8SNickeau    }
23837748cd8SNickeau
23937748cd8SNickeau    /**
24037748cd8SNickeau     * Return the attribute of a tag
24137748cd8SNickeau     * Because they are users input, they are all escaped
24237748cd8SNickeau     * @param $match
24337748cd8SNickeau     * @param $hasThirdValue - if true, the third parameter is treated as value, not a property and returned in the `third` key
24437748cd8SNickeau     * use for the code/file/console where they accept a name as third value
24537748cd8SNickeau     * @param $keyThirdArgument - if a third argument is found, return it with this key
2464cadd4f8SNickeau     * @param array|null $knownTypes
24704fd306cSNickeau     * @param bool $allowFirstBooleanAttributesAsType
24837748cd8SNickeau     * @return array
24937748cd8SNickeau     */
25004fd306cSNickeau    public static function getQualifiedTagAttributes($match, $hasThirdValue, $keyThirdArgument, array $knownTypes = [], bool $allowFirstBooleanAttributesAsType = false): array
25137748cd8SNickeau    {
25237748cd8SNickeau
25337748cd8SNickeau        $match = PluginUtility::getPreprocessEnterTag($match);
25437748cd8SNickeau
25537748cd8SNickeau        // Suppress the tag name (ie until the first blank)
25637748cd8SNickeau        $spacePosition = strpos($match, " ");
25737748cd8SNickeau        if (!$spacePosition) {
25837748cd8SNickeau            // No space, meaning this is only the tag name
25937748cd8SNickeau            return array();
26037748cd8SNickeau        }
26137748cd8SNickeau        $match = trim(substr($match, $spacePosition));
26237748cd8SNickeau        if ($match == "") {
26337748cd8SNickeau            return array();
26437748cd8SNickeau        }
26537748cd8SNickeau
26604fd306cSNickeau        /**
26704fd306cSNickeau         * Do we have a type as first argument ?
26804fd306cSNickeau         */
26937748cd8SNickeau        $attributes = array();
27037748cd8SNickeau        $spacePosition = strpos($match, " ");
27137748cd8SNickeau        if ($spacePosition) {
27237748cd8SNickeau            $nextArgument = substr($match, 0, $spacePosition);
27337748cd8SNickeau        } else {
27437748cd8SNickeau            $nextArgument = $match;
27537748cd8SNickeau        }
2764cadd4f8SNickeau
27704fd306cSNickeau        $isBooleanAttribute = !strpos($nextArgument, "=");
2784cadd4f8SNickeau        $isType = false;
27904fd306cSNickeau        if ($isBooleanAttribute) {
28004fd306cSNickeau            $possibleTypeLowercase = strtolower($nextArgument);
28104fd306cSNickeau            if ($allowFirstBooleanAttributesAsType) {
28204fd306cSNickeau                $isType = true;
28304fd306cSNickeau                $nextArgument = $possibleTypeLowercase;
28404fd306cSNickeau            } else {
28504fd306cSNickeau                if (!empty($knownTypes) && in_array($possibleTypeLowercase, $knownTypes)) {
28604fd306cSNickeau                    $isType = true;
28704fd306cSNickeau                    $nextArgument = $possibleTypeLowercase;
28804fd306cSNickeau                }
2894cadd4f8SNickeau            }
2904cadd4f8SNickeau        }
2914cadd4f8SNickeau        if ($isType) {
2924cadd4f8SNickeau
29304fd306cSNickeau            $attributes[TagAttributes::TYPE_KEY] = $nextArgument;
29404fd306cSNickeau            /**
29504fd306cSNickeau             * Suppress the type
29604fd306cSNickeau             */
29737748cd8SNickeau            $match = substr($match, strlen($nextArgument));
29837748cd8SNickeau            $match = trim($match);
29937748cd8SNickeau
30004fd306cSNickeau            /**
30104fd306cSNickeau             * Do we have a value as first argument ?
30204fd306cSNickeau             */
30337748cd8SNickeau            if (!empty($hasThirdValue)) {
30437748cd8SNickeau                $spacePosition = strpos($match, " ");
30537748cd8SNickeau                if ($spacePosition) {
30637748cd8SNickeau                    $nextArgument = substr($match, 0, $spacePosition);
30737748cd8SNickeau                } else {
30837748cd8SNickeau                    $nextArgument = $match;
30937748cd8SNickeau                }
31037748cd8SNickeau                if (!strpos($nextArgument, "=") && !empty($nextArgument)) {
31137748cd8SNickeau                    $attributes[$keyThirdArgument] = $nextArgument;
31204fd306cSNickeau                    /**
31304fd306cSNickeau                     * Suppress the third argument
31404fd306cSNickeau                     */
31537748cd8SNickeau                    $match = substr($match, strlen($nextArgument));
31637748cd8SNickeau                    $match = trim($match);
31737748cd8SNickeau                }
31837748cd8SNickeau            }
31937748cd8SNickeau        }
32037748cd8SNickeau
32104fd306cSNickeau        /**
32204fd306cSNickeau         * Parse the remaining attributes
32304fd306cSNickeau         */
32437748cd8SNickeau        $parsedAttributes = self::parseAttributes($match);
32537748cd8SNickeau
32604fd306cSNickeau        /**
32704fd306cSNickeau         * Merge
32804fd306cSNickeau         */
32937748cd8SNickeau        $attributes = array_merge($attributes, $parsedAttributes);;
33037748cd8SNickeau
33137748cd8SNickeau        return $attributes;
33237748cd8SNickeau
33337748cd8SNickeau    }
33437748cd8SNickeau
33537748cd8SNickeau    /**
33637748cd8SNickeau     * @param $tag
33737748cd8SNickeau     * @return string
33837748cd8SNickeau     * Create a pattern used where the tag is not a container.
33937748cd8SNickeau     * ie
34037748cd8SNickeau     * <br/>
34104fd306cSNickeau     *
34237748cd8SNickeau     * <icon/>
34337748cd8SNickeau     * This is generally used with a subtition plugin
34437748cd8SNickeau     * and a {@link Lexer::addSpecialPattern} state
34537748cd8SNickeau     * where the tag is just replaced
34637748cd8SNickeau     */
34719494974Sgerardnico    public static function getEmptyTagPattern($tag): string
34837748cd8SNickeau    {
34919494974Sgerardnico
3504cadd4f8SNickeau        /**
3514cadd4f8SNickeau         * A tag should start with the tag
3524cadd4f8SNickeau         * `(?=[/ ]{1})` - a space or the / (lookahead) => to allow allow tag name with minus character
3534cadd4f8SNickeau         * `(?![^/]>)` - it's not a normal tag (ie a > with the previous character that is not /)
3544cadd4f8SNickeau         * `[^>]*` then until the > is found (dokuwiki capture greedy, don't use the point character)
3554cadd4f8SNickeau         * then until the close `/>` character
3564cadd4f8SNickeau         */
3574cadd4f8SNickeau        return '<' . $tag . '(?=[/ ]{1})(?![^/]>)[^>]*\/>';
35837748cd8SNickeau    }
35937748cd8SNickeau
36004fd306cSNickeau    public static function getEmptyTagPatternGeneral(): string
36104fd306cSNickeau    {
36204fd306cSNickeau
36304fd306cSNickeau        return self::getEmptyTagPattern("[\w-]+");
36404fd306cSNickeau    }
36504fd306cSNickeau
36637748cd8SNickeau    /**
36737748cd8SNickeau     * Just call this function from a class like that
36837748cd8SNickeau     *     getTageName(get_called_class())
36937748cd8SNickeau     * to get the tag name (ie the component plugin)
37037748cd8SNickeau     * of a syntax plugin
37137748cd8SNickeau     *
37237748cd8SNickeau     * @param $get_called_class
37337748cd8SNickeau     * @return string
37437748cd8SNickeau     */
37537748cd8SNickeau    public static function getTagName($get_called_class)
37637748cd8SNickeau    {
37737748cd8SNickeau        list(/* $t */, /* $p */, /* $n */, $c) = explode('_', $get_called_class, 4);
37837748cd8SNickeau        return (isset($c) ? $c : '');
37937748cd8SNickeau    }
38037748cd8SNickeau
38137748cd8SNickeau    /**
38237748cd8SNickeau     * Just call this function from a class like that
38337748cd8SNickeau     *     getAdminPageName(get_called_class())
38437748cd8SNickeau     * to get the page name of a admin plugin
38537748cd8SNickeau     *
38637748cd8SNickeau     * @param $get_called_class
38737748cd8SNickeau     * @return string - the admin page name
38837748cd8SNickeau     */
38937748cd8SNickeau    public static function getAdminPageName($get_called_class)
39037748cd8SNickeau    {
39137748cd8SNickeau        $names = explode('_', $get_called_class);
39237748cd8SNickeau        $names = array_slice($names, -2);
39337748cd8SNickeau        return implode('_', $names);
39437748cd8SNickeau    }
39537748cd8SNickeau
39637748cd8SNickeau    public static function getNameSpace()
39737748cd8SNickeau    {
39837748cd8SNickeau// No : at the begin of the namespace please
39937748cd8SNickeau        return self::PLUGIN_BASE_NAME . ':';
40037748cd8SNickeau    }
40137748cd8SNickeau
40237748cd8SNickeau    /**
40337748cd8SNickeau     * @param $get_called_class - the plugin class
40437748cd8SNickeau     * @return array
40537748cd8SNickeau     */
40637748cd8SNickeau    public static function getTags($get_called_class)
40737748cd8SNickeau    {
40837748cd8SNickeau        $elements = array();
40937748cd8SNickeau        $elementName = PluginUtility::getTagName($get_called_class);
41037748cd8SNickeau        $elements[] = $elementName;
41137748cd8SNickeau        $elements[] = strtoupper($elementName);
41237748cd8SNickeau        return $elements;
41337748cd8SNickeau    }
41437748cd8SNickeau
41537748cd8SNickeau    /**
41637748cd8SNickeau     * Render a text
41737748cd8SNickeau     * @param $pageContent
41837748cd8SNickeau     * @return string|null
41937748cd8SNickeau     */
42004fd306cSNickeau    public static function render($pageContent): ?string
42137748cd8SNickeau    {
42204fd306cSNickeau        return MarkupRenderUtility::renderText2XhtmlAndStripPEventually($pageContent, false);
42337748cd8SNickeau    }
42437748cd8SNickeau
42537748cd8SNickeau
42637748cd8SNickeau    /**
42737748cd8SNickeau     * This method will takes attributes
42837748cd8SNickeau     * and process the plugin styling attribute such as width and height
42937748cd8SNickeau     * to put them in a style HTML attribute
43037748cd8SNickeau     * @param TagAttributes $attributes
43137748cd8SNickeau     */
43237748cd8SNickeau    public static function processStyle(&$attributes)
43337748cd8SNickeau    {
43437748cd8SNickeau        // Style
43537748cd8SNickeau        $styleAttributeName = "style";
43637748cd8SNickeau        if ($attributes->hasComponentAttribute($styleAttributeName)) {
43737748cd8SNickeau            $properties = explode(";", $attributes->getValueAndRemove($styleAttributeName));
43837748cd8SNickeau            foreach ($properties as $property) {
43937748cd8SNickeau                list($key, $value) = explode(":", $property);
44037748cd8SNickeau                if ($key != "") {
44182a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet($key, $value);
44237748cd8SNickeau                }
44337748cd8SNickeau            }
44437748cd8SNickeau        }
44537748cd8SNickeau
44637748cd8SNickeau
44737748cd8SNickeau        /**
44837748cd8SNickeau         * Border Color
44937748cd8SNickeau         * For background color, see {@link TagAttributes::processBackground()}
45037748cd8SNickeau         * For text color, see {@link TextColor}
45137748cd8SNickeau         */
45237748cd8SNickeau
4534cadd4f8SNickeau        if ($attributes->hasComponentAttribute(ColorRgb::BORDER_COLOR)) {
4544cadd4f8SNickeau            $colorValue = $attributes->getValueAndRemove(ColorRgb::BORDER_COLOR);
4554cadd4f8SNickeau            $attributes->addStyleDeclarationIfNotSet(ColorRgb::BORDER_COLOR, ColorRgb::createFromString($colorValue)->toCssValue());
45637748cd8SNickeau            self::checkDefaultBorderColorAttributes($attributes);
45737748cd8SNickeau        }
45837748cd8SNickeau
45937748cd8SNickeau
46037748cd8SNickeau    }
46137748cd8SNickeau
46237748cd8SNickeau    /**
46337748cd8SNickeau     * Return the name of the requested script
46437748cd8SNickeau     */
46537748cd8SNickeau    public
46637748cd8SNickeau    static function getRequestScript()
46737748cd8SNickeau    {
46837748cd8SNickeau        $scriptPath = null;
46937748cd8SNickeau        $testPropertyValue = self::getPropertyValue("SCRIPT_NAME");
47037748cd8SNickeau        if (defined('DOKU_UNITTEST') && $testPropertyValue != null) {
47137748cd8SNickeau            return $testPropertyValue;
47237748cd8SNickeau        }
47337748cd8SNickeau        if (array_key_exists("DOCUMENT_URI", $_SERVER)) {
47437748cd8SNickeau            $scriptPath = $_SERVER["DOCUMENT_URI"];
47537748cd8SNickeau        }
47637748cd8SNickeau        if ($scriptPath == null && array_key_exists("SCRIPT_NAME", $_SERVER)) {
47737748cd8SNickeau            $scriptPath = $_SERVER["SCRIPT_NAME"];
47837748cd8SNickeau        }
47937748cd8SNickeau        if ($scriptPath == null) {
48037748cd8SNickeau            msg("Unable to find the main script", LogUtility::LVL_MSG_ERROR);
48137748cd8SNickeau        }
48237748cd8SNickeau        $path_parts = pathinfo($scriptPath);
48337748cd8SNickeau        return $path_parts['basename'];
48437748cd8SNickeau    }
48537748cd8SNickeau
48637748cd8SNickeau    /**
48737748cd8SNickeau     *
48837748cd8SNickeau     * @param $name
48937748cd8SNickeau     * @param $default
49037748cd8SNickeau     * @return string - the value of a query string property or if in test mode, the value of a test variable
49137748cd8SNickeau     * set with {@link self::setTestProperty}
49237748cd8SNickeau     * This is used to test script that are not supported by the dokuwiki test framework
49337748cd8SNickeau     * such as css.php
49404fd306cSNickeau     * @deprecated use {@link ApiRouter::getRequestParameter()}
49537748cd8SNickeau     */
49637748cd8SNickeau    public
49737748cd8SNickeau    static function getPropertyValue($name, $default = null)
49837748cd8SNickeau    {
49937748cd8SNickeau        global $INPUT;
50037748cd8SNickeau        $value = $INPUT->str($name);
50137748cd8SNickeau        if ($value == null && defined('DOKU_UNITTEST')) {
50237748cd8SNickeau            global $COMBO;
503*d9ddda89Sgerardnico            if ($COMBO !== null) {
50437748cd8SNickeau                $value = $COMBO[$name];
50537748cd8SNickeau            }
506*d9ddda89Sgerardnico        }
50737748cd8SNickeau        if ($value == null) {
50837748cd8SNickeau            return $default;
50937748cd8SNickeau        } else {
51037748cd8SNickeau            return $value;
51137748cd8SNickeau        }
51237748cd8SNickeau
51337748cd8SNickeau    }
51437748cd8SNickeau
51537748cd8SNickeau    /**
51637748cd8SNickeau     * Create an URL to the documentation website
51737748cd8SNickeau     * @param $canonical - canonical id or slug
518c3437056SNickeau     * @param $label -  the text of the link
51904fd306cSNickeau     * @param bool $withIcon - used to break the recursion with the message in the {@link IconDownloader}
52037748cd8SNickeau     * @return string - an url
52137748cd8SNickeau     */
52237748cd8SNickeau    public
52304fd306cSNickeau    static function getDocumentationHyperLink($canonical, $label, bool $withIcon = true, $tooltip = ""): string
52437748cd8SNickeau    {
52537748cd8SNickeau
52637748cd8SNickeau        $xhtmlIcon = "";
52737748cd8SNickeau        if ($withIcon) {
52837748cd8SNickeau
52904fd306cSNickeau            $logoPath = WikiPath::createComboResource("images:logo.svg");
5304cadd4f8SNickeau            try {
53104fd306cSNickeau                $fetchImage = FetcherSvg::createSvgFromPath($logoPath);
53204fd306cSNickeau                $fetchImage->setRequestedType(FetcherSvg::ICON_TYPE)
53304fd306cSNickeau                    ->setRequestedWidth(20);
53404fd306cSNickeau                $xhtmlIcon = SvgImageLink::createFromFetcher($fetchImage)
53504fd306cSNickeau                    ->renderMediaTag();
53604fd306cSNickeau            } catch (ExceptionCompile $e) {
53704fd306cSNickeau                /**
53804fd306cSNickeau                 * We don't throw because this function
53904fd306cSNickeau                 * is also used by:
54004fd306cSNickeau                 *   * the log functionality to show link to the documentation creating a loop
54104fd306cSNickeau                 *   * inside the configuration description crashing the page
54204fd306cSNickeau                 */
54304fd306cSNickeau                if (PluginUtility::isDevOrTest()) {
54404fd306cSNickeau// shows errors in the html only on dev/test
54504fd306cSNickeau                    $xhtmlIcon = "Error: {$e->getMessage()}";
54637748cd8SNickeau                }
5474cadd4f8SNickeau            }
54837748cd8SNickeau
54937748cd8SNickeau        }
550c3437056SNickeau        $urlApex = self::$URL_APEX;
551c3437056SNickeau        $path = str_replace(":", "/", $canonical);
552c3437056SNickeau        if (empty($tooltip)) {
553c3437056SNickeau            $title = $label;
554c3437056SNickeau        } else {
555c3437056SNickeau            $title = $tooltip;
556c3437056SNickeau        }
557c3437056SNickeau        $htmlToolTip = "";
558c3437056SNickeau        if (!empty($tooltip)) {
559c3437056SNickeau            $dataAttributeNamespace = Bootstrap::getDataNamespace();
560c3437056SNickeau            $htmlToolTip = "data{$dataAttributeNamespace}-toggle=\"tooltip\"";
561c3437056SNickeau        }
562c3437056SNickeau        return "$xhtmlIcon<a href=\"$urlApex/$path\" title=\"$title\" $htmlToolTip style=\"text-decoration:none;\">$label</a>";
56337748cd8SNickeau    }
56437748cd8SNickeau
56537748cd8SNickeau    /**
56637748cd8SNickeau     * An utility function to not search every time which array should be first
56737748cd8SNickeau     * @param array $inlineAttributes - the component inline attributes
56837748cd8SNickeau     * @param array $defaultAttributes - the default configuration attributes
56937748cd8SNickeau     * @return array - a merged array
57037748cd8SNickeau     */
57137748cd8SNickeau    public
57237748cd8SNickeau    static function mergeAttributes(array $inlineAttributes, array $defaultAttributes = array())
57337748cd8SNickeau    {
57437748cd8SNickeau        return array_merge($defaultAttributes, $inlineAttributes);
57537748cd8SNickeau    }
57637748cd8SNickeau
57737748cd8SNickeau    /**
57837748cd8SNickeau     * A pattern for a container tag
57937748cd8SNickeau     * that needs to catch the content
58037748cd8SNickeau     *
58137748cd8SNickeau     * Use as a special pattern (substition)
58237748cd8SNickeau     *
58337748cd8SNickeau     * The {@link \syntax_plugin_combo_math} use it
58437748cd8SNickeau     * @param $tag
58537748cd8SNickeau     * @return string - a pattern
58637748cd8SNickeau     */
58737748cd8SNickeau    public
58837748cd8SNickeau    static function getLeafContainerTagPattern($tag)
58937748cd8SNickeau    {
59037748cd8SNickeau        return '<' . $tag . '.*?>.*?<\/' . $tag . '>';
59137748cd8SNickeau    }
59237748cd8SNickeau
59337748cd8SNickeau    /**
59437748cd8SNickeau     * Return the content of a tag
59504fd306cSNickeau     *
59637748cd8SNickeau     * <math>Content</math>
59737748cd8SNickeau     * @param $match
59837748cd8SNickeau     * @return string the content
59937748cd8SNickeau     */
60037748cd8SNickeau    public
60137748cd8SNickeau    static function getTagContent($match)
60237748cd8SNickeau    {
60337748cd8SNickeau// From the first >
60437748cd8SNickeau        $start = strpos($match, ">");
60537748cd8SNickeau        if ($start == false) {
60637748cd8SNickeau            LogUtility::msg("The match does not contain any opening tag. Match: {$match}", LogUtility::LVL_MSG_ERROR);
60737748cd8SNickeau            return "";
60837748cd8SNickeau        }
60937748cd8SNickeau        $match = substr($match, $start + 1);
61037748cd8SNickeau// If this is the last character, we get a false
61137748cd8SNickeau        if ($match == false) {
61237748cd8SNickeau            LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR);
61337748cd8SNickeau            return "";
61437748cd8SNickeau        }
61537748cd8SNickeau
61637748cd8SNickeau        $end = strrpos($match, "</");
61737748cd8SNickeau        if ($end == false) {
61837748cd8SNickeau            LogUtility::msg("The match does not contain any closing tag. Match: {$match}", LogUtility::LVL_MSG_ERROR);
61937748cd8SNickeau            return "";
62037748cd8SNickeau        }
62137748cd8SNickeau
62237748cd8SNickeau        return substr($match, 0, $end);
62337748cd8SNickeau    }
62437748cd8SNickeau
62537748cd8SNickeau    /**
62637748cd8SNickeau     *
62737748cd8SNickeau     * Check if a HTML tag was already added for a request
62837748cd8SNickeau     * The request id is just the timestamp
62937748cd8SNickeau     * An indicator array should be provided
63037748cd8SNickeau     * @return string
63137748cd8SNickeau     */
63237748cd8SNickeau    public
63337748cd8SNickeau    static function getRequestId()
63437748cd8SNickeau    {
63537748cd8SNickeau
63637748cd8SNickeau        if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
63737748cd8SNickeau// since php 5.4
63837748cd8SNickeau            $requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
63937748cd8SNickeau        } else {
64037748cd8SNickeau// DokuWiki test framework use this
64137748cd8SNickeau            $requestTime = $_SERVER['REQUEST_TIME'];
64237748cd8SNickeau        }
64337748cd8SNickeau        $keyPrefix = 'combo_';
64437748cd8SNickeau
64537748cd8SNickeau        global $ID;
64637748cd8SNickeau        return $keyPrefix . hash('crc32b', $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT'] . $requestTime . $ID);
64737748cd8SNickeau
64837748cd8SNickeau    }
64937748cd8SNickeau
65037748cd8SNickeau    /**
6514cadd4f8SNickeau     *
65204fd306cSNickeau     * Return the requested wiki id (known also as page id)
6534cadd4f8SNickeau     *
65404fd306cSNickeau     * If the code is rendering a sidebar, it will not return the id of the sidebar
65504fd306cSNickeau     * but the requested wiki id
6564cadd4f8SNickeau     *
65737748cd8SNickeau     * @return string
65804fd306cSNickeau     * @throws ExceptionNotFound
65904fd306cSNickeau     * @deprecated use {@link ExecutionContext::getRequestedPath()}
66037748cd8SNickeau     */
66104fd306cSNickeau    public static function getRequestedWikiId(): string
66237748cd8SNickeau    {
66304fd306cSNickeau
66404fd306cSNickeau        return ExecutionContext::getActualOrCreateFromEnv()->getRequestedPath()->getWikiId();
6654cadd4f8SNickeau
66637748cd8SNickeau    }
66737748cd8SNickeau
66804fd306cSNickeau    public static function xmlEncode($text)
669c3437056SNickeau    {
670c3437056SNickeau        /**
671c3437056SNickeau         * {@link htmlentities }
672c3437056SNickeau         */
673c3437056SNickeau        return htmlentities($text, ENT_XML1);
674c3437056SNickeau    }
675c3437056SNickeau
67637748cd8SNickeau
67737748cd8SNickeau    /**
67837748cd8SNickeau     * Add a class
67937748cd8SNickeau     * @param $classValue
68037748cd8SNickeau     * @param array $attributes
68137748cd8SNickeau     */
68237748cd8SNickeau    public
68337748cd8SNickeau    static function addClass2Attributes($classValue, array &$attributes)
68437748cd8SNickeau    {
68537748cd8SNickeau        self::addAttributeValue("class", $classValue, $attributes);
68637748cd8SNickeau    }
68737748cd8SNickeau
68837748cd8SNickeau    /**
68937748cd8SNickeau     * Add a style property to the attributes
69037748cd8SNickeau     * @param $property
69137748cd8SNickeau     * @param $value
69237748cd8SNickeau     * @param array $attributes
69382a60d03SNickeau     * @deprecated use {@link TagAttributes::addStyleDeclarationIfNotSet()} instead
69437748cd8SNickeau     */
69537748cd8SNickeau    public
69637748cd8SNickeau    static function addStyleProperty($property, $value, array &$attributes)
69737748cd8SNickeau    {
69837748cd8SNickeau        if (isset($attributes["style"])) {
69937748cd8SNickeau            $attributes["style"] .= ";$property:$value";
70037748cd8SNickeau        } else {
70137748cd8SNickeau            $attributes["style"] = "$property:$value";
70237748cd8SNickeau        }
70337748cd8SNickeau
70437748cd8SNickeau    }
70537748cd8SNickeau
70637748cd8SNickeau    /**
70737748cd8SNickeau     * Add default border attributes
70837748cd8SNickeau     * to see a border
70937748cd8SNickeau     * Doc
71037748cd8SNickeau     * https://combostrap.com/styling/color#border_color
71137748cd8SNickeau     * @param TagAttributes $tagAttributes
71237748cd8SNickeau     */
71337748cd8SNickeau    private
71437748cd8SNickeau    static function checkDefaultBorderColorAttributes(&$tagAttributes)
71537748cd8SNickeau    {
71637748cd8SNickeau        /**
71737748cd8SNickeau         * border color was set without the width
71837748cd8SNickeau         * setting the width
71937748cd8SNickeau         */
72037748cd8SNickeau        if (!(
72137748cd8SNickeau            $tagAttributes->hasStyleDeclaration("border")
72237748cd8SNickeau            ||
72337748cd8SNickeau            $tagAttributes->hasStyleDeclaration("border-width")
72437748cd8SNickeau        )
72537748cd8SNickeau        ) {
72682a60d03SNickeau            $tagAttributes->addStyleDeclarationIfNotSet("border-width", "1px");
72737748cd8SNickeau        }
72837748cd8SNickeau        /**
72937748cd8SNickeau         * border color was set without the style
73037748cd8SNickeau         * setting the style
73137748cd8SNickeau         */
73237748cd8SNickeau        if (!
73337748cd8SNickeau        (
73437748cd8SNickeau            $tagAttributes->hasStyleDeclaration("border")
73537748cd8SNickeau            ||
73637748cd8SNickeau            $tagAttributes->hasStyleDeclaration("border-style")
73737748cd8SNickeau        )
73837748cd8SNickeau        ) {
73982a60d03SNickeau            $tagAttributes->addStyleDeclarationIfNotSet("border-style", "solid");
74037748cd8SNickeau
74137748cd8SNickeau        }
74237748cd8SNickeau        if (!$tagAttributes->hasStyleDeclaration("border-radius")) {
74382a60d03SNickeau            $tagAttributes->addStyleDeclarationIfNotSet("border-radius", ".25rem");
74437748cd8SNickeau        }
74537748cd8SNickeau
74637748cd8SNickeau    }
74737748cd8SNickeau
74837748cd8SNickeau    /**
74937748cd8SNickeau     * @param $match
75037748cd8SNickeau     * @return null|string - return the tag name or null if not found
75137748cd8SNickeau     */
75237748cd8SNickeau    public
75304fd306cSNickeau    static function getMarkupTag($match): ?string
75437748cd8SNickeau    {
75537748cd8SNickeau
75637748cd8SNickeau        // Until the first >
75737748cd8SNickeau        $pos = strpos($match, ">");
75804fd306cSNickeau        if (!$pos) {
75937748cd8SNickeau            LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_ERROR);
76037748cd8SNickeau            return null;
76137748cd8SNickeau        }
76237748cd8SNickeau        $match = substr($match, 0, $pos);
76337748cd8SNickeau
76404fd306cSNickeau        // if this is a empty tag with / at the end we delete it
76504fd306cSNickeau        if ($match[strlen($match) - 1] == "/") {
76604fd306cSNickeau            $match = substr($match, 0, -1);
76704fd306cSNickeau        }
76804fd306cSNickeau
76937748cd8SNickeau        // Suppress the <
77037748cd8SNickeau        if ($match[0] == "<") {
77137748cd8SNickeau            $match = substr($match, 1);
77204fd306cSNickeau            // closing tag
77304fd306cSNickeau            if ($match[0] == "/") {
77404fd306cSNickeau                $match = substr($match, 1);
77504fd306cSNickeau            }
77637748cd8SNickeau        } else {
77737748cd8SNickeau            LogUtility::msg("This is not a text tag because it does not start with the character `>`");
77837748cd8SNickeau        }
77937748cd8SNickeau
78037748cd8SNickeau        // Suppress the tag name (ie until the first blank)
78137748cd8SNickeau        $spacePosition = strpos($match, " ");
78237748cd8SNickeau        if (!$spacePosition) {
78337748cd8SNickeau            // No space, meaning this is only the tag name
78437748cd8SNickeau            return $match;
78537748cd8SNickeau        } else {
78637748cd8SNickeau            return substr($match, 0, $spacePosition);
78737748cd8SNickeau        }
78837748cd8SNickeau
78937748cd8SNickeau    }
79037748cd8SNickeau
79137748cd8SNickeau
79237748cd8SNickeau    public
79304fd306cSNickeau    static function getComponentName($tag): string
79437748cd8SNickeau    {
79537748cd8SNickeau        return strtolower(PluginUtility::PLUGIN_BASE_NAME) . "_" . $tag;
79637748cd8SNickeau    }
79737748cd8SNickeau
79837748cd8SNickeau    public
79937748cd8SNickeau    static function addAttributeValue($attribute, $value, array &$attributes)
80037748cd8SNickeau    {
80137748cd8SNickeau        if (array_key_exists($attribute, $attributes) && $attributes[$attribute] !== "") {
80237748cd8SNickeau            $attributes[$attribute] .= " {$value}";
80337748cd8SNickeau        } else {
80437748cd8SNickeau            $attributes[$attribute] = "{$value}";
80537748cd8SNickeau        }
80637748cd8SNickeau    }
80737748cd8SNickeau
80837748cd8SNickeau    /**
80937748cd8SNickeau     * Plugin Utility is available to all plugin,
81037748cd8SNickeau     * this is a convenient way to the the snippet manager
81104fd306cSNickeau     * @return SnippetSystem
81237748cd8SNickeau     */
81337748cd8SNickeau    public
81404fd306cSNickeau    static function getSnippetManager(): SnippetSystem
81537748cd8SNickeau    {
81604fd306cSNickeau        return SnippetSystem::getFromContext();
81737748cd8SNickeau    }
81837748cd8SNickeau
819c3437056SNickeau
82037748cd8SNickeau    /**
82137748cd8SNickeau     * Function used in a render
82237748cd8SNickeau     * @param $data - the data from {@link PluginUtility::handleAndReturnUnmatchedData()}
82337748cd8SNickeau     * @return string
82404fd306cSNickeau     *
82504fd306cSNickeau     *
82637748cd8SNickeau     */
82737748cd8SNickeau    public
8284cadd4f8SNickeau    static function renderUnmatched($data): string
82937748cd8SNickeau    {
83037748cd8SNickeau        /**
83137748cd8SNickeau         * Attributes
83237748cd8SNickeau         */
83304fd306cSNickeau        $attributes = $data[PluginUtility::ATTRIBUTES] ?? [];
83437748cd8SNickeau        $tagAttributes = TagAttributes::createFromCallStackArray($attributes);
83504fd306cSNickeau
83604fd306cSNickeau        /**
83704fd306cSNickeau         * Display
83804fd306cSNickeau         */
83904fd306cSNickeau        $display = $tagAttributes->getValueAndRemoveIfPresent(Display::DISPLAY);
84004fd306cSNickeau        if ($display === "none") {
84104fd306cSNickeau            return "";
84204fd306cSNickeau        }
84304fd306cSNickeau
84437748cd8SNickeau        $payload = $data[self::PAYLOAD];
8451fa8c418SNickeau        $previousTagDisplayType = $data[self::CONTEXT];
8461fa8c418SNickeau        if ($previousTagDisplayType !== Call::INLINE_DISPLAY) {
84704fd306cSNickeau            // Delete the eol at the beginning and end
84804fd306cSNickeau            // otherwise we get a big block
84937748cd8SNickeau            $payload = ltrim($payload);
85037748cd8SNickeau        }
85104fd306cSNickeau        return Html::encode($payload);
85204fd306cSNickeau
85337748cd8SNickeau    }
85437748cd8SNickeau
855c3437056SNickeau    public
856c3437056SNickeau    static function renderUnmatchedXml($data)
857c3437056SNickeau    {
858c3437056SNickeau        $payload = $data[self::PAYLOAD];
859c3437056SNickeau        $previousTagDisplayType = $data[self::CONTEXT];
860c3437056SNickeau        if ($previousTagDisplayType !== Call::INLINE_DISPLAY) {
861c3437056SNickeau            $payload = ltrim($payload);
862c3437056SNickeau        }
863c3437056SNickeau        return PluginUtility::xmlEncode($payload);
864c3437056SNickeau
865c3437056SNickeau    }
866c3437056SNickeau
86737748cd8SNickeau    /**
86837748cd8SNickeau     * Function used in a handle function of a syntax plugin for
86937748cd8SNickeau     * unmatched context
87037748cd8SNickeau     * @param $tagName
87137748cd8SNickeau     * @param $match
87237748cd8SNickeau     * @param \Doku_Handler $handler
87337748cd8SNickeau     * @return array
87437748cd8SNickeau     */
87537748cd8SNickeau    public
8761fa8c418SNickeau    static function handleAndReturnUnmatchedData($tagName, $match, \Doku_Handler $handler): array
87737748cd8SNickeau    {
8781fa8c418SNickeau        $callStack = CallStack::createFromHandler($handler);
8791fa8c418SNickeau        $sibling = $callStack->previous();
88037748cd8SNickeau        $context = null;
88137748cd8SNickeau        if (!empty($sibling)) {
8821fa8c418SNickeau            $context = $sibling->getDisplay();
88337748cd8SNickeau        }
88437748cd8SNickeau        return array(
88537748cd8SNickeau            PluginUtility::STATE => DOKU_LEXER_UNMATCHED,
88637748cd8SNickeau            PluginUtility::PAYLOAD => $match,
88737748cd8SNickeau            PluginUtility::CONTEXT => $context
88837748cd8SNickeau        );
88937748cd8SNickeau    }
89037748cd8SNickeau
89137748cd8SNickeau    /**
89237748cd8SNickeau     * Utility methodPreprocess a start tag to be able to extract the name
89337748cd8SNickeau     * and the attributes easily
89437748cd8SNickeau     *
89537748cd8SNickeau     * It will delete:
89637748cd8SNickeau     *   * the characters <> and the /> if present
89737748cd8SNickeau     *   * and trim
89837748cd8SNickeau     *
89937748cd8SNickeau     * It will remain the tagname and its attributes
90037748cd8SNickeau     * @param $match
90137748cd8SNickeau     * @return false|string|null
90237748cd8SNickeau     */
90337748cd8SNickeau    private
90437748cd8SNickeau    static function getPreprocessEnterTag($match)
90537748cd8SNickeau    {
90637748cd8SNickeau// Until the first >
90737748cd8SNickeau        $pos = strpos($match, ">");
90804fd306cSNickeau        if (!$pos) {
90937748cd8SNickeau            LogUtility::msg("The match does not contain any tag. Match: {$match}", LogUtility::LVL_MSG_WARNING);
91037748cd8SNickeau            return null;
91137748cd8SNickeau        }
91237748cd8SNickeau        $match = substr($match, 0, $pos);
91337748cd8SNickeau
91437748cd8SNickeau
91537748cd8SNickeau// Trim to start clean
91637748cd8SNickeau        $match = trim($match);
91737748cd8SNickeau
91837748cd8SNickeau// Suppress the <
91937748cd8SNickeau        if ($match[0] == "<") {
92037748cd8SNickeau            $match = substr($match, 1);
92137748cd8SNickeau        }
92237748cd8SNickeau
92337748cd8SNickeau// Suppress the / for a leaf tag
92437748cd8SNickeau        if ($match[strlen($match) - 1] == "/") {
92537748cd8SNickeau            $match = substr($match, 0, strlen($match) - 1);
92637748cd8SNickeau        }
92737748cd8SNickeau        return $match;
92837748cd8SNickeau    }
92937748cd8SNickeau
93037748cd8SNickeau    /**
93137748cd8SNickeau     * Retrieve the tag name used in the text document
93237748cd8SNickeau     * @param $match
93337748cd8SNickeau     * @return false|string|null
93437748cd8SNickeau     */
93537748cd8SNickeau    public
93637748cd8SNickeau    static function getSyntaxTagNameFromMatch($match)
93737748cd8SNickeau    {
93837748cd8SNickeau        $preprocessMatch = PluginUtility::getPreprocessEnterTag($match);
93937748cd8SNickeau
94037748cd8SNickeau// Tag name (ie until the first blank)
94137748cd8SNickeau        $spacePosition = strpos($match, " ");
94237748cd8SNickeau        if (!$spacePosition) {
94337748cd8SNickeau// No space, meaning this is only the tag name
94437748cd8SNickeau            return $preprocessMatch;
94537748cd8SNickeau        } else {
94637748cd8SNickeau            return trim(substr(0, $spacePosition));
94737748cd8SNickeau        }
94837748cd8SNickeau
94937748cd8SNickeau    }
95037748cd8SNickeau
95137748cd8SNickeau    /**
95237748cd8SNickeau     * Add an enter call to the stack
95337748cd8SNickeau     * @param \Doku_Handler $handler
95437748cd8SNickeau     * @param $tagName
95537748cd8SNickeau     * @param array $callStackArray
95637748cd8SNickeau     */
95737748cd8SNickeau    public
95837748cd8SNickeau    static function addEnterCall(
95937748cd8SNickeau        \Doku_Handler &$handler,
96037748cd8SNickeau                      $tagName,
96137748cd8SNickeau                      $callStackArray = array()
96237748cd8SNickeau    )
96337748cd8SNickeau    {
96437748cd8SNickeau        $pluginName = PluginUtility::getComponentName($tagName);
96537748cd8SNickeau        $handler->addPluginCall(
96637748cd8SNickeau            $pluginName,
96737748cd8SNickeau            $callStackArray,
96837748cd8SNickeau            DOKU_LEXER_ENTER,
96937748cd8SNickeau            null,
97037748cd8SNickeau            null
97137748cd8SNickeau        );
97237748cd8SNickeau    }
97337748cd8SNickeau
97404fd306cSNickeau    /**
97504fd306cSNickeau     * Add an end call dynamically
97604fd306cSNickeau     * @param \Doku_Handler $handler
97704fd306cSNickeau     * @param $tagName
97804fd306cSNickeau     * @param array $callStackArray
97904fd306cSNickeau     */
98004fd306cSNickeau    public
98104fd306cSNickeau    static function addEndCall(\Doku_Handler $handler, $tagName, $callStackArray = array())
98204fd306cSNickeau    {
98304fd306cSNickeau        $pluginName = PluginUtility::getComponentName($tagName);
98404fd306cSNickeau        $handler->addPluginCall(
98504fd306cSNickeau            $pluginName,
98604fd306cSNickeau            $callStackArray,
98704fd306cSNickeau            DOKU_LEXER_EXIT,
98804fd306cSNickeau            null,
98904fd306cSNickeau            null
99004fd306cSNickeau        );
99104fd306cSNickeau    }
99237748cd8SNickeau
99337748cd8SNickeau    /**
99437748cd8SNickeau     * General Debug
99537748cd8SNickeau     */
99637748cd8SNickeau    public
99737748cd8SNickeau    static function isDebug()
99837748cd8SNickeau    {
99937748cd8SNickeau        global $conf;
100037748cd8SNickeau        return $conf["allowdebug"] === 1;
100137748cd8SNickeau
100237748cd8SNickeau    }
100337748cd8SNickeau
100437748cd8SNickeau
100537748cd8SNickeau    /**
100637748cd8SNickeau     *
100737748cd8SNickeau     * See also dev.md file
100837748cd8SNickeau     */
100937748cd8SNickeau    public static function isDevOrTest()
101037748cd8SNickeau    {
101137748cd8SNickeau        if (self::isDev()) {
101237748cd8SNickeau            return true;
101337748cd8SNickeau        }
101437748cd8SNickeau        return self::isTest();
101537748cd8SNickeau    }
101637748cd8SNickeau
10174cadd4f8SNickeau    /**
10184cadd4f8SNickeau     * Is this a dev environment (ie laptop where the dev is working)
10194cadd4f8SNickeau     * @return bool
10204cadd4f8SNickeau     */
10214cadd4f8SNickeau    public static function isDev(): bool
102237748cd8SNickeau    {
102337748cd8SNickeau        global $_SERVER;
102437748cd8SNickeau        if ($_SERVER["REMOTE_ADDR"] == "127.0.0.1") {
102537748cd8SNickeau            return true;
102637748cd8SNickeau        }
10274cadd4f8SNickeau        if ($_SERVER["COMPUTERNAME"] === "NICO") {
10284cadd4f8SNickeau            return true;
10294cadd4f8SNickeau        }
103037748cd8SNickeau        return false;
103137748cd8SNickeau    }
103237748cd8SNickeau
103337748cd8SNickeau    public static function getInstructions($markiCode)
103437748cd8SNickeau    {
103537748cd8SNickeau        return p_get_instructions($markiCode);
103637748cd8SNickeau    }
103737748cd8SNickeau
103837748cd8SNickeau    public static function getInstructionsWithoutRoot($markiCode)
103937748cd8SNickeau    {
104004fd306cSNickeau        return MarkupRenderUtility::getInstructionsAndStripPEventually($markiCode);
104137748cd8SNickeau    }
104237748cd8SNickeau
104304fd306cSNickeau    public static function isTest(): bool
104437748cd8SNickeau    {
104537748cd8SNickeau        return defined('DOKU_UNITTEST');
104637748cd8SNickeau    }
104737748cd8SNickeau
104837748cd8SNickeau
1049c3437056SNickeau    public static function getCacheManager(): CacheManager
105037748cd8SNickeau    {
105104fd306cSNickeau        return CacheManager::getFromContextExecution();
105237748cd8SNickeau    }
105337748cd8SNickeau
105437748cd8SNickeau    public static function getModeFromPluginName($name)
105537748cd8SNickeau    {
105637748cd8SNickeau        return "plugin_$name";
105737748cd8SNickeau    }
105837748cd8SNickeau
105937748cd8SNickeau    public static function isCi(): bool
106037748cd8SNickeau    {
106137748cd8SNickeau        // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
106204fd306cSNickeau        // https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
106337748cd8SNickeau        return getenv("CI") === "true";
106437748cd8SNickeau    }
106537748cd8SNickeau
10664cadd4f8SNickeau
10674cadd4f8SNickeau    /**
106804fd306cSNickeau     * @throws ExceptionCompile
10694cadd4f8SNickeau     */
10704cadd4f8SNickeau    public static function renderInstructionsToXhtml($callStackHeaderInstructions): ?string
10714cadd4f8SNickeau    {
107204fd306cSNickeau        return MarkupRenderUtility::renderInstructionsToXhtml($callStackHeaderInstructions);
10734cadd4f8SNickeau    }
10744cadd4f8SNickeau
10754cadd4f8SNickeau    /**
107604fd306cSNickeau     * @deprecated for {@link ExecutionContext::getExecutingWikiId()}
10774cadd4f8SNickeau     */
107804fd306cSNickeau    public static function getCurrentSlotId(): string
10794cadd4f8SNickeau    {
108004fd306cSNickeau        return ExecutionContext::getActualOrCreateFromEnv()->getExecutingWikiId();
10814cadd4f8SNickeau    }
10824cadd4f8SNickeau
108337748cd8SNickeau
108437748cd8SNickeau}
108537748cd8SNickeau
108637748cd8SNickeauPluginUtility::init();
1087