xref: /plugin/combo/ComboStrap/Call.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
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",
61e4026cd1Sgerardnico        "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    ];
113*4cadd4f8SNickeau    const CANONICAL = "call";
1141fa8c418SNickeau
11537748cd8SNickeau    private $call;
11637748cd8SNickeau
11737748cd8SNickeau    /**
11837748cd8SNickeau     * The key identifier in the {@link CallStack}
11937748cd8SNickeau     * @var mixed|string
12037748cd8SNickeau     */
12137748cd8SNickeau    private $key;
12237748cd8SNickeau
12337748cd8SNickeau    /**
12437748cd8SNickeau     * Call constructor.
12537748cd8SNickeau     * @param $call - the instruction array (ie called a call)
12637748cd8SNickeau     */
12737748cd8SNickeau    public function __construct(&$call, $key = "")
12837748cd8SNickeau    {
12937748cd8SNickeau        $this->call = &$call;
13037748cd8SNickeau        $this->key = $key;
13137748cd8SNickeau    }
13237748cd8SNickeau
13337748cd8SNickeau    /**
13437748cd8SNickeau     * Insert a tag above
13537748cd8SNickeau     * @param $tagName
13637748cd8SNickeau     * @param $state
137c3437056SNickeau     * @param array $attribute
138*4cadd4f8SNickeau     * @param string|null $rawContext
139*4cadd4f8SNickeau     * @param string|null $content - the parsed content
140c3437056SNickeau     * @param string|null $payload - the payload after handler
141c3437056SNickeau     * @param int|null $position
14237748cd8SNickeau     * @return Call - a call
14337748cd8SNickeau     */
144*4cadd4f8SNickeau    public static function createComboCall($tagName, $state, array $attribute = array(), string $rawContext = null, string $content = null, string $payload = null, int $position = null): Call
14537748cd8SNickeau    {
14637748cd8SNickeau        $data = array(
14737748cd8SNickeau            PluginUtility::ATTRIBUTES => $attribute,
148*4cadd4f8SNickeau            PluginUtility::CONTEXT => $rawContext,
149c3437056SNickeau            PluginUtility::STATE => $state,
150c3437056SNickeau            PluginUtility::POSITION => $position
15137748cd8SNickeau        );
15237748cd8SNickeau        if ($payload != null) {
15337748cd8SNickeau            $data[PluginUtility::PAYLOAD] = $payload;
15437748cd8SNickeau        }
155c3437056SNickeau        $positionInText = $position;
15637748cd8SNickeau
15737748cd8SNickeau        $call = [
15837748cd8SNickeau            "plugin",
15937748cd8SNickeau            array(
16037748cd8SNickeau                PluginUtility::getComponentName($tagName),
16137748cd8SNickeau                $data,
16237748cd8SNickeau                $state,
16337748cd8SNickeau                $content
16437748cd8SNickeau            ),
16537748cd8SNickeau            $positionInText
16637748cd8SNickeau        ];
16737748cd8SNickeau        return new Call($call);
16837748cd8SNickeau    }
16937748cd8SNickeau
17037748cd8SNickeau    /**
17137748cd8SNickeau     * Insert a dokuwiki call
17237748cd8SNickeau     * @param $callName
17337748cd8SNickeau     * @param $array
17437748cd8SNickeau     * @param $positionInText
17537748cd8SNickeau     * @return Call
17637748cd8SNickeau     */
177*4cadd4f8SNickeau    public static function createNativeCall($callName, $array = [], $positionInText = null): Call
17837748cd8SNickeau    {
17937748cd8SNickeau        $call = [
18037748cd8SNickeau            $callName,
18137748cd8SNickeau            $array,
18237748cd8SNickeau            $positionInText
18337748cd8SNickeau        ];
18437748cd8SNickeau        return new Call($call);
18537748cd8SNickeau    }
18637748cd8SNickeau
18737748cd8SNickeau    public static function createFromInstruction($instruction)
18837748cd8SNickeau    {
18937748cd8SNickeau        return new Call($instruction);
19037748cd8SNickeau    }
19137748cd8SNickeau
192*4cadd4f8SNickeau    /**
193*4cadd4f8SNickeau     * @param Call $call
194*4cadd4f8SNickeau     * @return Call
195*4cadd4f8SNickeau     */
196*4cadd4f8SNickeau    public static function createFromCall(Call $call): Call
197*4cadd4f8SNickeau    {
198*4cadd4f8SNickeau        return self::createFromInstruction($call->toCallArray());
199*4cadd4f8SNickeau    }
200*4cadd4f8SNickeau
20137748cd8SNickeau
20237748cd8SNickeau    /**
20337748cd8SNickeau     *
20437748cd8SNickeau     * Return the tag name from a call array
20537748cd8SNickeau     *
20637748cd8SNickeau     * This is not the logical tag.
20737748cd8SNickeau     * This is much more what's called:
20837748cd8SNickeau     *   * the component name for a plugin
20937748cd8SNickeau     *   * or the handler name for dokuwiki
21037748cd8SNickeau     *
21137748cd8SNickeau     * For a plugin, this is equivalent
21237748cd8SNickeau     * to the {@link SyntaxPlugin::getPluginComponent()}
21337748cd8SNickeau     *
21437748cd8SNickeau     * This is not the fully qualified component name:
21537748cd8SNickeau     *   * with the plugin as prefix such as in {@link Call::getComponentName()}
21637748cd8SNickeau     *   * or with the `open` and `close` prefix such as `p_close` ...
21737748cd8SNickeau     *
21837748cd8SNickeau     * @return mixed|string
21937748cd8SNickeau     */
22037748cd8SNickeau    public function getTagName()
22137748cd8SNickeau    {
22237748cd8SNickeau        $mode = $this->call[0];
22337748cd8SNickeau        if ($mode != "plugin") {
22437748cd8SNickeau
22537748cd8SNickeau            /**
22637748cd8SNickeau             * This is a standard dokuwiki node
22737748cd8SNickeau             */
22837748cd8SNickeau            $dokuWikiNodeName = $this->call[0];
22937748cd8SNickeau
23037748cd8SNickeau            /**
23137748cd8SNickeau             * The dokwuiki node name has also the open and close notion
23237748cd8SNickeau             * We delete this is not in the doc and therefore not logical
23337748cd8SNickeau             */
23437748cd8SNickeau            $tagName = str_replace("_close", "", $dokuWikiNodeName);
23537748cd8SNickeau            $tagName = str_replace("_open", "", $tagName);
23637748cd8SNickeau
23737748cd8SNickeau        } else {
23837748cd8SNickeau
23937748cd8SNickeau            /**
24037748cd8SNickeau             * This is a plugin node
24137748cd8SNickeau             */
24237748cd8SNickeau            $pluginDokuData = $this->call[1];
24337748cd8SNickeau            $component = $pluginDokuData[0];
24437748cd8SNickeau            if (!is_array($component)) {
24537748cd8SNickeau                /**
24637748cd8SNickeau                 * Tag name from class
24737748cd8SNickeau                 */
24837748cd8SNickeau                $componentNames = explode("_", $component);
24937748cd8SNickeau                /**
25037748cd8SNickeau                 * To take care of
25137748cd8SNickeau                 * PHP Warning:  sizeof(): Parameter must be an array or an object that implements Countable
25237748cd8SNickeau                 * in lib/plugins/combo/class/Tag.php on line 314
25337748cd8SNickeau                 */
25437748cd8SNickeau                if (is_array($componentNames)) {
25537748cd8SNickeau                    $tagName = $componentNames[sizeof($componentNames) - 1];
25637748cd8SNickeau                } else {
25737748cd8SNickeau                    $tagName = $component;
25837748cd8SNickeau                }
25937748cd8SNickeau            } else {
26037748cd8SNickeau                // To resolve: explode() expects parameter 2 to be string, array given
261c3437056SNickeau                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);
26237748cd8SNickeau                $tagName = "";
26337748cd8SNickeau            }
26437748cd8SNickeau
26537748cd8SNickeau
26637748cd8SNickeau        }
26737748cd8SNickeau        return $tagName;
26837748cd8SNickeau
26937748cd8SNickeau    }
27037748cd8SNickeau
27137748cd8SNickeau
27237748cd8SNickeau    /**
27337748cd8SNickeau     * The parser state
27437748cd8SNickeau     * @return mixed
27537748cd8SNickeau     * May be null (example eol, internallink, ...)
27637748cd8SNickeau     */
27737748cd8SNickeau    public function getState()
27837748cd8SNickeau    {
27937748cd8SNickeau        $mode = $this->call[0];
280*4cadd4f8SNickeau        if ($mode !== "plugin") {
28137748cd8SNickeau
28237748cd8SNickeau            /**
28337748cd8SNickeau             * There is no state because this is a standard
28437748cd8SNickeau             * dokuwiki syntax found in {@link \Doku_Renderer_xhtml}
28537748cd8SNickeau             * check if this is not a `...._close` or `...._open`
28637748cd8SNickeau             * to derive the state
28737748cd8SNickeau             */
28837748cd8SNickeau            $mode = $this->call[0];
28937748cd8SNickeau            $lastPositionSepName = strrpos($mode, "_");
29037748cd8SNickeau            $closeOrOpen = substr($mode, $lastPositionSepName + 1);
29137748cd8SNickeau            switch ($closeOrOpen) {
29237748cd8SNickeau                case "open":
29337748cd8SNickeau                    return DOKU_LEXER_ENTER;
29437748cd8SNickeau                case "close":
29537748cd8SNickeau                    return DOKU_LEXER_EXIT;
29637748cd8SNickeau                default:
29737748cd8SNickeau                    return null;
29837748cd8SNickeau            }
29937748cd8SNickeau
30037748cd8SNickeau        } else {
30137748cd8SNickeau            // Plugin
30237748cd8SNickeau            $returnedArray = $this->call[1];
30337748cd8SNickeau            if (array_key_exists(2, $returnedArray)) {
30437748cd8SNickeau                return $returnedArray[2];
30537748cd8SNickeau            } else {
30637748cd8SNickeau                return null;
30737748cd8SNickeau            }
30837748cd8SNickeau        }
30937748cd8SNickeau    }
31037748cd8SNickeau
31137748cd8SNickeau    /**
31237748cd8SNickeau     * @return mixed the data returned from the {@link DokuWiki_Syntax_Plugin::handle} (ie attributes, payload, ...)
31337748cd8SNickeau     */
314*4cadd4f8SNickeau    public function &getPluginData($attribute = null)
31537748cd8SNickeau    {
316*4cadd4f8SNickeau        $data = &$this->call[1][1];
317*4cadd4f8SNickeau        if ($attribute === null) {
318*4cadd4f8SNickeau            return $data;
319*4cadd4f8SNickeau        }
320*4cadd4f8SNickeau        return $data[$attribute];
321*4cadd4f8SNickeau
32237748cd8SNickeau    }
32337748cd8SNickeau
32437748cd8SNickeau    /**
32537748cd8SNickeau     * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle}
32637748cd8SNickeau     */
32737748cd8SNickeau    public function getCapturedContent()
32837748cd8SNickeau    {
32937748cd8SNickeau        $caller = $this->call[0];
33037748cd8SNickeau        switch ($caller) {
33137748cd8SNickeau            case "plugin":
33237748cd8SNickeau                return $this->call[1][3];
33337748cd8SNickeau            case "internallink":
33437748cd8SNickeau                return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]';
33537748cd8SNickeau            case "eol":
33637748cd8SNickeau                return DOKU_LF;
33737748cd8SNickeau            case "header":
33837748cd8SNickeau            case "cdata":
33937748cd8SNickeau                return $this->call[1][0];
34037748cd8SNickeau            default:
34137748cd8SNickeau                if (isset($this->call[1][0]) && is_string($this->call[1][0])) {
34237748cd8SNickeau                    return $this->call[1][0];
34337748cd8SNickeau                } else {
34437748cd8SNickeau                    return "";
34537748cd8SNickeau                }
34637748cd8SNickeau        }
34737748cd8SNickeau    }
34837748cd8SNickeau
34937748cd8SNickeau
350*4cadd4f8SNickeau    public function getAttributes(): ?array
35137748cd8SNickeau    {
35237748cd8SNickeau
35337748cd8SNickeau        $tagName = $this->getTagName();
35437748cd8SNickeau        switch ($tagName) {
35537748cd8SNickeau            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
35637748cd8SNickeau                return $this->call[1];
35737748cd8SNickeau            default:
35837748cd8SNickeau                $data = $this->getPluginData();
35937748cd8SNickeau                if (isset($data[PluginUtility::ATTRIBUTES])) {
360*4cadd4f8SNickeau                    $attributes = $data[PluginUtility::ATTRIBUTES];
361*4cadd4f8SNickeau                    if (!is_array($attributes)) {
362*4cadd4f8SNickeau                        $message = "The attributes value are not an array for the call ($this)";
363*4cadd4f8SNickeau                        if (PluginUtility::isDevOrTest()) {
364*4cadd4f8SNickeau                            throw new ExceptionComboRuntime($message, self::CANONICAL);
365*4cadd4f8SNickeau                        }
366*4cadd4f8SNickeau                        LogUtility::msg($message);
367*4cadd4f8SNickeau                        return null;
368*4cadd4f8SNickeau                    }
369*4cadd4f8SNickeau                    return $attributes;
37037748cd8SNickeau                } else {
37137748cd8SNickeau                    return null;
37237748cd8SNickeau                }
37337748cd8SNickeau        }
37437748cd8SNickeau    }
37537748cd8SNickeau
37637748cd8SNickeau    public function removeAttributes()
37737748cd8SNickeau    {
37837748cd8SNickeau
37937748cd8SNickeau        $data = &$this->getPluginData();
38037748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES])) {
38137748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES]);
38237748cd8SNickeau        }
38337748cd8SNickeau
38437748cd8SNickeau    }
38537748cd8SNickeau
38637748cd8SNickeau    public function updateToPluginComponent($component, $state, $attributes = array())
38737748cd8SNickeau    {
38837748cd8SNickeau        if ($this->call[0] == "plugin") {
38937748cd8SNickeau            $match = $this->call[1][3];
39037748cd8SNickeau        } else {
39137748cd8SNickeau            $this->call[0] = "plugin";
39237748cd8SNickeau            $match = "";
39337748cd8SNickeau        }
39437748cd8SNickeau        $this->call[1] = array(
39537748cd8SNickeau            0 => $component,
39637748cd8SNickeau            1 => array(
39737748cd8SNickeau                PluginUtility::ATTRIBUTES => $attributes,
39837748cd8SNickeau                PluginUtility::STATE => $state,
39937748cd8SNickeau            ),
40037748cd8SNickeau            2 => $state,
40137748cd8SNickeau            3 => $match
40237748cd8SNickeau        );
40337748cd8SNickeau
40437748cd8SNickeau    }
40537748cd8SNickeau
4061fa8c418SNickeau    /**
4071fa8c418SNickeau     * Does the display has been set
4081fa8c418SNickeau     * to override the dokuwiki default
4091fa8c418SNickeau     * ({@link Syntax::getPType()}
4101fa8c418SNickeau     *
4111fa8c418SNickeau     * because an image is by default a inline component
4121fa8c418SNickeau     * but can be a block (ie top image of a card)
4131fa8c418SNickeau     * @return bool
4141fa8c418SNickeau     */
4151fa8c418SNickeau    public function isDisplaySet(): bool
4161fa8c418SNickeau    {
4171fa8c418SNickeau        return isset($this->call[1][1][PluginUtility::DISPLAY]);
4181fa8c418SNickeau    }
4191fa8c418SNickeau
42037748cd8SNickeau    public function getDisplay()
42137748cd8SNickeau    {
4221fa8c418SNickeau        $mode = $this->getMode();
4231fa8c418SNickeau        if ($mode == "plugin") {
4241fa8c418SNickeau            if ($this->isDisplaySet()) {
4251fa8c418SNickeau                return $this->call[1][1][PluginUtility::DISPLAY];
4261fa8c418SNickeau            }
4271fa8c418SNickeau        }
4281fa8c418SNickeau
42937748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
43037748cd8SNickeau            /**
43137748cd8SNickeau             * Unmatched are content (ie text node in XML/HTML) and have
43237748cd8SNickeau             * no display
43337748cd8SNickeau             */
43437748cd8SNickeau            return Call::INLINE_DISPLAY;
43537748cd8SNickeau        } else {
43637748cd8SNickeau            $mode = $this->call[0];
43737748cd8SNickeau            if ($mode == "plugin") {
43837748cd8SNickeau                global $DOKU_PLUGINS;
43937748cd8SNickeau                $component = $this->getComponentName();
44037748cd8SNickeau                /**
44137748cd8SNickeau                 * @var SyntaxPlugin $syntaxPlugin
44237748cd8SNickeau                 */
44337748cd8SNickeau                $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component];
44437748cd8SNickeau                $pType = $syntaxPlugin->getPType();
44537748cd8SNickeau                switch ($pType) {
44637748cd8SNickeau                    case "normal":
44737748cd8SNickeau                        return Call::INLINE_DISPLAY;
44837748cd8SNickeau                    case "block":
44937748cd8SNickeau                    case "stack":
45037748cd8SNickeau                        return Call::BlOCK_DISPLAY;
45137748cd8SNickeau                    default:
45237748cd8SNickeau                        LogUtility::msg("The ptype (" . $pType . ") is unknown.");
45337748cd8SNickeau                        return null;
45437748cd8SNickeau                }
45537748cd8SNickeau            } else {
45637748cd8SNickeau                if ($mode == "eol") {
45737748cd8SNickeau                    /**
45837748cd8SNickeau                     * Control character
45937748cd8SNickeau                     * We return it as it's used in the
46037748cd8SNickeau                     * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()}
46137748cd8SNickeau                     * to create the paragraph
46237748cd8SNickeau                     * This is not a block, nor an inline
46337748cd8SNickeau                     */
46437748cd8SNickeau                    return $mode;
46537748cd8SNickeau                }
46637748cd8SNickeau
46737748cd8SNickeau                if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) {
46837748cd8SNickeau                    return Call::INLINE_DISPLAY;
46937748cd8SNickeau                }
47037748cd8SNickeau
47137748cd8SNickeau                if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) {
47237748cd8SNickeau                    return Call::BlOCK_DISPLAY;
47337748cd8SNickeau                }
47437748cd8SNickeau
47537748cd8SNickeau                LogUtility::msg("The display of the call with the mode " . $mode . " is unknown");
47637748cd8SNickeau                return null;
47737748cd8SNickeau
47837748cd8SNickeau
47937748cd8SNickeau            }
48037748cd8SNickeau        }
48137748cd8SNickeau
48237748cd8SNickeau    }
48337748cd8SNickeau
48437748cd8SNickeau    /**
48537748cd8SNickeau     * Same as {@link Call::getTagName()}
48637748cd8SNickeau     * but fully qualified
48737748cd8SNickeau     * @return string
48837748cd8SNickeau     */
48937748cd8SNickeau    public function getComponentName()
49037748cd8SNickeau    {
49137748cd8SNickeau        $mode = $this->call[0];
49237748cd8SNickeau        if ($mode == "plugin") {
49337748cd8SNickeau            $pluginDokuData = $this->call[1];
49437748cd8SNickeau            return $pluginDokuData[0];
49537748cd8SNickeau        } else {
49637748cd8SNickeau            return $mode;
49737748cd8SNickeau        }
49837748cd8SNickeau    }
49937748cd8SNickeau
50037748cd8SNickeau    public function updateEolToSpace()
50137748cd8SNickeau    {
50237748cd8SNickeau        $mode = $this->call[0];
50337748cd8SNickeau        if ($mode != "eol") {
50437748cd8SNickeau            LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support");
50537748cd8SNickeau        } else {
50637748cd8SNickeau            $this->call[0] = "cdata";
50737748cd8SNickeau            $this->call[1] = array(
50837748cd8SNickeau                0 => " "
50937748cd8SNickeau            );
51037748cd8SNickeau        }
51137748cd8SNickeau
51237748cd8SNickeau    }
51337748cd8SNickeau
51437748cd8SNickeau    public function addAttribute($key, $value)
51537748cd8SNickeau    {
51637748cd8SNickeau        $mode = $this->call[0];
51737748cd8SNickeau        if ($mode == "plugin") {
51837748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value;
51937748cd8SNickeau        } else {
52037748cd8SNickeau            LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
52137748cd8SNickeau        }
52237748cd8SNickeau    }
52337748cd8SNickeau
52437748cd8SNickeau    public function getContext()
52537748cd8SNickeau    {
52637748cd8SNickeau        $mode = $this->call[0];
52737748cd8SNickeau        if ($mode == "plugin") {
52837748cd8SNickeau            return $this->call[1][1][PluginUtility::CONTEXT];
52937748cd8SNickeau        } else {
53037748cd8SNickeau            LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
53137748cd8SNickeau            return null;
53237748cd8SNickeau        }
53337748cd8SNickeau    }
53437748cd8SNickeau
53537748cd8SNickeau    /**
53637748cd8SNickeau     *
53737748cd8SNickeau     * @return array
53837748cd8SNickeau     */
53937748cd8SNickeau    public function toCallArray()
54037748cd8SNickeau    {
54137748cd8SNickeau        return $this->call;
54237748cd8SNickeau    }
54337748cd8SNickeau
54437748cd8SNickeau    public function __toString()
54537748cd8SNickeau    {
54637748cd8SNickeau        $name = $this->key;
54737748cd8SNickeau        if (!empty($name)) {
54837748cd8SNickeau            $name .= " - ";
54937748cd8SNickeau        }
55037748cd8SNickeau        $name .= $this->getTagName();
55137748cd8SNickeau        return $name;
55237748cd8SNickeau    }
55337748cd8SNickeau
554*4cadd4f8SNickeau    /**
555*4cadd4f8SNickeau     * @return string|null
556*4cadd4f8SNickeau     *
557*4cadd4f8SNickeau     * If the type returned is a boolean attribute,
558*4cadd4f8SNickeau     * it means you need to define the expected types
559*4cadd4f8SNickeau     * in the function {@link TagAttributes::createFromTagMatch()}
560*4cadd4f8SNickeau     * as third attribute
561*4cadd4f8SNickeau     */
562*4cadd4f8SNickeau    public function getType(): ?string
56337748cd8SNickeau    {
56437748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
56537748cd8SNickeau            return null;
56637748cd8SNickeau        } else {
567*4cadd4f8SNickeau            return $this->getAttribute(TagAttributes::TYPE_KEY);
56837748cd8SNickeau        }
56937748cd8SNickeau    }
57037748cd8SNickeau
57137748cd8SNickeau    /**
57237748cd8SNickeau     * @param $key
57337748cd8SNickeau     * @param null $default
574*4cadd4f8SNickeau     * @return array|string|null
57537748cd8SNickeau     */
57637748cd8SNickeau    public function getAttribute($key, $default = null)
57737748cd8SNickeau    {
57837748cd8SNickeau        $attributes = $this->getAttributes();
57937748cd8SNickeau        if (isset($attributes[$key])) {
58037748cd8SNickeau            return $attributes[$key];
581*4cadd4f8SNickeau        }
58237748cd8SNickeau        return $default;
583*4cadd4f8SNickeau
58437748cd8SNickeau    }
58537748cd8SNickeau
586*4cadd4f8SNickeau    public
587*4cadd4f8SNickeau    function getPayload()
58837748cd8SNickeau    {
58937748cd8SNickeau        $mode = $this->call[0];
59037748cd8SNickeau        if ($mode == "plugin") {
59137748cd8SNickeau            return $this->call[1][1][PluginUtility::PAYLOAD];
59237748cd8SNickeau        } else {
59337748cd8SNickeau            LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support");
59437748cd8SNickeau            return null;
59537748cd8SNickeau        }
59637748cd8SNickeau    }
59737748cd8SNickeau
598*4cadd4f8SNickeau    public
599*4cadd4f8SNickeau    function setContext($value)
60037748cd8SNickeau    {
60137748cd8SNickeau        $this->call[1][1][PluginUtility::CONTEXT] = $value;
60237748cd8SNickeau        return $this;
60337748cd8SNickeau    }
60437748cd8SNickeau
605*4cadd4f8SNickeau    public
606*4cadd4f8SNickeau    function hasAttribute($attributeName): bool
60737748cd8SNickeau    {
60837748cd8SNickeau        $attributes = $this->getAttributes();
60937748cd8SNickeau        if (isset($attributes[$attributeName])) {
61037748cd8SNickeau            return true;
61137748cd8SNickeau        } else {
61237748cd8SNickeau            if ($this->getType() == $attributeName) {
61337748cd8SNickeau                return true;
61437748cd8SNickeau            } else {
61537748cd8SNickeau                return false;
61637748cd8SNickeau            }
61737748cd8SNickeau        }
61837748cd8SNickeau    }
61937748cd8SNickeau
620*4cadd4f8SNickeau    public
621*4cadd4f8SNickeau    function isPluginCall()
62237748cd8SNickeau    {
62337748cd8SNickeau        return $this->call[0] === "plugin";
62437748cd8SNickeau    }
62537748cd8SNickeau
62637748cd8SNickeau    /**
62737748cd8SNickeau     * @return mixed|string the position (ie key) in the array
62837748cd8SNickeau     */
629*4cadd4f8SNickeau    public
630*4cadd4f8SNickeau    function getKey()
63137748cd8SNickeau    {
63237748cd8SNickeau        return $this->key;
63337748cd8SNickeau    }
63437748cd8SNickeau
635*4cadd4f8SNickeau    public
636*4cadd4f8SNickeau    function &getCall()
63737748cd8SNickeau    {
63837748cd8SNickeau        return $this->call;
63937748cd8SNickeau    }
64037748cd8SNickeau
641*4cadd4f8SNickeau    public
642*4cadd4f8SNickeau    function setState($state)
64337748cd8SNickeau    {
64437748cd8SNickeau        if ($this->call[0] == "plugin") {
64537748cd8SNickeau            // for dokuwiki
64637748cd8SNickeau            $this->call[1][2] = $state;
64737748cd8SNickeau            // for the combo plugin if any
64837748cd8SNickeau            if (isset($this->call[1][1][PluginUtility::STATE])) {
64937748cd8SNickeau                $this->call[1][1][PluginUtility::STATE] = $state;
65037748cd8SNickeau            }
65137748cd8SNickeau        } else {
65237748cd8SNickeau            LogUtility::msg("This modification of state is not yet supported for a native call");
65337748cd8SNickeau        }
65437748cd8SNickeau    }
65537748cd8SNickeau
65637748cd8SNickeau
65737748cd8SNickeau    /**
65837748cd8SNickeau     * Return the position of the first matched character in the text file
65937748cd8SNickeau     * @return mixed
66037748cd8SNickeau     */
661*4cadd4f8SNickeau    public
662*4cadd4f8SNickeau    function getFirstMatchedCharacterPosition()
66337748cd8SNickeau    {
66437748cd8SNickeau
66537748cd8SNickeau        return $this->call[2];
66637748cd8SNickeau
66737748cd8SNickeau    }
66837748cd8SNickeau
66937748cd8SNickeau    /**
67037748cd8SNickeau     * Return the position of the last matched character in the text file
67137748cd8SNickeau     *
67237748cd8SNickeau     * This is the {@link Call::getFirstMatchedCharacterPosition()}
67337748cd8SNickeau     * plus the length of the {@link Call::getCapturedContent()}
67437748cd8SNickeau     * matched content
67537748cd8SNickeau     * @return int|mixed
67637748cd8SNickeau     */
677*4cadd4f8SNickeau    public
678*4cadd4f8SNickeau    function getLastMatchedCharacterPosition()
67937748cd8SNickeau    {
68037748cd8SNickeau        return $this->getFirstMatchedCharacterPosition() + strlen($this->getCapturedContent());
68137748cd8SNickeau    }
68237748cd8SNickeau
68337748cd8SNickeau    /**
68437748cd8SNickeau     * @param $value string the class string to add
68537748cd8SNickeau     * @return Call
68637748cd8SNickeau     */
687*4cadd4f8SNickeau    public
688*4cadd4f8SNickeau    function addClassName(string $value): Call
68937748cd8SNickeau    {
69037748cd8SNickeau        $class = $this->getAttribute("class");
69137748cd8SNickeau        if ($class != null) {
69237748cd8SNickeau            $value = "$class $value";
69337748cd8SNickeau        }
69437748cd8SNickeau        $this->addAttribute("class", $value);
69537748cd8SNickeau        return $this;
69637748cd8SNickeau
69737748cd8SNickeau    }
69837748cd8SNickeau
69937748cd8SNickeau    /**
70037748cd8SNickeau     * @param $key
70137748cd8SNickeau     * @return mixed|null - the delete value of null if not found
70237748cd8SNickeau     */
703*4cadd4f8SNickeau    public
704*4cadd4f8SNickeau    function removeAttribute($key)
70537748cd8SNickeau    {
70637748cd8SNickeau
70737748cd8SNickeau        $data = &$this->getPluginData();
70837748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES][$key])) {
70937748cd8SNickeau            $value = $data[PluginUtility::ATTRIBUTES][$key];
71037748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES][$key]);
71137748cd8SNickeau            return $value;
71237748cd8SNickeau        } else {
71337748cd8SNickeau            // boolean attribute as first attribute
71437748cd8SNickeau            if ($this->getType() == $key) {
71537748cd8SNickeau                unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]);
71637748cd8SNickeau                return true;
71737748cd8SNickeau            }
71837748cd8SNickeau            return null;
71937748cd8SNickeau        }
72037748cd8SNickeau
72137748cd8SNickeau    }
72237748cd8SNickeau
723*4cadd4f8SNickeau    public
724*4cadd4f8SNickeau    function setPayload($text)
72537748cd8SNickeau    {
72637748cd8SNickeau        if ($this->isPluginCall()) {
72737748cd8SNickeau            $this->call[1][1][PluginUtility::PAYLOAD] = $text;
72837748cd8SNickeau        } else {
72937748cd8SNickeau            LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented");
73037748cd8SNickeau        }
73137748cd8SNickeau    }
73237748cd8SNickeau
73337748cd8SNickeau    /**
73437748cd8SNickeau     * @return bool true if the call is a text call (same as dom text node)
73537748cd8SNickeau     */
736*4cadd4f8SNickeau    public
737*4cadd4f8SNickeau    function isTextCall()
73837748cd8SNickeau    {
73937748cd8SNickeau        return (
74037748cd8SNickeau            $this->getState() == DOKU_LEXER_UNMATCHED ||
74137748cd8SNickeau            $this->getTagName() == "cdata" ||
74237748cd8SNickeau            $this->getTagName() == "acronym"
74337748cd8SNickeau        );
74437748cd8SNickeau    }
74537748cd8SNickeau
746*4cadd4f8SNickeau    public
747*4cadd4f8SNickeau    function setType($type)
74837748cd8SNickeau    {
74937748cd8SNickeau        if ($this->isPluginCall()) {
75037748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type;
75137748cd8SNickeau        } else {
75237748cd8SNickeau            LogUtility::msg("This is not a plugin call ($this), you can't set the type");
75337748cd8SNickeau        }
75437748cd8SNickeau    }
75537748cd8SNickeau
756*4cadd4f8SNickeau    public
757*4cadd4f8SNickeau    function addCssStyle($key, $value)
75837748cd8SNickeau    {
75937748cd8SNickeau        $style = $this->getAttribute("style");
76037748cd8SNickeau        $cssValue = "$key:$value";
761*4cadd4f8SNickeau        if ($style !== null) {
76237748cd8SNickeau            $cssValue = "$style; $cssValue";
76337748cd8SNickeau        }
76437748cd8SNickeau        $this->addAttribute("style", $cssValue);
76537748cd8SNickeau    }
76637748cd8SNickeau
767*4cadd4f8SNickeau    public
768*4cadd4f8SNickeau    function setSyntaxComponentFromTag($tag)
76937748cd8SNickeau    {
77037748cd8SNickeau
77137748cd8SNickeau        if ($this->isPluginCall()) {
77237748cd8SNickeau            $this->call[1][0] = PluginUtility::getComponentName($tag);
77337748cd8SNickeau        } else {
77437748cd8SNickeau            LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)");
77537748cd8SNickeau        }
77637748cd8SNickeau    }
77737748cd8SNickeau
77837748cd8SNickeau    /**
77937748cd8SNickeau     * @param Page $page
78037748cd8SNickeau     * @return Call
78137748cd8SNickeau     */
782*4cadd4f8SNickeau    public
783*4cadd4f8SNickeau    function render(Page $page)
78437748cd8SNickeau    {
78537748cd8SNickeau        return $this->renderFromData(TemplateUtility::getMetadataDataFromPage($page));
78637748cd8SNickeau    }
78737748cd8SNickeau
788*4cadd4f8SNickeau    public
789*4cadd4f8SNickeau    function renderFromData(array $array): Call
79037748cd8SNickeau    {
7911fa8c418SNickeau
7921fa8c418SNickeau        /**
7931fa8c418SNickeau         * Render all attributes
7941fa8c418SNickeau         */
7951fa8c418SNickeau        $attributes = $this->getAttributes();
7961fa8c418SNickeau        if ($attributes !== null) {
7971fa8c418SNickeau            foreach ($attributes as $key => $value) {
7981fa8c418SNickeau                if (is_string($value)) {
7991fa8c418SNickeau                    $this->addAttribute($key, TemplateUtility::renderStringTemplateFromDataArray($value, $array));
8001fa8c418SNickeau                }
8011fa8c418SNickeau            }
8021fa8c418SNickeau        }
8031fa8c418SNickeau
8041fa8c418SNickeau        /**
8051fa8c418SNickeau         * Content rendering
8061fa8c418SNickeau         */
80737748cd8SNickeau        $state = $this->getState();
80837748cd8SNickeau        if ($state == DOKU_LEXER_UNMATCHED) {
80937748cd8SNickeau            if ($this->isPluginCall()) {
81037748cd8SNickeau                $payload = $this->getPayload();
81137748cd8SNickeau                if (!empty($payload)) {
81237748cd8SNickeau                    $this->setPayload(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
81337748cd8SNickeau                }
81437748cd8SNickeau            }
81537748cd8SNickeau        } else {
81637748cd8SNickeau            $tagName = $this->getTagName();
81737748cd8SNickeau            switch ($tagName) {
81837748cd8SNickeau                case "eol":
81937748cd8SNickeau                    break;
82037748cd8SNickeau                case "cdata":
82137748cd8SNickeau                    $payload = $this->getCapturedContent();
82237748cd8SNickeau                    $this->setCapturedContent(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
82337748cd8SNickeau                    break;
82437748cd8SNickeau                case \syntax_plugin_combo_pipeline::TAG:
82537748cd8SNickeau                    $pageTemplate = PluginUtility::getTagContent($this->getCapturedContent());
82637748cd8SNickeau                    $script = TemplateUtility::renderStringTemplateFromDataArray($pageTemplate, $array);
82737748cd8SNickeau                    $string = PipelineUtility::execute($script);
82837748cd8SNickeau                    $this->setPayload($string);
82937748cd8SNickeau                    break;
83037748cd8SNickeau            }
83137748cd8SNickeau        }
8321fa8c418SNickeau
83337748cd8SNickeau        return $this;
83437748cd8SNickeau    }
83537748cd8SNickeau
836*4cadd4f8SNickeau    public
837*4cadd4f8SNickeau    function setCapturedContent($content)
83837748cd8SNickeau    {
83937748cd8SNickeau        $tagName = $this->getTagName();
84037748cd8SNickeau        switch ($tagName) {
84137748cd8SNickeau            case "cdata":
84237748cd8SNickeau                $this->call[1][0] = $content;
84337748cd8SNickeau                break;
84437748cd8SNickeau            default:
84537748cd8SNickeau                LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR);
84637748cd8SNickeau        }
84737748cd8SNickeau    }
84837748cd8SNickeau
8491fa8c418SNickeau    /**
8501fa8c418SNickeau     * Set the display to block or inline
8511fa8c418SNickeau     * One of `block` or `inline`
8521fa8c418SNickeau     */
853*4cadd4f8SNickeau    public
854*4cadd4f8SNickeau    function setDisplay($display): Call
8551fa8c418SNickeau    {
8561fa8c418SNickeau        $mode = $this->getMode();
8571fa8c418SNickeau        if ($mode == "plugin") {
8581fa8c418SNickeau            $this->call[1][1][PluginUtility::DISPLAY] = $display;
8591fa8c418SNickeau        } else {
8601fa8c418SNickeau            LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING);
8611fa8c418SNickeau        }
8621fa8c418SNickeau        return $this;
8631fa8c418SNickeau
8641fa8c418SNickeau    }
8651fa8c418SNickeau
8661fa8c418SNickeau    /**
8671fa8c418SNickeau     * The plugin or not
8681fa8c418SNickeau     * @return mixed
8691fa8c418SNickeau     */
870*4cadd4f8SNickeau    private
871*4cadd4f8SNickeau    function getMode()
8721fa8c418SNickeau    {
8731fa8c418SNickeau        return $this->call[0];
8741fa8c418SNickeau    }
8751fa8c418SNickeau
8761fa8c418SNickeau    /**
8771fa8c418SNickeau     * Return if this an unmatched call with space
8781fa8c418SNickeau     * in captured content
8791fa8c418SNickeau     * @return bool
8801fa8c418SNickeau     */
881*4cadd4f8SNickeau    public
882*4cadd4f8SNickeau    function isUnMatchedEmptyCall(): bool
8831fa8c418SNickeau    {
8841fa8c418SNickeau        if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") {
8851fa8c418SNickeau            return true;
8861fa8c418SNickeau        }
8871fa8c418SNickeau        return false;
8881fa8c418SNickeau    }
8891fa8c418SNickeau
890*4cadd4f8SNickeau    public
891*4cadd4f8SNickeau    function getExitCode()
892*4cadd4f8SNickeau    {
893*4cadd4f8SNickeau        $mode = $this->call[0];
894*4cadd4f8SNickeau        if ($mode == "plugin") {
895*4cadd4f8SNickeau            $value = $this->call[1][1][PluginUtility::EXIT_CODE];
896*4cadd4f8SNickeau            if ($value === null) {
897*4cadd4f8SNickeau                return 0;
898*4cadd4f8SNickeau            }
899*4cadd4f8SNickeau            return $value;
900*4cadd4f8SNickeau        } else {
901*4cadd4f8SNickeau            LogUtility::msg("You can't ask for the exit code from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support");
902*4cadd4f8SNickeau            return 0;
903*4cadd4f8SNickeau        }
904*4cadd4f8SNickeau    }
905*4cadd4f8SNickeau
906*4cadd4f8SNickeau    public function setAttribute(string $name, $value): Call
907*4cadd4f8SNickeau    {
908*4cadd4f8SNickeau        $this->getPluginData()[PluginUtility::ATTRIBUTES][$name] = $value;
909*4cadd4f8SNickeau        return $this;
910*4cadd4f8SNickeau    }
911*4cadd4f8SNickeau
912*4cadd4f8SNickeau    public function setPluginData(string $name, $value): Call
913*4cadd4f8SNickeau    {
914*4cadd4f8SNickeau        $this->getPluginData()[$name] = $value;
915*4cadd4f8SNickeau        return $this;
916*4cadd4f8SNickeau    }
917*4cadd4f8SNickeau
91837748cd8SNickeau
91937748cd8SNickeau}
920