xref: /template/strap/ComboStrap/Call.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
137748cd8SNickeau<?php
237748cd8SNickeau/**
337748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
437748cd8SNickeau *
537748cd8SNickeau * This source code is licensed under the GPL license found in the
637748cd8SNickeau * COPYING  file in the root directory of this source tree.
737748cd8SNickeau *
837748cd8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
937748cd8SNickeau * @author   ComboStrap <support@combostrap.com>
1037748cd8SNickeau *
1137748cd8SNickeau */
1237748cd8SNickeau
1337748cd8SNickeaunamespace ComboStrap;
1437748cd8SNickeau
1537748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin;
16*1fa8c418SNickeauuse syntax_plugin_combo_media;
17*1fa8c418SNickeauuse syntax_plugin_combo_pageimage;
1837748cd8SNickeau
1937748cd8SNickeau
2037748cd8SNickeau/**
2137748cd8SNickeau * Class Call
2237748cd8SNickeau * @package ComboStrap
2337748cd8SNickeau *
2437748cd8SNickeau * A wrapper around what's called a call
2537748cd8SNickeau * which is an array of information such
2637748cd8SNickeau * the mode, the data
2737748cd8SNickeau *
2837748cd8SNickeau * The {@link CallStack} is the only syntax representation that
2937748cd8SNickeau * is available in DokuWiki
3037748cd8SNickeau */
3137748cd8SNickeauclass Call
3237748cd8SNickeau{
3337748cd8SNickeau
3437748cd8SNickeau    const INLINE_DISPLAY = "inline";
3537748cd8SNickeau    const BlOCK_DISPLAY = "block";
3637748cd8SNickeau    /**
3737748cd8SNickeau     * List of inline components
3837748cd8SNickeau     * Used to manage white space before an unmatched string.
3937748cd8SNickeau     * The syntax tree of Dokuwiki (ie {@link \Doku_Handler::$calls})
4037748cd8SNickeau     * has only data and no class, for now, we create this
4137748cd8SNickeau     * lists manually because this is a hassle to retrieve this information from {@link \DokuWiki_Syntax_Plugin::getType()}
4237748cd8SNickeau     */
4337748cd8SNickeau    const INLINE_DOKUWIKI_COMPONENTS = array(
4437748cd8SNickeau        /**
4537748cd8SNickeau         * Formatting https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
4637748cd8SNickeau         * Comes from the {@link \dokuwiki\Parsing\ParserMode\Formatting} class
4737748cd8SNickeau         */
4837748cd8SNickeau        "cdata",
4937748cd8SNickeau        "unformatted", // ie %% or nowiki
5037748cd8SNickeau        "doublequoteclosing", // https://www.dokuwiki.org/config:typography / https://www.dokuwiki.org/wiki:syntax#text_to_html_conversions
5137748cd8SNickeau        "doublequoteopening",
5237748cd8SNickeau        "singlequoteopening",
5337748cd8SNickeau        "singlequoteclosing",
5437748cd8SNickeau        "multiplyentity",
5537748cd8SNickeau        "apostrophe",
5637748cd8SNickeau        "strong",
5737748cd8SNickeau        "emphasis",
5837748cd8SNickeau        "emphasis_open",
5937748cd8SNickeau        "emphasis_close",
6037748cd8SNickeau        "underline",
6137748cd8SNickeau        "monospace",
6237748cd8SNickeau        "subscript",
6337748cd8SNickeau        "superscript",
6437748cd8SNickeau        "deleted",
6537748cd8SNickeau        "footnote",
6637748cd8SNickeau        /**
6737748cd8SNickeau         * Others
6837748cd8SNickeau         */
69*1fa8c418SNickeau        "acronym", // abbr
7037748cd8SNickeau        "strong_close",
7137748cd8SNickeau        "strong_open",
7237748cd8SNickeau        "monospace_open",
7337748cd8SNickeau        "monospace_close",
7437748cd8SNickeau        "doublequoteopening", // ie the character " in "The"
7537748cd8SNickeau        "entity", // for instance `...` are transformed in character
7637748cd8SNickeau        "linebreak",
7737748cd8SNickeau        "externallink",
7837748cd8SNickeau        "internallink",
7937748cd8SNickeau        MediaLink::INTERNAL_MEDIA_CALL_NAME,
8037748cd8SNickeau        MediaLink::EXTERNAL_MEDIA_CALL_NAME,
8137748cd8SNickeau        /**
8237748cd8SNickeau         * The inline of combo
8337748cd8SNickeau         */
8437748cd8SNickeau        \syntax_plugin_combo_link::TAG,
8537748cd8SNickeau        \syntax_plugin_combo_icon::TAG,
8637748cd8SNickeau        \syntax_plugin_combo_inote::TAG,
8737748cd8SNickeau        \syntax_plugin_combo_button::TAG,
8837748cd8SNickeau        \syntax_plugin_combo_tooltip::TAG,
8937748cd8SNickeau        \syntax_plugin_combo_pipeline::TAG,
9037748cd8SNickeau    );
9137748cd8SNickeau
9237748cd8SNickeau
9337748cd8SNickeau    const BLOCK_MARKUP_DOKUWIKI_COMPONENTS = array(
9437748cd8SNickeau        "listu_open", // ul
9537748cd8SNickeau        "listu_close",
9637748cd8SNickeau        "listitem_open", //li
9737748cd8SNickeau        "listitem_close",
9837748cd8SNickeau        "listcontent_open", // after li ???
9937748cd8SNickeau        "listcontent_close",
10037748cd8SNickeau        "table_open",
10137748cd8SNickeau        "table_close",
10237748cd8SNickeau    );
10337748cd8SNickeau
104*1fa8c418SNickeau    /**
105*1fa8c418SNickeau     * A media is not really an image
106*1fa8c418SNickeau     * but it may contains one
107*1fa8c418SNickeau     */
108*1fa8c418SNickeau    const IMAGE_TAGS = [
109*1fa8c418SNickeau        syntax_plugin_combo_media::TAG,
110*1fa8c418SNickeau        syntax_plugin_combo_pageimage::TAG
111*1fa8c418SNickeau    ];
112*1fa8c418SNickeau
11337748cd8SNickeau    private $call;
11437748cd8SNickeau
11537748cd8SNickeau    /**
11637748cd8SNickeau     * The key identifier in the {@link CallStack}
11737748cd8SNickeau     * @var mixed|string
11837748cd8SNickeau     */
11937748cd8SNickeau    private $key;
12037748cd8SNickeau
12137748cd8SNickeau    /**
12237748cd8SNickeau     * Call constructor.
12337748cd8SNickeau     * @param $call - the instruction array (ie called a call)
12437748cd8SNickeau     */
12537748cd8SNickeau    public function __construct(&$call, $key = "")
12637748cd8SNickeau    {
12737748cd8SNickeau        $this->call = &$call;
12837748cd8SNickeau        $this->key = $key;
12937748cd8SNickeau    }
13037748cd8SNickeau
13137748cd8SNickeau    /**
13237748cd8SNickeau     * Insert a tag above
13337748cd8SNickeau     * @param $tagName
13437748cd8SNickeau     * @param $state
13537748cd8SNickeau     * @param $attribute
13637748cd8SNickeau     * @param $context
13737748cd8SNickeau     * @param string $content
13837748cd8SNickeau     * @return Call - a call
13937748cd8SNickeau     */
14037748cd8SNickeau    public static function createComboCall($tagName, $state, $attribute = array(), $context = null, $content = '', $payload = null)
14137748cd8SNickeau    {
14237748cd8SNickeau        $data = array(
14337748cd8SNickeau            PluginUtility::ATTRIBUTES => $attribute,
14437748cd8SNickeau            PluginUtility::CONTEXT => $context,
14537748cd8SNickeau            PluginUtility::STATE => $state
14637748cd8SNickeau        );
14737748cd8SNickeau        if ($payload != null) {
14837748cd8SNickeau            $data[PluginUtility::PAYLOAD] = $payload;
14937748cd8SNickeau        }
15037748cd8SNickeau        $positionInText = null;
15137748cd8SNickeau
15237748cd8SNickeau        $call = [
15337748cd8SNickeau            "plugin",
15437748cd8SNickeau            array(
15537748cd8SNickeau                PluginUtility::getComponentName($tagName),
15637748cd8SNickeau                $data,
15737748cd8SNickeau                $state,
15837748cd8SNickeau                $content
15937748cd8SNickeau            ),
16037748cd8SNickeau            $positionInText
16137748cd8SNickeau        ];
16237748cd8SNickeau        return new Call($call);
16337748cd8SNickeau    }
16437748cd8SNickeau
16537748cd8SNickeau    /**
16637748cd8SNickeau     * Insert a dokuwiki call
16737748cd8SNickeau     * @param $callName
16837748cd8SNickeau     * @param $array
16937748cd8SNickeau     * @param $positionInText
17037748cd8SNickeau     * @return Call
17137748cd8SNickeau     */
17237748cd8SNickeau    public static function createNativeCall($callName, $array = [], $positionInText = null)
17337748cd8SNickeau    {
17437748cd8SNickeau        $call = [
17537748cd8SNickeau            $callName,
17637748cd8SNickeau            $array,
17737748cd8SNickeau            $positionInText
17837748cd8SNickeau        ];
17937748cd8SNickeau        return new Call($call);
18037748cd8SNickeau    }
18137748cd8SNickeau
18237748cd8SNickeau    public static function createFromInstruction($instruction)
18337748cd8SNickeau    {
18437748cd8SNickeau        return new Call($instruction);
18537748cd8SNickeau    }
18637748cd8SNickeau
18737748cd8SNickeau
18837748cd8SNickeau    /**
18937748cd8SNickeau     *
19037748cd8SNickeau     * Return the tag name from a call array
19137748cd8SNickeau     *
19237748cd8SNickeau     * This is not the logical tag.
19337748cd8SNickeau     * This is much more what's called:
19437748cd8SNickeau     *   * the component name for a plugin
19537748cd8SNickeau     *   * or the handler name for dokuwiki
19637748cd8SNickeau     *
19737748cd8SNickeau     * For a plugin, this is equivalent
19837748cd8SNickeau     * to the {@link SyntaxPlugin::getPluginComponent()}
19937748cd8SNickeau     *
20037748cd8SNickeau     * This is not the fully qualified component name:
20137748cd8SNickeau     *   * with the plugin as prefix such as in {@link Call::getComponentName()}
20237748cd8SNickeau     *   * or with the `open` and `close` prefix such as `p_close` ...
20337748cd8SNickeau     *
20437748cd8SNickeau     * @return mixed|string
20537748cd8SNickeau     */
20637748cd8SNickeau    public function getTagName()
20737748cd8SNickeau    {
20837748cd8SNickeau        $mode = $this->call[0];
20937748cd8SNickeau        if ($mode != "plugin") {
21037748cd8SNickeau
21137748cd8SNickeau            /**
21237748cd8SNickeau             * This is a standard dokuwiki node
21337748cd8SNickeau             */
21437748cd8SNickeau            $dokuWikiNodeName = $this->call[0];
21537748cd8SNickeau
21637748cd8SNickeau            /**
21737748cd8SNickeau             * The dokwuiki node name has also the open and close notion
21837748cd8SNickeau             * We delete this is not in the doc and therefore not logical
21937748cd8SNickeau             */
22037748cd8SNickeau            $tagName = str_replace("_close", "", $dokuWikiNodeName);
22137748cd8SNickeau            $tagName = str_replace("_open", "", $tagName);
22237748cd8SNickeau
22337748cd8SNickeau        } else {
22437748cd8SNickeau
22537748cd8SNickeau            /**
22637748cd8SNickeau             * This is a plugin node
22737748cd8SNickeau             */
22837748cd8SNickeau            $pluginDokuData = $this->call[1];
22937748cd8SNickeau            $component = $pluginDokuData[0];
23037748cd8SNickeau            if (!is_array($component)) {
23137748cd8SNickeau                /**
23237748cd8SNickeau                 * Tag name from class
23337748cd8SNickeau                 */
23437748cd8SNickeau                $componentNames = explode("_", $component);
23537748cd8SNickeau                /**
23637748cd8SNickeau                 * To take care of
23737748cd8SNickeau                 * PHP Warning:  sizeof(): Parameter must be an array or an object that implements Countable
23837748cd8SNickeau                 * in lib/plugins/combo/class/Tag.php on line 314
23937748cd8SNickeau                 */
24037748cd8SNickeau                if (is_array($componentNames)) {
24137748cd8SNickeau                    $tagName = $componentNames[sizeof($componentNames) - 1];
24237748cd8SNickeau                } else {
24337748cd8SNickeau                    $tagName = $component;
24437748cd8SNickeau                }
24537748cd8SNickeau            } else {
24637748cd8SNickeau                // To resolve: explode() expects parameter 2 to be string, array given
24737748cd8SNickeau                LogUtility::msg("The call (" . print_r($this->call, true) . ") has an array and not a string as component (" . print_r($component, true) . "). Page: " . PluginUtility::getPageId(), LogUtility::LVL_MSG_ERROR);
24837748cd8SNickeau                $tagName = "";
24937748cd8SNickeau            }
25037748cd8SNickeau
25137748cd8SNickeau
25237748cd8SNickeau        }
25337748cd8SNickeau        return $tagName;
25437748cd8SNickeau
25537748cd8SNickeau    }
25637748cd8SNickeau
25737748cd8SNickeau
25837748cd8SNickeau    /**
25937748cd8SNickeau     * The parser state
26037748cd8SNickeau     * @return mixed
26137748cd8SNickeau     * May be null (example eol, internallink, ...)
26237748cd8SNickeau     */
26337748cd8SNickeau    public function getState()
26437748cd8SNickeau    {
26537748cd8SNickeau        $mode = $this->call[0];
26637748cd8SNickeau        if ($mode != "plugin") {
26737748cd8SNickeau
26837748cd8SNickeau            /**
26937748cd8SNickeau             * There is no state because this is a standard
27037748cd8SNickeau             * dokuwiki syntax found in {@link \Doku_Renderer_xhtml}
27137748cd8SNickeau             * check if this is not a `...._close` or `...._open`
27237748cd8SNickeau             * to derive the state
27337748cd8SNickeau             */
27437748cd8SNickeau            $mode = $this->call[0];
27537748cd8SNickeau            $lastPositionSepName = strrpos($mode, "_");
27637748cd8SNickeau            $closeOrOpen = substr($mode, $lastPositionSepName + 1);
27737748cd8SNickeau            switch ($closeOrOpen) {
27837748cd8SNickeau                case "open":
27937748cd8SNickeau                    return DOKU_LEXER_ENTER;
28037748cd8SNickeau                case "close":
28137748cd8SNickeau                    return DOKU_LEXER_EXIT;
28237748cd8SNickeau                default:
28337748cd8SNickeau                    return null;
28437748cd8SNickeau            }
28537748cd8SNickeau
28637748cd8SNickeau        } else {
28737748cd8SNickeau            // Plugin
28837748cd8SNickeau            $returnedArray = $this->call[1];
28937748cd8SNickeau            if (array_key_exists(2, $returnedArray)) {
29037748cd8SNickeau                return $returnedArray[2];
29137748cd8SNickeau            } else {
29237748cd8SNickeau                return null;
29337748cd8SNickeau            }
29437748cd8SNickeau        }
29537748cd8SNickeau    }
29637748cd8SNickeau
29737748cd8SNickeau    /**
29837748cd8SNickeau     * @return mixed the data returned from the {@link DokuWiki_Syntax_Plugin::handle} (ie attributes, payload, ...)
29937748cd8SNickeau     */
30037748cd8SNickeau    public function &getPluginData()
30137748cd8SNickeau    {
30237748cd8SNickeau        return $this->call[1][1];
30337748cd8SNickeau    }
30437748cd8SNickeau
30537748cd8SNickeau    /**
30637748cd8SNickeau     * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle}
30737748cd8SNickeau     */
30837748cd8SNickeau    public function getCapturedContent()
30937748cd8SNickeau    {
31037748cd8SNickeau        $caller = $this->call[0];
31137748cd8SNickeau        switch ($caller) {
31237748cd8SNickeau            case "plugin":
31337748cd8SNickeau                return $this->call[1][3];
31437748cd8SNickeau            case "internallink":
31537748cd8SNickeau                return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]';
31637748cd8SNickeau            case "eol":
31737748cd8SNickeau                return DOKU_LF;
31837748cd8SNickeau            case "header":
31937748cd8SNickeau            case "cdata":
32037748cd8SNickeau                return $this->call[1][0];
32137748cd8SNickeau            default:
32237748cd8SNickeau                if (isset($this->call[1][0]) && is_string($this->call[1][0])) {
32337748cd8SNickeau                    return $this->call[1][0];
32437748cd8SNickeau                } else {
32537748cd8SNickeau                    return "";
32637748cd8SNickeau                }
32737748cd8SNickeau        }
32837748cd8SNickeau    }
32937748cd8SNickeau
33037748cd8SNickeau
33137748cd8SNickeau    public function getAttributes()
33237748cd8SNickeau    {
33337748cd8SNickeau
33437748cd8SNickeau        $tagName = $this->getTagName();
33537748cd8SNickeau        switch ($tagName) {
33637748cd8SNickeau            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
33737748cd8SNickeau                return $this->call[1];
33837748cd8SNickeau            default:
33937748cd8SNickeau                $data = $this->getPluginData();
34037748cd8SNickeau                if (isset($data[PluginUtility::ATTRIBUTES])) {
34137748cd8SNickeau                    return $data[PluginUtility::ATTRIBUTES];
34237748cd8SNickeau                } else {
34337748cd8SNickeau                    return null;
34437748cd8SNickeau                }
34537748cd8SNickeau        }
34637748cd8SNickeau    }
34737748cd8SNickeau
34837748cd8SNickeau    public function removeAttributes()
34937748cd8SNickeau    {
35037748cd8SNickeau
35137748cd8SNickeau        $data = &$this->getPluginData();
35237748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES])) {
35337748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES]);
35437748cd8SNickeau        }
35537748cd8SNickeau
35637748cd8SNickeau    }
35737748cd8SNickeau
35837748cd8SNickeau    public function updateToPluginComponent($component, $state, $attributes = array())
35937748cd8SNickeau    {
36037748cd8SNickeau        if ($this->call[0] == "plugin") {
36137748cd8SNickeau            $match = $this->call[1][3];
36237748cd8SNickeau        } else {
36337748cd8SNickeau            $this->call[0] = "plugin";
36437748cd8SNickeau            $match = "";
36537748cd8SNickeau        }
36637748cd8SNickeau        $this->call[1] = array(
36737748cd8SNickeau            0 => $component,
36837748cd8SNickeau            1 => array(
36937748cd8SNickeau                PluginUtility::ATTRIBUTES => $attributes,
37037748cd8SNickeau                PluginUtility::STATE => $state,
37137748cd8SNickeau            ),
37237748cd8SNickeau            2 => $state,
37337748cd8SNickeau            3 => $match
37437748cd8SNickeau        );
37537748cd8SNickeau
37637748cd8SNickeau    }
37737748cd8SNickeau
378*1fa8c418SNickeau    /**
379*1fa8c418SNickeau     * Does the display has been set
380*1fa8c418SNickeau     * to override the dokuwiki default
381*1fa8c418SNickeau     * ({@link Syntax::getPType()}
382*1fa8c418SNickeau     *
383*1fa8c418SNickeau     * because an image is by default a inline component
384*1fa8c418SNickeau     * but can be a block (ie top image of a card)
385*1fa8c418SNickeau     * @return bool
386*1fa8c418SNickeau     */
387*1fa8c418SNickeau    public function isDisplaySet(): bool
388*1fa8c418SNickeau    {
389*1fa8c418SNickeau        return isset($this->call[1][1][PluginUtility::DISPLAY]);
390*1fa8c418SNickeau    }
391*1fa8c418SNickeau
39237748cd8SNickeau    public function getDisplay()
39337748cd8SNickeau    {
394*1fa8c418SNickeau        $mode = $this->getMode();
395*1fa8c418SNickeau        if ($mode == "plugin") {
396*1fa8c418SNickeau            if ($this->isDisplaySet()) {
397*1fa8c418SNickeau                return $this->call[1][1][PluginUtility::DISPLAY];
398*1fa8c418SNickeau            }
399*1fa8c418SNickeau        }
400*1fa8c418SNickeau
40137748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
40237748cd8SNickeau            /**
40337748cd8SNickeau             * Unmatched are content (ie text node in XML/HTML) and have
40437748cd8SNickeau             * no display
40537748cd8SNickeau             */
40637748cd8SNickeau            return Call::INLINE_DISPLAY;
40737748cd8SNickeau        } else {
40837748cd8SNickeau            $mode = $this->call[0];
40937748cd8SNickeau            if ($mode == "plugin") {
41037748cd8SNickeau                global $DOKU_PLUGINS;
41137748cd8SNickeau                $component = $this->getComponentName();
41237748cd8SNickeau                /**
41337748cd8SNickeau                 * @var SyntaxPlugin $syntaxPlugin
41437748cd8SNickeau                 */
41537748cd8SNickeau                $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component];
41637748cd8SNickeau                $pType = $syntaxPlugin->getPType();
41737748cd8SNickeau                switch ($pType) {
41837748cd8SNickeau                    case "normal":
41937748cd8SNickeau                        return Call::INLINE_DISPLAY;
42037748cd8SNickeau                    case "block":
42137748cd8SNickeau                    case "stack":
42237748cd8SNickeau                        return Call::BlOCK_DISPLAY;
42337748cd8SNickeau                    default:
42437748cd8SNickeau                        LogUtility::msg("The ptype (" . $pType . ") is unknown.");
42537748cd8SNickeau                        return null;
42637748cd8SNickeau                }
42737748cd8SNickeau            } else {
42837748cd8SNickeau                if ($mode == "eol") {
42937748cd8SNickeau                    /**
43037748cd8SNickeau                     * Control character
43137748cd8SNickeau                     * We return it as it's used in the
43237748cd8SNickeau                     * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()}
43337748cd8SNickeau                     * to create the paragraph
43437748cd8SNickeau                     * This is not a block, nor an inline
43537748cd8SNickeau                     */
43637748cd8SNickeau                    return $mode;
43737748cd8SNickeau                }
43837748cd8SNickeau
43937748cd8SNickeau                if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) {
44037748cd8SNickeau                    return Call::INLINE_DISPLAY;
44137748cd8SNickeau                }
44237748cd8SNickeau
44337748cd8SNickeau                if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) {
44437748cd8SNickeau                    return Call::BlOCK_DISPLAY;
44537748cd8SNickeau                }
44637748cd8SNickeau
44737748cd8SNickeau                LogUtility::msg("The display of the call with the mode " . $mode . " is unknown");
44837748cd8SNickeau                return null;
44937748cd8SNickeau
45037748cd8SNickeau
45137748cd8SNickeau            }
45237748cd8SNickeau        }
45337748cd8SNickeau
45437748cd8SNickeau    }
45537748cd8SNickeau
45637748cd8SNickeau    /**
45737748cd8SNickeau     * Same as {@link Call::getTagName()}
45837748cd8SNickeau     * but fully qualified
45937748cd8SNickeau     * @return string
46037748cd8SNickeau     */
46137748cd8SNickeau    public function getComponentName()
46237748cd8SNickeau    {
46337748cd8SNickeau        $mode = $this->call[0];
46437748cd8SNickeau        if ($mode == "plugin") {
46537748cd8SNickeau            $pluginDokuData = $this->call[1];
46637748cd8SNickeau            return $pluginDokuData[0];
46737748cd8SNickeau        } else {
46837748cd8SNickeau            return $mode;
46937748cd8SNickeau        }
47037748cd8SNickeau    }
47137748cd8SNickeau
47237748cd8SNickeau    public function updateEolToSpace()
47337748cd8SNickeau    {
47437748cd8SNickeau        $mode = $this->call[0];
47537748cd8SNickeau        if ($mode != "eol") {
47637748cd8SNickeau            LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support");
47737748cd8SNickeau        } else {
47837748cd8SNickeau            $this->call[0] = "cdata";
47937748cd8SNickeau            $this->call[1] = array(
48037748cd8SNickeau                0 => " "
48137748cd8SNickeau            );
48237748cd8SNickeau        }
48337748cd8SNickeau
48437748cd8SNickeau    }
48537748cd8SNickeau
48637748cd8SNickeau    public function addAttribute($key, $value)
48737748cd8SNickeau    {
48837748cd8SNickeau        $mode = $this->call[0];
48937748cd8SNickeau        if ($mode == "plugin") {
49037748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value;
49137748cd8SNickeau        } else {
49237748cd8SNickeau            LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
49337748cd8SNickeau        }
49437748cd8SNickeau    }
49537748cd8SNickeau
49637748cd8SNickeau    public function getContext()
49737748cd8SNickeau    {
49837748cd8SNickeau        $mode = $this->call[0];
49937748cd8SNickeau        if ($mode == "plugin") {
50037748cd8SNickeau            return $this->call[1][1][PluginUtility::CONTEXT];
50137748cd8SNickeau        } else {
50237748cd8SNickeau            LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
50337748cd8SNickeau            return null;
50437748cd8SNickeau        }
50537748cd8SNickeau    }
50637748cd8SNickeau
50737748cd8SNickeau    /**
50837748cd8SNickeau     *
50937748cd8SNickeau     * @return array
51037748cd8SNickeau     */
51137748cd8SNickeau    public function toCallArray()
51237748cd8SNickeau    {
51337748cd8SNickeau        return $this->call;
51437748cd8SNickeau    }
51537748cd8SNickeau
51637748cd8SNickeau    public function __toString()
51737748cd8SNickeau    {
51837748cd8SNickeau        $name = $this->key;
51937748cd8SNickeau        if (!empty($name)) {
52037748cd8SNickeau            $name .= " - ";
52137748cd8SNickeau        }
52237748cd8SNickeau        $name .= $this->getTagName();
52337748cd8SNickeau        return $name;
52437748cd8SNickeau    }
52537748cd8SNickeau
52637748cd8SNickeau    public function getType()
52737748cd8SNickeau    {
52837748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
52937748cd8SNickeau            return null;
53037748cd8SNickeau        } else {
53137748cd8SNickeau            /**
53237748cd8SNickeau             * don't use {@link Call::getAttribute()} to get the type
53337748cd8SNickeau             * as this function stack also depends on
53437748cd8SNickeau             * this function {@link Call::getType()}
53537748cd8SNickeau             * to return the value
53637748cd8SNickeau             * Ie: if this is a boolean attribute without specified type
53737748cd8SNickeau             * if the boolean value is in the type, we return it
53837748cd8SNickeau             */
53937748cd8SNickeau            return $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY];
54037748cd8SNickeau        }
54137748cd8SNickeau    }
54237748cd8SNickeau
54337748cd8SNickeau    /**
54437748cd8SNickeau     * @param $key
54537748cd8SNickeau     * @param null $default
54637748cd8SNickeau     * @return string|null
54737748cd8SNickeau     */
54837748cd8SNickeau    public function getAttribute($key, $default = null)
54937748cd8SNickeau    {
55037748cd8SNickeau        $attributes = $this->getAttributes();
55137748cd8SNickeau        if (isset($attributes[$key])) {
55237748cd8SNickeau            return $attributes[$key];
55337748cd8SNickeau        } else {
55437748cd8SNickeau            // boolean attribute
55537748cd8SNickeau            if ($this->getType() == $key) {
55637748cd8SNickeau                return true;
55737748cd8SNickeau            } else {
55837748cd8SNickeau                return $default;
55937748cd8SNickeau            }
56037748cd8SNickeau        }
56137748cd8SNickeau    }
56237748cd8SNickeau
56337748cd8SNickeau    public function getPayload()
56437748cd8SNickeau    {
56537748cd8SNickeau        $mode = $this->call[0];
56637748cd8SNickeau        if ($mode == "plugin") {
56737748cd8SNickeau            return $this->call[1][1][PluginUtility::PAYLOAD];
56837748cd8SNickeau        } else {
56937748cd8SNickeau            LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support");
57037748cd8SNickeau            return null;
57137748cd8SNickeau        }
57237748cd8SNickeau    }
57337748cd8SNickeau
57437748cd8SNickeau    public function setContext($value)
57537748cd8SNickeau    {
57637748cd8SNickeau        $this->call[1][1][PluginUtility::CONTEXT] = $value;
57737748cd8SNickeau        return $this;
57837748cd8SNickeau    }
57937748cd8SNickeau
58037748cd8SNickeau    public function hasAttribute($attributeName)
58137748cd8SNickeau    {
58237748cd8SNickeau        $attributes = $this->getAttributes();
58337748cd8SNickeau        if (isset($attributes[$attributeName])) {
58437748cd8SNickeau            return true;
58537748cd8SNickeau        } else {
58637748cd8SNickeau            if ($this->getType() == $attributeName) {
58737748cd8SNickeau                return true;
58837748cd8SNickeau            } else {
58937748cd8SNickeau                return false;
59037748cd8SNickeau            }
59137748cd8SNickeau        }
59237748cd8SNickeau    }
59337748cd8SNickeau
59437748cd8SNickeau    public function isPluginCall()
59537748cd8SNickeau    {
59637748cd8SNickeau        return $this->call[0] === "plugin";
59737748cd8SNickeau    }
59837748cd8SNickeau
59937748cd8SNickeau    /**
60037748cd8SNickeau     * @return mixed|string the position (ie key) in the array
60137748cd8SNickeau     */
60237748cd8SNickeau    public function getKey()
60337748cd8SNickeau    {
60437748cd8SNickeau        return $this->key;
60537748cd8SNickeau    }
60637748cd8SNickeau
60737748cd8SNickeau    public function &getCall()
60837748cd8SNickeau    {
60937748cd8SNickeau        return $this->call;
61037748cd8SNickeau    }
61137748cd8SNickeau
61237748cd8SNickeau    public function setState($state)
61337748cd8SNickeau    {
61437748cd8SNickeau        if ($this->call[0] == "plugin") {
61537748cd8SNickeau            // for dokuwiki
61637748cd8SNickeau            $this->call[1][2] = $state;
61737748cd8SNickeau            // for the combo plugin if any
61837748cd8SNickeau            if (isset($this->call[1][1][PluginUtility::STATE])) {
61937748cd8SNickeau                $this->call[1][1][PluginUtility::STATE] = $state;
62037748cd8SNickeau            }
62137748cd8SNickeau        } else {
62237748cd8SNickeau            LogUtility::msg("This modification of state is not yet supported for a native call");
62337748cd8SNickeau        }
62437748cd8SNickeau    }
62537748cd8SNickeau
62637748cd8SNickeau
62737748cd8SNickeau    /**
62837748cd8SNickeau     * Return the position of the first matched character in the text file
62937748cd8SNickeau     * @return mixed
63037748cd8SNickeau     */
63137748cd8SNickeau    public function getFirstMatchedCharacterPosition()
63237748cd8SNickeau    {
63337748cd8SNickeau
63437748cd8SNickeau        return $this->call[2];
63537748cd8SNickeau
63637748cd8SNickeau    }
63737748cd8SNickeau
63837748cd8SNickeau    /**
63937748cd8SNickeau     * Return the position of the last matched character in the text file
64037748cd8SNickeau     *
64137748cd8SNickeau     * This is the {@link Call::getFirstMatchedCharacterPosition()}
64237748cd8SNickeau     * plus the length of the {@link Call::getCapturedContent()}
64337748cd8SNickeau     * matched content
64437748cd8SNickeau     * @return int|mixed
64537748cd8SNickeau     */
64637748cd8SNickeau    public function getLastMatchedCharacterPosition()
64737748cd8SNickeau    {
64837748cd8SNickeau        return $this->getFirstMatchedCharacterPosition() + strlen($this->getCapturedContent());
64937748cd8SNickeau    }
65037748cd8SNickeau
65137748cd8SNickeau    /**
65237748cd8SNickeau     * @param $value string the class string to add
65337748cd8SNickeau     * @return Call
65437748cd8SNickeau     */
65537748cd8SNickeau    public function addClassName($value)
65637748cd8SNickeau    {
65737748cd8SNickeau        $class = $this->getAttribute("class");
65837748cd8SNickeau        if ($class != null) {
65937748cd8SNickeau            $value = "$class $value";
66037748cd8SNickeau        }
66137748cd8SNickeau        $this->addAttribute("class", $value);
66237748cd8SNickeau        return $this;
66337748cd8SNickeau
66437748cd8SNickeau    }
66537748cd8SNickeau
66637748cd8SNickeau    /**
66737748cd8SNickeau     * @param $key
66837748cd8SNickeau     * @return mixed|null - the delete value of null if not found
66937748cd8SNickeau     */
67037748cd8SNickeau    public function removeAttribute($key)
67137748cd8SNickeau    {
67237748cd8SNickeau
67337748cd8SNickeau        $data = &$this->getPluginData();
67437748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES][$key])) {
67537748cd8SNickeau            $value = $data[PluginUtility::ATTRIBUTES][$key];
67637748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES][$key]);
67737748cd8SNickeau            return $value;
67837748cd8SNickeau        } else {
67937748cd8SNickeau            // boolean attribute as first attribute
68037748cd8SNickeau            if ($this->getType() == $key) {
68137748cd8SNickeau                unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]);
68237748cd8SNickeau                return true;
68337748cd8SNickeau            }
68437748cd8SNickeau            return null;
68537748cd8SNickeau        }
68637748cd8SNickeau
68737748cd8SNickeau    }
68837748cd8SNickeau
68937748cd8SNickeau    public function setPayload($text)
69037748cd8SNickeau    {
69137748cd8SNickeau        if ($this->isPluginCall()) {
69237748cd8SNickeau            $this->call[1][1][PluginUtility::PAYLOAD] = $text;
69337748cd8SNickeau        } else {
69437748cd8SNickeau            LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented");
69537748cd8SNickeau        }
69637748cd8SNickeau    }
69737748cd8SNickeau
69837748cd8SNickeau    /**
69937748cd8SNickeau     * @return bool true if the call is a text call (same as dom text node)
70037748cd8SNickeau     */
70137748cd8SNickeau    public function isTextCall()
70237748cd8SNickeau    {
70337748cd8SNickeau        return (
70437748cd8SNickeau            $this->getState() == DOKU_LEXER_UNMATCHED ||
70537748cd8SNickeau            $this->getTagName() == "cdata" ||
70637748cd8SNickeau            $this->getTagName() == "acronym"
70737748cd8SNickeau        );
70837748cd8SNickeau    }
70937748cd8SNickeau
71037748cd8SNickeau    public function setType($type)
71137748cd8SNickeau    {
71237748cd8SNickeau        if ($this->isPluginCall()) {
71337748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type;
71437748cd8SNickeau        } else {
71537748cd8SNickeau            LogUtility::msg("This is not a plugin call ($this), you can't set the type");
71637748cd8SNickeau        }
71737748cd8SNickeau    }
71837748cd8SNickeau
71937748cd8SNickeau    public function addCssStyle($key, $value)
72037748cd8SNickeau    {
72137748cd8SNickeau        $style = $this->getAttribute("style");
72237748cd8SNickeau        $cssValue = "$key:$value";
72337748cd8SNickeau        if ($style != null) {
72437748cd8SNickeau            $cssValue = "$style; $cssValue";
72537748cd8SNickeau        }
72637748cd8SNickeau        $this->addAttribute("style", $cssValue);
72737748cd8SNickeau    }
72837748cd8SNickeau
72937748cd8SNickeau    public function setSyntaxComponentFromTag($tag)
73037748cd8SNickeau    {
73137748cd8SNickeau
73237748cd8SNickeau        if ($this->isPluginCall()) {
73337748cd8SNickeau            $this->call[1][0] = PluginUtility::getComponentName($tag);
73437748cd8SNickeau        } else {
73537748cd8SNickeau            LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)");
73637748cd8SNickeau        }
73737748cd8SNickeau    }
73837748cd8SNickeau
73937748cd8SNickeau    /**
74037748cd8SNickeau     * @param Page $page
74137748cd8SNickeau     * @return Call
74237748cd8SNickeau     */
74337748cd8SNickeau    public function render(Page $page)
74437748cd8SNickeau    {
74537748cd8SNickeau        return $this->renderFromData(TemplateUtility::getMetadataDataFromPage($page));
74637748cd8SNickeau    }
74737748cd8SNickeau
748*1fa8c418SNickeau    public function renderFromData(array $array): Call
74937748cd8SNickeau    {
750*1fa8c418SNickeau
751*1fa8c418SNickeau        /**
752*1fa8c418SNickeau         * Render all attributes
753*1fa8c418SNickeau         */
754*1fa8c418SNickeau        $attributes = $this->getAttributes();
755*1fa8c418SNickeau        if ($attributes !== null) {
756*1fa8c418SNickeau            foreach ($attributes as $key => $value) {
757*1fa8c418SNickeau                if (is_string($value)) {
758*1fa8c418SNickeau                    $this->addAttribute($key, TemplateUtility::renderStringTemplateFromDataArray($value, $array));
759*1fa8c418SNickeau                }
760*1fa8c418SNickeau            }
761*1fa8c418SNickeau        }
762*1fa8c418SNickeau
763*1fa8c418SNickeau        /**
764*1fa8c418SNickeau         * Content rendering
765*1fa8c418SNickeau         */
76637748cd8SNickeau        $state = $this->getState();
76737748cd8SNickeau        if ($state == DOKU_LEXER_UNMATCHED) {
76837748cd8SNickeau            if ($this->isPluginCall()) {
76937748cd8SNickeau                $payload = $this->getPayload();
77037748cd8SNickeau                if (!empty($payload)) {
77137748cd8SNickeau                    $this->setPayload(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
77237748cd8SNickeau                }
77337748cd8SNickeau            }
77437748cd8SNickeau        } else {
77537748cd8SNickeau            $tagName = $this->getTagName();
77637748cd8SNickeau            switch ($tagName) {
77737748cd8SNickeau                case "eol":
77837748cd8SNickeau                    break;
77937748cd8SNickeau                case "cdata":
78037748cd8SNickeau                    $payload = $this->getCapturedContent();
78137748cd8SNickeau                    $this->setCapturedContent(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
78237748cd8SNickeau                    break;
78337748cd8SNickeau                case \syntax_plugin_combo_pipeline::TAG:
78437748cd8SNickeau                    $pageTemplate = PluginUtility::getTagContent($this->getCapturedContent());
78537748cd8SNickeau                    $script = TemplateUtility::renderStringTemplateFromDataArray($pageTemplate, $array);
78637748cd8SNickeau                    $string = PipelineUtility::execute($script);
78737748cd8SNickeau                    $this->setPayload($string);
78837748cd8SNickeau                    break;
78937748cd8SNickeau            }
79037748cd8SNickeau        }
791*1fa8c418SNickeau
79237748cd8SNickeau        return $this;
79337748cd8SNickeau    }
79437748cd8SNickeau
79537748cd8SNickeau    public function setCapturedContent($content)
79637748cd8SNickeau    {
79737748cd8SNickeau        $tagName = $this->getTagName();
79837748cd8SNickeau        switch ($tagName) {
79937748cd8SNickeau            case "cdata":
80037748cd8SNickeau                $this->call[1][0] = $content;
80137748cd8SNickeau                break;
80237748cd8SNickeau            default:
80337748cd8SNickeau                LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR);
80437748cd8SNickeau        }
80537748cd8SNickeau    }
80637748cd8SNickeau
807*1fa8c418SNickeau    /**
808*1fa8c418SNickeau     * Set the display to block or inline
809*1fa8c418SNickeau     * One of `block` or `inline`
810*1fa8c418SNickeau     */
811*1fa8c418SNickeau    public function setDisplay($display): Call
812*1fa8c418SNickeau    {
813*1fa8c418SNickeau        $mode = $this->getMode();
814*1fa8c418SNickeau        if ($mode == "plugin") {
815*1fa8c418SNickeau            $this->call[1][1][PluginUtility::DISPLAY] = $display;
816*1fa8c418SNickeau        } else {
817*1fa8c418SNickeau            LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING);
818*1fa8c418SNickeau        }
819*1fa8c418SNickeau        return $this;
820*1fa8c418SNickeau
821*1fa8c418SNickeau    }
822*1fa8c418SNickeau
823*1fa8c418SNickeau    /**
824*1fa8c418SNickeau     * The plugin or not
825*1fa8c418SNickeau     * @return mixed
826*1fa8c418SNickeau     */
827*1fa8c418SNickeau    private function getMode()
828*1fa8c418SNickeau    {
829*1fa8c418SNickeau        return $this->call[0];
830*1fa8c418SNickeau    }
831*1fa8c418SNickeau
832*1fa8c418SNickeau    /**
833*1fa8c418SNickeau     * Return if this an unmatched call with space
834*1fa8c418SNickeau     * in captured content
835*1fa8c418SNickeau     * @return bool
836*1fa8c418SNickeau     */
837*1fa8c418SNickeau    public function isUnMatchedEmptyCall(): bool
838*1fa8c418SNickeau    {
839*1fa8c418SNickeau        if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") {
840*1fa8c418SNickeau            return true;
841*1fa8c418SNickeau        }
842*1fa8c418SNickeau        return false;
843*1fa8c418SNickeau    }
844*1fa8c418SNickeau
84537748cd8SNickeau
84637748cd8SNickeau}
847