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