xref: /plugin/combo/ComboStrap/Call.php (revision e4026cd1b0c4088a2208afc8f3dd143c48923c0a)
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;
161fa8c418SNickeauuse syntax_plugin_combo_media;
171fa8c418SNickeauuse 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",
61*e4026cd1Sgerardnico        "underline_close",
6237748cd8SNickeau        "monospace",
6337748cd8SNickeau        "subscript",
6437748cd8SNickeau        "superscript",
6537748cd8SNickeau        "deleted",
6637748cd8SNickeau        "footnote",
6737748cd8SNickeau        /**
6837748cd8SNickeau         * Others
6937748cd8SNickeau         */
701fa8c418SNickeau        "acronym", // abbr
7137748cd8SNickeau        "strong_close",
7237748cd8SNickeau        "strong_open",
7337748cd8SNickeau        "monospace_open",
7437748cd8SNickeau        "monospace_close",
7537748cd8SNickeau        "doublequoteopening", // ie the character " in "The"
7637748cd8SNickeau        "entity", // for instance `...` are transformed in character
7737748cd8SNickeau        "linebreak",
7837748cd8SNickeau        "externallink",
7937748cd8SNickeau        "internallink",
8037748cd8SNickeau        MediaLink::INTERNAL_MEDIA_CALL_NAME,
8137748cd8SNickeau        MediaLink::EXTERNAL_MEDIA_CALL_NAME,
8237748cd8SNickeau        /**
8337748cd8SNickeau         * The inline of combo
8437748cd8SNickeau         */
8537748cd8SNickeau        \syntax_plugin_combo_link::TAG,
8637748cd8SNickeau        \syntax_plugin_combo_icon::TAG,
8737748cd8SNickeau        \syntax_plugin_combo_inote::TAG,
8837748cd8SNickeau        \syntax_plugin_combo_button::TAG,
8937748cd8SNickeau        \syntax_plugin_combo_tooltip::TAG,
9037748cd8SNickeau        \syntax_plugin_combo_pipeline::TAG,
9137748cd8SNickeau    );
9237748cd8SNickeau
9337748cd8SNickeau
9437748cd8SNickeau    const BLOCK_MARKUP_DOKUWIKI_COMPONENTS = array(
9537748cd8SNickeau        "listu_open", // ul
9637748cd8SNickeau        "listu_close",
9737748cd8SNickeau        "listitem_open", //li
9837748cd8SNickeau        "listitem_close",
9937748cd8SNickeau        "listcontent_open", // after li ???
10037748cd8SNickeau        "listcontent_close",
10137748cd8SNickeau        "table_open",
10237748cd8SNickeau        "table_close",
10337748cd8SNickeau    );
10437748cd8SNickeau
1051fa8c418SNickeau    /**
1061fa8c418SNickeau     * A media is not really an image
1071fa8c418SNickeau     * but it may contains one
1081fa8c418SNickeau     */
1091fa8c418SNickeau    const IMAGE_TAGS = [
1101fa8c418SNickeau        syntax_plugin_combo_media::TAG,
1111fa8c418SNickeau        syntax_plugin_combo_pageimage::TAG
1121fa8c418SNickeau    ];
1131fa8c418SNickeau
11437748cd8SNickeau    private $call;
11537748cd8SNickeau
11637748cd8SNickeau    /**
11737748cd8SNickeau     * The key identifier in the {@link CallStack}
11837748cd8SNickeau     * @var mixed|string
11937748cd8SNickeau     */
12037748cd8SNickeau    private $key;
12137748cd8SNickeau
12237748cd8SNickeau    /**
12337748cd8SNickeau     * Call constructor.
12437748cd8SNickeau     * @param $call - the instruction array (ie called a call)
12537748cd8SNickeau     */
12637748cd8SNickeau    public function __construct(&$call, $key = "")
12737748cd8SNickeau    {
12837748cd8SNickeau        $this->call = &$call;
12937748cd8SNickeau        $this->key = $key;
13037748cd8SNickeau    }
13137748cd8SNickeau
13237748cd8SNickeau    /**
13337748cd8SNickeau     * Insert a tag above
13437748cd8SNickeau     * @param $tagName
13537748cd8SNickeau     * @param $state
136c3437056SNickeau     * @param array $attribute
137c3437056SNickeau     * @param string|null $context
138c3437056SNickeau     * @param string $content - the parsed content
139c3437056SNickeau     * @param string|null $payload - the payload after handler
140c3437056SNickeau     * @param int|null $position
14137748cd8SNickeau     * @return Call - a call
14237748cd8SNickeau     */
143c3437056SNickeau    public static function createComboCall($tagName, $state, array $attribute = array(), string $context = null, string $content = null, string $payload = null, int $position= null): Call
14437748cd8SNickeau    {
14537748cd8SNickeau        $data = array(
14637748cd8SNickeau            PluginUtility::ATTRIBUTES => $attribute,
14737748cd8SNickeau            PluginUtility::CONTEXT => $context,
148c3437056SNickeau            PluginUtility::STATE => $state,
149c3437056SNickeau            PluginUtility::POSITION => $position
15037748cd8SNickeau        );
15137748cd8SNickeau        if ($payload != null) {
15237748cd8SNickeau            $data[PluginUtility::PAYLOAD] = $payload;
15337748cd8SNickeau        }
154c3437056SNickeau        $positionInText = $position;
15537748cd8SNickeau
15637748cd8SNickeau        $call = [
15737748cd8SNickeau            "plugin",
15837748cd8SNickeau            array(
15937748cd8SNickeau                PluginUtility::getComponentName($tagName),
16037748cd8SNickeau                $data,
16137748cd8SNickeau                $state,
16237748cd8SNickeau                $content
16337748cd8SNickeau            ),
16437748cd8SNickeau            $positionInText
16537748cd8SNickeau        ];
16637748cd8SNickeau        return new Call($call);
16737748cd8SNickeau    }
16837748cd8SNickeau
16937748cd8SNickeau    /**
17037748cd8SNickeau     * Insert a dokuwiki call
17137748cd8SNickeau     * @param $callName
17237748cd8SNickeau     * @param $array
17337748cd8SNickeau     * @param $positionInText
17437748cd8SNickeau     * @return Call
17537748cd8SNickeau     */
17637748cd8SNickeau    public static function createNativeCall($callName, $array = [], $positionInText = null)
17737748cd8SNickeau    {
17837748cd8SNickeau        $call = [
17937748cd8SNickeau            $callName,
18037748cd8SNickeau            $array,
18137748cd8SNickeau            $positionInText
18237748cd8SNickeau        ];
18337748cd8SNickeau        return new Call($call);
18437748cd8SNickeau    }
18537748cd8SNickeau
18637748cd8SNickeau    public static function createFromInstruction($instruction)
18737748cd8SNickeau    {
18837748cd8SNickeau        return new Call($instruction);
18937748cd8SNickeau    }
19037748cd8SNickeau
19137748cd8SNickeau
19237748cd8SNickeau    /**
19337748cd8SNickeau     *
19437748cd8SNickeau     * Return the tag name from a call array
19537748cd8SNickeau     *
19637748cd8SNickeau     * This is not the logical tag.
19737748cd8SNickeau     * This is much more what's called:
19837748cd8SNickeau     *   * the component name for a plugin
19937748cd8SNickeau     *   * or the handler name for dokuwiki
20037748cd8SNickeau     *
20137748cd8SNickeau     * For a plugin, this is equivalent
20237748cd8SNickeau     * to the {@link SyntaxPlugin::getPluginComponent()}
20337748cd8SNickeau     *
20437748cd8SNickeau     * This is not the fully qualified component name:
20537748cd8SNickeau     *   * with the plugin as prefix such as in {@link Call::getComponentName()}
20637748cd8SNickeau     *   * or with the `open` and `close` prefix such as `p_close` ...
20737748cd8SNickeau     *
20837748cd8SNickeau     * @return mixed|string
20937748cd8SNickeau     */
21037748cd8SNickeau    public function getTagName()
21137748cd8SNickeau    {
21237748cd8SNickeau        $mode = $this->call[0];
21337748cd8SNickeau        if ($mode != "plugin") {
21437748cd8SNickeau
21537748cd8SNickeau            /**
21637748cd8SNickeau             * This is a standard dokuwiki node
21737748cd8SNickeau             */
21837748cd8SNickeau            $dokuWikiNodeName = $this->call[0];
21937748cd8SNickeau
22037748cd8SNickeau            /**
22137748cd8SNickeau             * The dokwuiki node name has also the open and close notion
22237748cd8SNickeau             * We delete this is not in the doc and therefore not logical
22337748cd8SNickeau             */
22437748cd8SNickeau            $tagName = str_replace("_close", "", $dokuWikiNodeName);
22537748cd8SNickeau            $tagName = str_replace("_open", "", $tagName);
22637748cd8SNickeau
22737748cd8SNickeau        } else {
22837748cd8SNickeau
22937748cd8SNickeau            /**
23037748cd8SNickeau             * This is a plugin node
23137748cd8SNickeau             */
23237748cd8SNickeau            $pluginDokuData = $this->call[1];
23337748cd8SNickeau            $component = $pluginDokuData[0];
23437748cd8SNickeau            if (!is_array($component)) {
23537748cd8SNickeau                /**
23637748cd8SNickeau                 * Tag name from class
23737748cd8SNickeau                 */
23837748cd8SNickeau                $componentNames = explode("_", $component);
23937748cd8SNickeau                /**
24037748cd8SNickeau                 * To take care of
24137748cd8SNickeau                 * PHP Warning:  sizeof(): Parameter must be an array or an object that implements Countable
24237748cd8SNickeau                 * in lib/plugins/combo/class/Tag.php on line 314
24337748cd8SNickeau                 */
24437748cd8SNickeau                if (is_array($componentNames)) {
24537748cd8SNickeau                    $tagName = $componentNames[sizeof($componentNames) - 1];
24637748cd8SNickeau                } else {
24737748cd8SNickeau                    $tagName = $component;
24837748cd8SNickeau                }
24937748cd8SNickeau            } else {
25037748cd8SNickeau                // To resolve: explode() expects parameter 2 to be string, array given
251c3437056SNickeau                LogUtility::msg("The call (" . print_r($this->call, true) . ") has an array and not a string as component (" . print_r($component, true) . "). Page: " . Page::createPageFromRequestedPage(), LogUtility::LVL_MSG_ERROR);
25237748cd8SNickeau                $tagName = "";
25337748cd8SNickeau            }
25437748cd8SNickeau
25537748cd8SNickeau
25637748cd8SNickeau        }
25737748cd8SNickeau        return $tagName;
25837748cd8SNickeau
25937748cd8SNickeau    }
26037748cd8SNickeau
26137748cd8SNickeau
26237748cd8SNickeau    /**
26337748cd8SNickeau     * The parser state
26437748cd8SNickeau     * @return mixed
26537748cd8SNickeau     * May be null (example eol, internallink, ...)
26637748cd8SNickeau     */
26737748cd8SNickeau    public function getState()
26837748cd8SNickeau    {
26937748cd8SNickeau        $mode = $this->call[0];
27037748cd8SNickeau        if ($mode != "plugin") {
27137748cd8SNickeau
27237748cd8SNickeau            /**
27337748cd8SNickeau             * There is no state because this is a standard
27437748cd8SNickeau             * dokuwiki syntax found in {@link \Doku_Renderer_xhtml}
27537748cd8SNickeau             * check if this is not a `...._close` or `...._open`
27637748cd8SNickeau             * to derive the state
27737748cd8SNickeau             */
27837748cd8SNickeau            $mode = $this->call[0];
27937748cd8SNickeau            $lastPositionSepName = strrpos($mode, "_");
28037748cd8SNickeau            $closeOrOpen = substr($mode, $lastPositionSepName + 1);
28137748cd8SNickeau            switch ($closeOrOpen) {
28237748cd8SNickeau                case "open":
28337748cd8SNickeau                    return DOKU_LEXER_ENTER;
28437748cd8SNickeau                case "close":
28537748cd8SNickeau                    return DOKU_LEXER_EXIT;
28637748cd8SNickeau                default:
28737748cd8SNickeau                    return null;
28837748cd8SNickeau            }
28937748cd8SNickeau
29037748cd8SNickeau        } else {
29137748cd8SNickeau            // Plugin
29237748cd8SNickeau            $returnedArray = $this->call[1];
29337748cd8SNickeau            if (array_key_exists(2, $returnedArray)) {
29437748cd8SNickeau                return $returnedArray[2];
29537748cd8SNickeau            } else {
29637748cd8SNickeau                return null;
29737748cd8SNickeau            }
29837748cd8SNickeau        }
29937748cd8SNickeau    }
30037748cd8SNickeau
30137748cd8SNickeau    /**
30237748cd8SNickeau     * @return mixed the data returned from the {@link DokuWiki_Syntax_Plugin::handle} (ie attributes, payload, ...)
30337748cd8SNickeau     */
30437748cd8SNickeau    public function &getPluginData()
30537748cd8SNickeau    {
30637748cd8SNickeau        return $this->call[1][1];
30737748cd8SNickeau    }
30837748cd8SNickeau
30937748cd8SNickeau    /**
31037748cd8SNickeau     * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle}
31137748cd8SNickeau     */
31237748cd8SNickeau    public function getCapturedContent()
31337748cd8SNickeau    {
31437748cd8SNickeau        $caller = $this->call[0];
31537748cd8SNickeau        switch ($caller) {
31637748cd8SNickeau            case "plugin":
31737748cd8SNickeau                return $this->call[1][3];
31837748cd8SNickeau            case "internallink":
31937748cd8SNickeau                return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]';
32037748cd8SNickeau            case "eol":
32137748cd8SNickeau                return DOKU_LF;
32237748cd8SNickeau            case "header":
32337748cd8SNickeau            case "cdata":
32437748cd8SNickeau                return $this->call[1][0];
32537748cd8SNickeau            default:
32637748cd8SNickeau                if (isset($this->call[1][0]) && is_string($this->call[1][0])) {
32737748cd8SNickeau                    return $this->call[1][0];
32837748cd8SNickeau                } else {
32937748cd8SNickeau                    return "";
33037748cd8SNickeau                }
33137748cd8SNickeau        }
33237748cd8SNickeau    }
33337748cd8SNickeau
33437748cd8SNickeau
33537748cd8SNickeau    public function getAttributes()
33637748cd8SNickeau    {
33737748cd8SNickeau
33837748cd8SNickeau        $tagName = $this->getTagName();
33937748cd8SNickeau        switch ($tagName) {
34037748cd8SNickeau            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
34137748cd8SNickeau                return $this->call[1];
34237748cd8SNickeau            default:
34337748cd8SNickeau                $data = $this->getPluginData();
34437748cd8SNickeau                if (isset($data[PluginUtility::ATTRIBUTES])) {
34537748cd8SNickeau                    return $data[PluginUtility::ATTRIBUTES];
34637748cd8SNickeau                } else {
34737748cd8SNickeau                    return null;
34837748cd8SNickeau                }
34937748cd8SNickeau        }
35037748cd8SNickeau    }
35137748cd8SNickeau
35237748cd8SNickeau    public function removeAttributes()
35337748cd8SNickeau    {
35437748cd8SNickeau
35537748cd8SNickeau        $data = &$this->getPluginData();
35637748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES])) {
35737748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES]);
35837748cd8SNickeau        }
35937748cd8SNickeau
36037748cd8SNickeau    }
36137748cd8SNickeau
36237748cd8SNickeau    public function updateToPluginComponent($component, $state, $attributes = array())
36337748cd8SNickeau    {
36437748cd8SNickeau        if ($this->call[0] == "plugin") {
36537748cd8SNickeau            $match = $this->call[1][3];
36637748cd8SNickeau        } else {
36737748cd8SNickeau            $this->call[0] = "plugin";
36837748cd8SNickeau            $match = "";
36937748cd8SNickeau        }
37037748cd8SNickeau        $this->call[1] = array(
37137748cd8SNickeau            0 => $component,
37237748cd8SNickeau            1 => array(
37337748cd8SNickeau                PluginUtility::ATTRIBUTES => $attributes,
37437748cd8SNickeau                PluginUtility::STATE => $state,
37537748cd8SNickeau            ),
37637748cd8SNickeau            2 => $state,
37737748cd8SNickeau            3 => $match
37837748cd8SNickeau        );
37937748cd8SNickeau
38037748cd8SNickeau    }
38137748cd8SNickeau
3821fa8c418SNickeau    /**
3831fa8c418SNickeau     * Does the display has been set
3841fa8c418SNickeau     * to override the dokuwiki default
3851fa8c418SNickeau     * ({@link Syntax::getPType()}
3861fa8c418SNickeau     *
3871fa8c418SNickeau     * because an image is by default a inline component
3881fa8c418SNickeau     * but can be a block (ie top image of a card)
3891fa8c418SNickeau     * @return bool
3901fa8c418SNickeau     */
3911fa8c418SNickeau    public function isDisplaySet(): bool
3921fa8c418SNickeau    {
3931fa8c418SNickeau        return isset($this->call[1][1][PluginUtility::DISPLAY]);
3941fa8c418SNickeau    }
3951fa8c418SNickeau
39637748cd8SNickeau    public function getDisplay()
39737748cd8SNickeau    {
3981fa8c418SNickeau        $mode = $this->getMode();
3991fa8c418SNickeau        if ($mode == "plugin") {
4001fa8c418SNickeau            if ($this->isDisplaySet()) {
4011fa8c418SNickeau                return $this->call[1][1][PluginUtility::DISPLAY];
4021fa8c418SNickeau            }
4031fa8c418SNickeau        }
4041fa8c418SNickeau
40537748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
40637748cd8SNickeau            /**
40737748cd8SNickeau             * Unmatched are content (ie text node in XML/HTML) and have
40837748cd8SNickeau             * no display
40937748cd8SNickeau             */
41037748cd8SNickeau            return Call::INLINE_DISPLAY;
41137748cd8SNickeau        } else {
41237748cd8SNickeau            $mode = $this->call[0];
41337748cd8SNickeau            if ($mode == "plugin") {
41437748cd8SNickeau                global $DOKU_PLUGINS;
41537748cd8SNickeau                $component = $this->getComponentName();
41637748cd8SNickeau                /**
41737748cd8SNickeau                 * @var SyntaxPlugin $syntaxPlugin
41837748cd8SNickeau                 */
41937748cd8SNickeau                $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component];
42037748cd8SNickeau                $pType = $syntaxPlugin->getPType();
42137748cd8SNickeau                switch ($pType) {
42237748cd8SNickeau                    case "normal":
42337748cd8SNickeau                        return Call::INLINE_DISPLAY;
42437748cd8SNickeau                    case "block":
42537748cd8SNickeau                    case "stack":
42637748cd8SNickeau                        return Call::BlOCK_DISPLAY;
42737748cd8SNickeau                    default:
42837748cd8SNickeau                        LogUtility::msg("The ptype (" . $pType . ") is unknown.");
42937748cd8SNickeau                        return null;
43037748cd8SNickeau                }
43137748cd8SNickeau            } else {
43237748cd8SNickeau                if ($mode == "eol") {
43337748cd8SNickeau                    /**
43437748cd8SNickeau                     * Control character
43537748cd8SNickeau                     * We return it as it's used in the
43637748cd8SNickeau                     * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()}
43737748cd8SNickeau                     * to create the paragraph
43837748cd8SNickeau                     * This is not a block, nor an inline
43937748cd8SNickeau                     */
44037748cd8SNickeau                    return $mode;
44137748cd8SNickeau                }
44237748cd8SNickeau
44337748cd8SNickeau                if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) {
44437748cd8SNickeau                    return Call::INLINE_DISPLAY;
44537748cd8SNickeau                }
44637748cd8SNickeau
44737748cd8SNickeau                if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) {
44837748cd8SNickeau                    return Call::BlOCK_DISPLAY;
44937748cd8SNickeau                }
45037748cd8SNickeau
45137748cd8SNickeau                LogUtility::msg("The display of the call with the mode " . $mode . " is unknown");
45237748cd8SNickeau                return null;
45337748cd8SNickeau
45437748cd8SNickeau
45537748cd8SNickeau            }
45637748cd8SNickeau        }
45737748cd8SNickeau
45837748cd8SNickeau    }
45937748cd8SNickeau
46037748cd8SNickeau    /**
46137748cd8SNickeau     * Same as {@link Call::getTagName()}
46237748cd8SNickeau     * but fully qualified
46337748cd8SNickeau     * @return string
46437748cd8SNickeau     */
46537748cd8SNickeau    public function getComponentName()
46637748cd8SNickeau    {
46737748cd8SNickeau        $mode = $this->call[0];
46837748cd8SNickeau        if ($mode == "plugin") {
46937748cd8SNickeau            $pluginDokuData = $this->call[1];
47037748cd8SNickeau            return $pluginDokuData[0];
47137748cd8SNickeau        } else {
47237748cd8SNickeau            return $mode;
47337748cd8SNickeau        }
47437748cd8SNickeau    }
47537748cd8SNickeau
47637748cd8SNickeau    public function updateEolToSpace()
47737748cd8SNickeau    {
47837748cd8SNickeau        $mode = $this->call[0];
47937748cd8SNickeau        if ($mode != "eol") {
48037748cd8SNickeau            LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support");
48137748cd8SNickeau        } else {
48237748cd8SNickeau            $this->call[0] = "cdata";
48337748cd8SNickeau            $this->call[1] = array(
48437748cd8SNickeau                0 => " "
48537748cd8SNickeau            );
48637748cd8SNickeau        }
48737748cd8SNickeau
48837748cd8SNickeau    }
48937748cd8SNickeau
49037748cd8SNickeau    public function addAttribute($key, $value)
49137748cd8SNickeau    {
49237748cd8SNickeau        $mode = $this->call[0];
49337748cd8SNickeau        if ($mode == "plugin") {
49437748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value;
49537748cd8SNickeau        } else {
49637748cd8SNickeau            LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
49737748cd8SNickeau        }
49837748cd8SNickeau    }
49937748cd8SNickeau
50037748cd8SNickeau    public function getContext()
50137748cd8SNickeau    {
50237748cd8SNickeau        $mode = $this->call[0];
50337748cd8SNickeau        if ($mode == "plugin") {
50437748cd8SNickeau            return $this->call[1][1][PluginUtility::CONTEXT];
50537748cd8SNickeau        } else {
50637748cd8SNickeau            LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
50737748cd8SNickeau            return null;
50837748cd8SNickeau        }
50937748cd8SNickeau    }
51037748cd8SNickeau
51137748cd8SNickeau    /**
51237748cd8SNickeau     *
51337748cd8SNickeau     * @return array
51437748cd8SNickeau     */
51537748cd8SNickeau    public function toCallArray()
51637748cd8SNickeau    {
51737748cd8SNickeau        return $this->call;
51837748cd8SNickeau    }
51937748cd8SNickeau
52037748cd8SNickeau    public function __toString()
52137748cd8SNickeau    {
52237748cd8SNickeau        $name = $this->key;
52337748cd8SNickeau        if (!empty($name)) {
52437748cd8SNickeau            $name .= " - ";
52537748cd8SNickeau        }
52637748cd8SNickeau        $name .= $this->getTagName();
52737748cd8SNickeau        return $name;
52837748cd8SNickeau    }
52937748cd8SNickeau
53037748cd8SNickeau    public function getType()
53137748cd8SNickeau    {
53237748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
53337748cd8SNickeau            return null;
53437748cd8SNickeau        } else {
53537748cd8SNickeau            /**
53637748cd8SNickeau             * don't use {@link Call::getAttribute()} to get the type
53737748cd8SNickeau             * as this function stack also depends on
53837748cd8SNickeau             * this function {@link Call::getType()}
53937748cd8SNickeau             * to return the value
54037748cd8SNickeau             * Ie: if this is a boolean attribute without specified type
54137748cd8SNickeau             * if the boolean value is in the type, we return it
54237748cd8SNickeau             */
54337748cd8SNickeau            return $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY];
54437748cd8SNickeau        }
54537748cd8SNickeau    }
54637748cd8SNickeau
54737748cd8SNickeau    /**
54837748cd8SNickeau     * @param $key
54937748cd8SNickeau     * @param null $default
55037748cd8SNickeau     * @return string|null
55137748cd8SNickeau     */
55237748cd8SNickeau    public function getAttribute($key, $default = null)
55337748cd8SNickeau    {
55437748cd8SNickeau        $attributes = $this->getAttributes();
55537748cd8SNickeau        if (isset($attributes[$key])) {
55637748cd8SNickeau            return $attributes[$key];
55737748cd8SNickeau        } else {
55837748cd8SNickeau            // boolean attribute
55937748cd8SNickeau            if ($this->getType() == $key) {
56037748cd8SNickeau                return true;
56137748cd8SNickeau            } else {
56237748cd8SNickeau                return $default;
56337748cd8SNickeau            }
56437748cd8SNickeau        }
56537748cd8SNickeau    }
56637748cd8SNickeau
56737748cd8SNickeau    public function getPayload()
56837748cd8SNickeau    {
56937748cd8SNickeau        $mode = $this->call[0];
57037748cd8SNickeau        if ($mode == "plugin") {
57137748cd8SNickeau            return $this->call[1][1][PluginUtility::PAYLOAD];
57237748cd8SNickeau        } else {
57337748cd8SNickeau            LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support");
57437748cd8SNickeau            return null;
57537748cd8SNickeau        }
57637748cd8SNickeau    }
57737748cd8SNickeau
57837748cd8SNickeau    public function setContext($value)
57937748cd8SNickeau    {
58037748cd8SNickeau        $this->call[1][1][PluginUtility::CONTEXT] = $value;
58137748cd8SNickeau        return $this;
58237748cd8SNickeau    }
58337748cd8SNickeau
58437748cd8SNickeau    public function hasAttribute($attributeName)
58537748cd8SNickeau    {
58637748cd8SNickeau        $attributes = $this->getAttributes();
58737748cd8SNickeau        if (isset($attributes[$attributeName])) {
58837748cd8SNickeau            return true;
58937748cd8SNickeau        } else {
59037748cd8SNickeau            if ($this->getType() == $attributeName) {
59137748cd8SNickeau                return true;
59237748cd8SNickeau            } else {
59337748cd8SNickeau                return false;
59437748cd8SNickeau            }
59537748cd8SNickeau        }
59637748cd8SNickeau    }
59737748cd8SNickeau
59837748cd8SNickeau    public function isPluginCall()
59937748cd8SNickeau    {
60037748cd8SNickeau        return $this->call[0] === "plugin";
60137748cd8SNickeau    }
60237748cd8SNickeau
60337748cd8SNickeau    /**
60437748cd8SNickeau     * @return mixed|string the position (ie key) in the array
60537748cd8SNickeau     */
60637748cd8SNickeau    public function getKey()
60737748cd8SNickeau    {
60837748cd8SNickeau        return $this->key;
60937748cd8SNickeau    }
61037748cd8SNickeau
61137748cd8SNickeau    public function &getCall()
61237748cd8SNickeau    {
61337748cd8SNickeau        return $this->call;
61437748cd8SNickeau    }
61537748cd8SNickeau
61637748cd8SNickeau    public function setState($state)
61737748cd8SNickeau    {
61837748cd8SNickeau        if ($this->call[0] == "plugin") {
61937748cd8SNickeau            // for dokuwiki
62037748cd8SNickeau            $this->call[1][2] = $state;
62137748cd8SNickeau            // for the combo plugin if any
62237748cd8SNickeau            if (isset($this->call[1][1][PluginUtility::STATE])) {
62337748cd8SNickeau                $this->call[1][1][PluginUtility::STATE] = $state;
62437748cd8SNickeau            }
62537748cd8SNickeau        } else {
62637748cd8SNickeau            LogUtility::msg("This modification of state is not yet supported for a native call");
62737748cd8SNickeau        }
62837748cd8SNickeau    }
62937748cd8SNickeau
63037748cd8SNickeau
63137748cd8SNickeau    /**
63237748cd8SNickeau     * Return the position of the first matched character in the text file
63337748cd8SNickeau     * @return mixed
63437748cd8SNickeau     */
63537748cd8SNickeau    public function getFirstMatchedCharacterPosition()
63637748cd8SNickeau    {
63737748cd8SNickeau
63837748cd8SNickeau        return $this->call[2];
63937748cd8SNickeau
64037748cd8SNickeau    }
64137748cd8SNickeau
64237748cd8SNickeau    /**
64337748cd8SNickeau     * Return the position of the last matched character in the text file
64437748cd8SNickeau     *
64537748cd8SNickeau     * This is the {@link Call::getFirstMatchedCharacterPosition()}
64637748cd8SNickeau     * plus the length of the {@link Call::getCapturedContent()}
64737748cd8SNickeau     * matched content
64837748cd8SNickeau     * @return int|mixed
64937748cd8SNickeau     */
65037748cd8SNickeau    public function getLastMatchedCharacterPosition()
65137748cd8SNickeau    {
65237748cd8SNickeau        return $this->getFirstMatchedCharacterPosition() + strlen($this->getCapturedContent());
65337748cd8SNickeau    }
65437748cd8SNickeau
65537748cd8SNickeau    /**
65637748cd8SNickeau     * @param $value string the class string to add
65737748cd8SNickeau     * @return Call
65837748cd8SNickeau     */
65937748cd8SNickeau    public function addClassName($value)
66037748cd8SNickeau    {
66137748cd8SNickeau        $class = $this->getAttribute("class");
66237748cd8SNickeau        if ($class != null) {
66337748cd8SNickeau            $value = "$class $value";
66437748cd8SNickeau        }
66537748cd8SNickeau        $this->addAttribute("class", $value);
66637748cd8SNickeau        return $this;
66737748cd8SNickeau
66837748cd8SNickeau    }
66937748cd8SNickeau
67037748cd8SNickeau    /**
67137748cd8SNickeau     * @param $key
67237748cd8SNickeau     * @return mixed|null - the delete value of null if not found
67337748cd8SNickeau     */
67437748cd8SNickeau    public function removeAttribute($key)
67537748cd8SNickeau    {
67637748cd8SNickeau
67737748cd8SNickeau        $data = &$this->getPluginData();
67837748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES][$key])) {
67937748cd8SNickeau            $value = $data[PluginUtility::ATTRIBUTES][$key];
68037748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES][$key]);
68137748cd8SNickeau            return $value;
68237748cd8SNickeau        } else {
68337748cd8SNickeau            // boolean attribute as first attribute
68437748cd8SNickeau            if ($this->getType() == $key) {
68537748cd8SNickeau                unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]);
68637748cd8SNickeau                return true;
68737748cd8SNickeau            }
68837748cd8SNickeau            return null;
68937748cd8SNickeau        }
69037748cd8SNickeau
69137748cd8SNickeau    }
69237748cd8SNickeau
69337748cd8SNickeau    public function setPayload($text)
69437748cd8SNickeau    {
69537748cd8SNickeau        if ($this->isPluginCall()) {
69637748cd8SNickeau            $this->call[1][1][PluginUtility::PAYLOAD] = $text;
69737748cd8SNickeau        } else {
69837748cd8SNickeau            LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented");
69937748cd8SNickeau        }
70037748cd8SNickeau    }
70137748cd8SNickeau
70237748cd8SNickeau    /**
70337748cd8SNickeau     * @return bool true if the call is a text call (same as dom text node)
70437748cd8SNickeau     */
70537748cd8SNickeau    public function isTextCall()
70637748cd8SNickeau    {
70737748cd8SNickeau        return (
70837748cd8SNickeau            $this->getState() == DOKU_LEXER_UNMATCHED ||
70937748cd8SNickeau            $this->getTagName() == "cdata" ||
71037748cd8SNickeau            $this->getTagName() == "acronym"
71137748cd8SNickeau        );
71237748cd8SNickeau    }
71337748cd8SNickeau
71437748cd8SNickeau    public function setType($type)
71537748cd8SNickeau    {
71637748cd8SNickeau        if ($this->isPluginCall()) {
71737748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type;
71837748cd8SNickeau        } else {
71937748cd8SNickeau            LogUtility::msg("This is not a plugin call ($this), you can't set the type");
72037748cd8SNickeau        }
72137748cd8SNickeau    }
72237748cd8SNickeau
72337748cd8SNickeau    public function addCssStyle($key, $value)
72437748cd8SNickeau    {
72537748cd8SNickeau        $style = $this->getAttribute("style");
72637748cd8SNickeau        $cssValue = "$key:$value";
72737748cd8SNickeau        if ($style != null) {
72837748cd8SNickeau            $cssValue = "$style; $cssValue";
72937748cd8SNickeau        }
73037748cd8SNickeau        $this->addAttribute("style", $cssValue);
73137748cd8SNickeau    }
73237748cd8SNickeau
73337748cd8SNickeau    public function setSyntaxComponentFromTag($tag)
73437748cd8SNickeau    {
73537748cd8SNickeau
73637748cd8SNickeau        if ($this->isPluginCall()) {
73737748cd8SNickeau            $this->call[1][0] = PluginUtility::getComponentName($tag);
73837748cd8SNickeau        } else {
73937748cd8SNickeau            LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)");
74037748cd8SNickeau        }
74137748cd8SNickeau    }
74237748cd8SNickeau
74337748cd8SNickeau    /**
74437748cd8SNickeau     * @param Page $page
74537748cd8SNickeau     * @return Call
74637748cd8SNickeau     */
74737748cd8SNickeau    public function render(Page $page)
74837748cd8SNickeau    {
74937748cd8SNickeau        return $this->renderFromData(TemplateUtility::getMetadataDataFromPage($page));
75037748cd8SNickeau    }
75137748cd8SNickeau
7521fa8c418SNickeau    public function renderFromData(array $array): Call
75337748cd8SNickeau    {
7541fa8c418SNickeau
7551fa8c418SNickeau        /**
7561fa8c418SNickeau         * Render all attributes
7571fa8c418SNickeau         */
7581fa8c418SNickeau        $attributes = $this->getAttributes();
7591fa8c418SNickeau        if ($attributes !== null) {
7601fa8c418SNickeau            foreach ($attributes as $key => $value) {
7611fa8c418SNickeau                if (is_string($value)) {
7621fa8c418SNickeau                    $this->addAttribute($key, TemplateUtility::renderStringTemplateFromDataArray($value, $array));
7631fa8c418SNickeau                }
7641fa8c418SNickeau            }
7651fa8c418SNickeau        }
7661fa8c418SNickeau
7671fa8c418SNickeau        /**
7681fa8c418SNickeau         * Content rendering
7691fa8c418SNickeau         */
77037748cd8SNickeau        $state = $this->getState();
77137748cd8SNickeau        if ($state == DOKU_LEXER_UNMATCHED) {
77237748cd8SNickeau            if ($this->isPluginCall()) {
77337748cd8SNickeau                $payload = $this->getPayload();
77437748cd8SNickeau                if (!empty($payload)) {
77537748cd8SNickeau                    $this->setPayload(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
77637748cd8SNickeau                }
77737748cd8SNickeau            }
77837748cd8SNickeau        } else {
77937748cd8SNickeau            $tagName = $this->getTagName();
78037748cd8SNickeau            switch ($tagName) {
78137748cd8SNickeau                case "eol":
78237748cd8SNickeau                    break;
78337748cd8SNickeau                case "cdata":
78437748cd8SNickeau                    $payload = $this->getCapturedContent();
78537748cd8SNickeau                    $this->setCapturedContent(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
78637748cd8SNickeau                    break;
78737748cd8SNickeau                case \syntax_plugin_combo_pipeline::TAG:
78837748cd8SNickeau                    $pageTemplate = PluginUtility::getTagContent($this->getCapturedContent());
78937748cd8SNickeau                    $script = TemplateUtility::renderStringTemplateFromDataArray($pageTemplate, $array);
79037748cd8SNickeau                    $string = PipelineUtility::execute($script);
79137748cd8SNickeau                    $this->setPayload($string);
79237748cd8SNickeau                    break;
79337748cd8SNickeau            }
79437748cd8SNickeau        }
7951fa8c418SNickeau
79637748cd8SNickeau        return $this;
79737748cd8SNickeau    }
79837748cd8SNickeau
79937748cd8SNickeau    public function setCapturedContent($content)
80037748cd8SNickeau    {
80137748cd8SNickeau        $tagName = $this->getTagName();
80237748cd8SNickeau        switch ($tagName) {
80337748cd8SNickeau            case "cdata":
80437748cd8SNickeau                $this->call[1][0] = $content;
80537748cd8SNickeau                break;
80637748cd8SNickeau            default:
80737748cd8SNickeau                LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR);
80837748cd8SNickeau        }
80937748cd8SNickeau    }
81037748cd8SNickeau
8111fa8c418SNickeau    /**
8121fa8c418SNickeau     * Set the display to block or inline
8131fa8c418SNickeau     * One of `block` or `inline`
8141fa8c418SNickeau     */
8151fa8c418SNickeau    public function setDisplay($display): Call
8161fa8c418SNickeau    {
8171fa8c418SNickeau        $mode = $this->getMode();
8181fa8c418SNickeau        if ($mode == "plugin") {
8191fa8c418SNickeau            $this->call[1][1][PluginUtility::DISPLAY] = $display;
8201fa8c418SNickeau        } else {
8211fa8c418SNickeau            LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING);
8221fa8c418SNickeau        }
8231fa8c418SNickeau        return $this;
8241fa8c418SNickeau
8251fa8c418SNickeau    }
8261fa8c418SNickeau
8271fa8c418SNickeau    /**
8281fa8c418SNickeau     * The plugin or not
8291fa8c418SNickeau     * @return mixed
8301fa8c418SNickeau     */
8311fa8c418SNickeau    private function getMode()
8321fa8c418SNickeau    {
8331fa8c418SNickeau        return $this->call[0];
8341fa8c418SNickeau    }
8351fa8c418SNickeau
8361fa8c418SNickeau    /**
8371fa8c418SNickeau     * Return if this an unmatched call with space
8381fa8c418SNickeau     * in captured content
8391fa8c418SNickeau     * @return bool
8401fa8c418SNickeau     */
8411fa8c418SNickeau    public function isUnMatchedEmptyCall(): bool
8421fa8c418SNickeau    {
8431fa8c418SNickeau        if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") {
8441fa8c418SNickeau            return true;
8451fa8c418SNickeau        }
8461fa8c418SNickeau        return false;
8471fa8c418SNickeau    }
8481fa8c418SNickeau
84937748cd8SNickeau
85037748cd8SNickeau}
851