xref: /template/strap/ComboStrap/Call.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau/**
3*37748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
4*37748cd8SNickeau *
5*37748cd8SNickeau * This source code is licensed under the GPL license found in the
6*37748cd8SNickeau * COPYING  file in the root directory of this source tree.
7*37748cd8SNickeau *
8*37748cd8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
9*37748cd8SNickeau * @author   ComboStrap <support@combostrap.com>
10*37748cd8SNickeau *
11*37748cd8SNickeau */
12*37748cd8SNickeau
13*37748cd8SNickeaunamespace ComboStrap;
14*37748cd8SNickeau
15*37748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin;
16*37748cd8SNickeau
17*37748cd8SNickeau
18*37748cd8SNickeau/**
19*37748cd8SNickeau * Class Call
20*37748cd8SNickeau * @package ComboStrap
21*37748cd8SNickeau *
22*37748cd8SNickeau * A wrapper around what's called a call
23*37748cd8SNickeau * which is an array of information such
24*37748cd8SNickeau * the mode, the data
25*37748cd8SNickeau *
26*37748cd8SNickeau * The {@link CallStack} is the only syntax representation that
27*37748cd8SNickeau * is available in DokuWiki
28*37748cd8SNickeau */
29*37748cd8SNickeauclass Call
30*37748cd8SNickeau{
31*37748cd8SNickeau
32*37748cd8SNickeau    const INLINE_DISPLAY = "inline";
33*37748cd8SNickeau    const BlOCK_DISPLAY = "block";
34*37748cd8SNickeau    /**
35*37748cd8SNickeau     * List of inline components
36*37748cd8SNickeau     * Used to manage white space before an unmatched string.
37*37748cd8SNickeau     * The syntax tree of Dokuwiki (ie {@link \Doku_Handler::$calls})
38*37748cd8SNickeau     * has only data and no class, for now, we create this
39*37748cd8SNickeau     * lists manually because this is a hassle to retrieve this information from {@link \DokuWiki_Syntax_Plugin::getType()}
40*37748cd8SNickeau     */
41*37748cd8SNickeau    const INLINE_DOKUWIKI_COMPONENTS = array(
42*37748cd8SNickeau        /**
43*37748cd8SNickeau         * Formatting https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
44*37748cd8SNickeau         * Comes from the {@link \dokuwiki\Parsing\ParserMode\Formatting} class
45*37748cd8SNickeau         */
46*37748cd8SNickeau        "cdata",
47*37748cd8SNickeau        "unformatted", // ie %% or nowiki
48*37748cd8SNickeau        "doublequoteclosing", // https://www.dokuwiki.org/config:typography / https://www.dokuwiki.org/wiki:syntax#text_to_html_conversions
49*37748cd8SNickeau        "doublequoteopening",
50*37748cd8SNickeau        "singlequoteopening",
51*37748cd8SNickeau        "singlequoteclosing",
52*37748cd8SNickeau        "multiplyentity",
53*37748cd8SNickeau        "apostrophe",
54*37748cd8SNickeau        "strong",
55*37748cd8SNickeau        "emphasis",
56*37748cd8SNickeau        "emphasis_open",
57*37748cd8SNickeau        "emphasis_close",
58*37748cd8SNickeau        "underline",
59*37748cd8SNickeau        "monospace",
60*37748cd8SNickeau        "subscript",
61*37748cd8SNickeau        "superscript",
62*37748cd8SNickeau        "deleted",
63*37748cd8SNickeau        "footnote",
64*37748cd8SNickeau        /**
65*37748cd8SNickeau         * Others
66*37748cd8SNickeau         */
67*37748cd8SNickeau        "acronym",
68*37748cd8SNickeau        "strong_close",
69*37748cd8SNickeau        "strong_open",
70*37748cd8SNickeau        "monospace_open",
71*37748cd8SNickeau        "monospace_close",
72*37748cd8SNickeau        "doublequoteopening", // ie the character " in "The"
73*37748cd8SNickeau        "entity", // for instance `...` are transformed in character
74*37748cd8SNickeau        "linebreak",
75*37748cd8SNickeau        "externallink",
76*37748cd8SNickeau        "internallink",
77*37748cd8SNickeau        MediaLink::INTERNAL_MEDIA_CALL_NAME,
78*37748cd8SNickeau        MediaLink::EXTERNAL_MEDIA_CALL_NAME,
79*37748cd8SNickeau        /**
80*37748cd8SNickeau         * The inline of combo
81*37748cd8SNickeau         * TODO: Should be deleted when {@link PluginUtility::renderUnmatched()} is not using the array anymore
82*37748cd8SNickeau         * but is using {@link Call::getDisplay()} instead or any other rewrite
83*37748cd8SNickeau         */
84*37748cd8SNickeau        \syntax_plugin_combo_link::TAG,
85*37748cd8SNickeau        \syntax_plugin_combo_icon::TAG,
86*37748cd8SNickeau        \syntax_plugin_combo_inote::TAG,
87*37748cd8SNickeau        \syntax_plugin_combo_button::TAG,
88*37748cd8SNickeau        \syntax_plugin_combo_tooltip::TAG,
89*37748cd8SNickeau        \syntax_plugin_combo_pipeline::TAG,
90*37748cd8SNickeau    );
91*37748cd8SNickeau
92*37748cd8SNickeau
93*37748cd8SNickeau    const BLOCK_MARKUP_DOKUWIKI_COMPONENTS = array(
94*37748cd8SNickeau        "listu_open", // ul
95*37748cd8SNickeau        "listu_close",
96*37748cd8SNickeau        "listitem_open", //li
97*37748cd8SNickeau        "listitem_close",
98*37748cd8SNickeau        "listcontent_open", // after li ???
99*37748cd8SNickeau        "listcontent_close",
100*37748cd8SNickeau        "table_open",
101*37748cd8SNickeau        "table_close",
102*37748cd8SNickeau    );
103*37748cd8SNickeau
104*37748cd8SNickeau    private $call;
105*37748cd8SNickeau
106*37748cd8SNickeau    /**
107*37748cd8SNickeau     * The key identifier in the {@link CallStack}
108*37748cd8SNickeau     * @var mixed|string
109*37748cd8SNickeau     */
110*37748cd8SNickeau    private $key;
111*37748cd8SNickeau
112*37748cd8SNickeau    /**
113*37748cd8SNickeau     * Call constructor.
114*37748cd8SNickeau     * @param $call - the instruction array (ie called a call)
115*37748cd8SNickeau     */
116*37748cd8SNickeau    public function __construct(&$call, $key = "")
117*37748cd8SNickeau    {
118*37748cd8SNickeau        $this->call = &$call;
119*37748cd8SNickeau        $this->key = $key;
120*37748cd8SNickeau    }
121*37748cd8SNickeau
122*37748cd8SNickeau    /**
123*37748cd8SNickeau     * Insert a tag above
124*37748cd8SNickeau     * @param $tagName
125*37748cd8SNickeau     * @param $state
126*37748cd8SNickeau     * @param $attribute
127*37748cd8SNickeau     * @param $context
128*37748cd8SNickeau     * @param string $content
129*37748cd8SNickeau     * @return Call - a call
130*37748cd8SNickeau     */
131*37748cd8SNickeau    public static function createComboCall($tagName, $state, $attribute = array(), $context = null, $content = '', $payload = null)
132*37748cd8SNickeau    {
133*37748cd8SNickeau        $data = array(
134*37748cd8SNickeau            PluginUtility::ATTRIBUTES => $attribute,
135*37748cd8SNickeau            PluginUtility::CONTEXT => $context,
136*37748cd8SNickeau            PluginUtility::STATE => $state
137*37748cd8SNickeau        );
138*37748cd8SNickeau        if ($payload != null) {
139*37748cd8SNickeau            $data[PluginUtility::PAYLOAD] = $payload;
140*37748cd8SNickeau        }
141*37748cd8SNickeau        $positionInText = null;
142*37748cd8SNickeau
143*37748cd8SNickeau        $call = [
144*37748cd8SNickeau            "plugin",
145*37748cd8SNickeau            array(
146*37748cd8SNickeau                PluginUtility::getComponentName($tagName),
147*37748cd8SNickeau                $data,
148*37748cd8SNickeau                $state,
149*37748cd8SNickeau                $content
150*37748cd8SNickeau            ),
151*37748cd8SNickeau            $positionInText
152*37748cd8SNickeau        ];
153*37748cd8SNickeau        return new Call($call);
154*37748cd8SNickeau    }
155*37748cd8SNickeau
156*37748cd8SNickeau    /**
157*37748cd8SNickeau     * Insert a dokuwiki call
158*37748cd8SNickeau     * @param $callName
159*37748cd8SNickeau     * @param $array
160*37748cd8SNickeau     * @param $positionInText
161*37748cd8SNickeau     * @return Call
162*37748cd8SNickeau     */
163*37748cd8SNickeau    public static function createNativeCall($callName, $array = [], $positionInText = null)
164*37748cd8SNickeau    {
165*37748cd8SNickeau        $call = [
166*37748cd8SNickeau            $callName,
167*37748cd8SNickeau            $array,
168*37748cd8SNickeau            $positionInText
169*37748cd8SNickeau        ];
170*37748cd8SNickeau        return new Call($call);
171*37748cd8SNickeau    }
172*37748cd8SNickeau
173*37748cd8SNickeau    public static function createFromInstruction($instruction)
174*37748cd8SNickeau    {
175*37748cd8SNickeau        return new Call($instruction);
176*37748cd8SNickeau    }
177*37748cd8SNickeau
178*37748cd8SNickeau
179*37748cd8SNickeau    /**
180*37748cd8SNickeau     *
181*37748cd8SNickeau     * Return the tag name from a call array
182*37748cd8SNickeau     *
183*37748cd8SNickeau     * This is not the logical tag.
184*37748cd8SNickeau     * This is much more what's called:
185*37748cd8SNickeau     *   * the component name for a plugin
186*37748cd8SNickeau     *   * or the handler name for dokuwiki
187*37748cd8SNickeau     *
188*37748cd8SNickeau     * For a plugin, this is equivalent
189*37748cd8SNickeau     * to the {@link SyntaxPlugin::getPluginComponent()}
190*37748cd8SNickeau     *
191*37748cd8SNickeau     * This is not the fully qualified component name:
192*37748cd8SNickeau     *   * with the plugin as prefix such as in {@link Call::getComponentName()}
193*37748cd8SNickeau     *   * or with the `open` and `close` prefix such as `p_close` ...
194*37748cd8SNickeau     *
195*37748cd8SNickeau     * @return mixed|string
196*37748cd8SNickeau     */
197*37748cd8SNickeau    public function getTagName()
198*37748cd8SNickeau    {
199*37748cd8SNickeau        $mode = $this->call[0];
200*37748cd8SNickeau        if ($mode != "plugin") {
201*37748cd8SNickeau
202*37748cd8SNickeau            /**
203*37748cd8SNickeau             * This is a standard dokuwiki node
204*37748cd8SNickeau             */
205*37748cd8SNickeau            $dokuWikiNodeName = $this->call[0];
206*37748cd8SNickeau
207*37748cd8SNickeau            /**
208*37748cd8SNickeau             * The dokwuiki node name has also the open and close notion
209*37748cd8SNickeau             * We delete this is not in the doc and therefore not logical
210*37748cd8SNickeau             */
211*37748cd8SNickeau            $tagName = str_replace("_close", "", $dokuWikiNodeName);
212*37748cd8SNickeau            $tagName = str_replace("_open", "", $tagName);
213*37748cd8SNickeau
214*37748cd8SNickeau        } else {
215*37748cd8SNickeau
216*37748cd8SNickeau            /**
217*37748cd8SNickeau             * This is a plugin node
218*37748cd8SNickeau             */
219*37748cd8SNickeau            $pluginDokuData = $this->call[1];
220*37748cd8SNickeau            $component = $pluginDokuData[0];
221*37748cd8SNickeau            if (!is_array($component)) {
222*37748cd8SNickeau                /**
223*37748cd8SNickeau                 * Tag name from class
224*37748cd8SNickeau                 */
225*37748cd8SNickeau                $componentNames = explode("_", $component);
226*37748cd8SNickeau                /**
227*37748cd8SNickeau                 * To take care of
228*37748cd8SNickeau                 * PHP Warning:  sizeof(): Parameter must be an array or an object that implements Countable
229*37748cd8SNickeau                 * in lib/plugins/combo/class/Tag.php on line 314
230*37748cd8SNickeau                 */
231*37748cd8SNickeau                if (is_array($componentNames)) {
232*37748cd8SNickeau                    $tagName = $componentNames[sizeof($componentNames) - 1];
233*37748cd8SNickeau                } else {
234*37748cd8SNickeau                    $tagName = $component;
235*37748cd8SNickeau                }
236*37748cd8SNickeau            } else {
237*37748cd8SNickeau                // To resolve: explode() expects parameter 2 to be string, array given
238*37748cd8SNickeau                LogUtility::msg("The call (" . print_r($this->call, true) . ") has an array and not a string as component (" . print_r($component, true) . "). Page: " . PluginUtility::getPageId(), LogUtility::LVL_MSG_ERROR);
239*37748cd8SNickeau                $tagName = "";
240*37748cd8SNickeau            }
241*37748cd8SNickeau
242*37748cd8SNickeau
243*37748cd8SNickeau        }
244*37748cd8SNickeau        return $tagName;
245*37748cd8SNickeau
246*37748cd8SNickeau    }
247*37748cd8SNickeau
248*37748cd8SNickeau
249*37748cd8SNickeau    /**
250*37748cd8SNickeau     * The parser state
251*37748cd8SNickeau     * @return mixed
252*37748cd8SNickeau     * May be null (example eol, internallink, ...)
253*37748cd8SNickeau     */
254*37748cd8SNickeau    public function getState()
255*37748cd8SNickeau    {
256*37748cd8SNickeau        $mode = $this->call[0];
257*37748cd8SNickeau        if ($mode != "plugin") {
258*37748cd8SNickeau
259*37748cd8SNickeau            /**
260*37748cd8SNickeau             * There is no state because this is a standard
261*37748cd8SNickeau             * dokuwiki syntax found in {@link \Doku_Renderer_xhtml}
262*37748cd8SNickeau             * check if this is not a `...._close` or `...._open`
263*37748cd8SNickeau             * to derive the state
264*37748cd8SNickeau             */
265*37748cd8SNickeau            $mode = $this->call[0];
266*37748cd8SNickeau            $lastPositionSepName = strrpos($mode, "_");
267*37748cd8SNickeau            $closeOrOpen = substr($mode, $lastPositionSepName + 1);
268*37748cd8SNickeau            switch ($closeOrOpen) {
269*37748cd8SNickeau                case "open":
270*37748cd8SNickeau                    return DOKU_LEXER_ENTER;
271*37748cd8SNickeau                case "close":
272*37748cd8SNickeau                    return DOKU_LEXER_EXIT;
273*37748cd8SNickeau                default:
274*37748cd8SNickeau                    return null;
275*37748cd8SNickeau            }
276*37748cd8SNickeau
277*37748cd8SNickeau        } else {
278*37748cd8SNickeau            // Plugin
279*37748cd8SNickeau            $returnedArray = $this->call[1];
280*37748cd8SNickeau            if (array_key_exists(2, $returnedArray)) {
281*37748cd8SNickeau                return $returnedArray[2];
282*37748cd8SNickeau            } else {
283*37748cd8SNickeau                return null;
284*37748cd8SNickeau            }
285*37748cd8SNickeau        }
286*37748cd8SNickeau    }
287*37748cd8SNickeau
288*37748cd8SNickeau    /**
289*37748cd8SNickeau     * @return mixed the data returned from the {@link DokuWiki_Syntax_Plugin::handle} (ie attributes, payload, ...)
290*37748cd8SNickeau     */
291*37748cd8SNickeau    public function &getPluginData()
292*37748cd8SNickeau    {
293*37748cd8SNickeau        return $this->call[1][1];
294*37748cd8SNickeau    }
295*37748cd8SNickeau
296*37748cd8SNickeau    /**
297*37748cd8SNickeau     * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle}
298*37748cd8SNickeau     */
299*37748cd8SNickeau    public function getCapturedContent()
300*37748cd8SNickeau    {
301*37748cd8SNickeau        $caller = $this->call[0];
302*37748cd8SNickeau        switch ($caller) {
303*37748cd8SNickeau            case "plugin":
304*37748cd8SNickeau                return $this->call[1][3];
305*37748cd8SNickeau            case "internallink":
306*37748cd8SNickeau                return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]';
307*37748cd8SNickeau            case "eol":
308*37748cd8SNickeau                return DOKU_LF;
309*37748cd8SNickeau            case "header":
310*37748cd8SNickeau            case "cdata":
311*37748cd8SNickeau                return $this->call[1][0];
312*37748cd8SNickeau            default:
313*37748cd8SNickeau                if (isset($this->call[1][0]) && is_string($this->call[1][0])) {
314*37748cd8SNickeau                    return $this->call[1][0];
315*37748cd8SNickeau                } else {
316*37748cd8SNickeau                    return "";
317*37748cd8SNickeau                }
318*37748cd8SNickeau        }
319*37748cd8SNickeau    }
320*37748cd8SNickeau
321*37748cd8SNickeau
322*37748cd8SNickeau    public function getAttributes()
323*37748cd8SNickeau    {
324*37748cd8SNickeau
325*37748cd8SNickeau        $tagName = $this->getTagName();
326*37748cd8SNickeau        switch ($tagName) {
327*37748cd8SNickeau            case MediaLink::INTERNAL_MEDIA_CALL_NAME:
328*37748cd8SNickeau                return $this->call[1];
329*37748cd8SNickeau            default:
330*37748cd8SNickeau                $data = $this->getPluginData();
331*37748cd8SNickeau                if (isset($data[PluginUtility::ATTRIBUTES])) {
332*37748cd8SNickeau                    return $data[PluginUtility::ATTRIBUTES];
333*37748cd8SNickeau                } else {
334*37748cd8SNickeau                    return null;
335*37748cd8SNickeau                }
336*37748cd8SNickeau        }
337*37748cd8SNickeau    }
338*37748cd8SNickeau
339*37748cd8SNickeau    public function removeAttributes()
340*37748cd8SNickeau    {
341*37748cd8SNickeau
342*37748cd8SNickeau        $data = &$this->getPluginData();
343*37748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES])) {
344*37748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES]);
345*37748cd8SNickeau        }
346*37748cd8SNickeau
347*37748cd8SNickeau    }
348*37748cd8SNickeau
349*37748cd8SNickeau    public function updateToPluginComponent($component, $state, $attributes = array())
350*37748cd8SNickeau    {
351*37748cd8SNickeau        if ($this->call[0] == "plugin") {
352*37748cd8SNickeau            $match = $this->call[1][3];
353*37748cd8SNickeau        } else {
354*37748cd8SNickeau            $this->call[0] = "plugin";
355*37748cd8SNickeau            $match = "";
356*37748cd8SNickeau        }
357*37748cd8SNickeau        $this->call[1] = array(
358*37748cd8SNickeau            0 => $component,
359*37748cd8SNickeau            1 => array(
360*37748cd8SNickeau                PluginUtility::ATTRIBUTES => $attributes,
361*37748cd8SNickeau                PluginUtility::STATE => $state,
362*37748cd8SNickeau            ),
363*37748cd8SNickeau            2 => $state,
364*37748cd8SNickeau            3 => $match
365*37748cd8SNickeau        );
366*37748cd8SNickeau
367*37748cd8SNickeau    }
368*37748cd8SNickeau
369*37748cd8SNickeau    public function getDisplay()
370*37748cd8SNickeau    {
371*37748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
372*37748cd8SNickeau            /**
373*37748cd8SNickeau             * Unmatched are content (ie text node in XML/HTML) and have
374*37748cd8SNickeau             * no display
375*37748cd8SNickeau             */
376*37748cd8SNickeau            return Call::INLINE_DISPLAY;
377*37748cd8SNickeau        } else {
378*37748cd8SNickeau            $mode = $this->call[0];
379*37748cd8SNickeau            if ($mode == "plugin") {
380*37748cd8SNickeau                global $DOKU_PLUGINS;
381*37748cd8SNickeau                $component = $this->getComponentName();
382*37748cd8SNickeau                /**
383*37748cd8SNickeau                 * @var SyntaxPlugin $syntaxPlugin
384*37748cd8SNickeau                 */
385*37748cd8SNickeau                $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component];
386*37748cd8SNickeau                $pType = $syntaxPlugin->getPType();
387*37748cd8SNickeau                switch ($pType) {
388*37748cd8SNickeau                    case "normal":
389*37748cd8SNickeau                        return Call::INLINE_DISPLAY;
390*37748cd8SNickeau                    case "block":
391*37748cd8SNickeau                    case "stack":
392*37748cd8SNickeau                        return Call::BlOCK_DISPLAY;
393*37748cd8SNickeau                    default:
394*37748cd8SNickeau                        LogUtility::msg("The ptype (" . $pType . ") is unknown.");
395*37748cd8SNickeau                        return null;
396*37748cd8SNickeau                }
397*37748cd8SNickeau            } else {
398*37748cd8SNickeau                if ($mode == "eol") {
399*37748cd8SNickeau                    /**
400*37748cd8SNickeau                     * Control character
401*37748cd8SNickeau                     * We return it as it's used in the
402*37748cd8SNickeau                     * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()}
403*37748cd8SNickeau                     * to create the paragraph
404*37748cd8SNickeau                     * This is not a block, nor an inline
405*37748cd8SNickeau                     */
406*37748cd8SNickeau                    return $mode;
407*37748cd8SNickeau                }
408*37748cd8SNickeau
409*37748cd8SNickeau                if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) {
410*37748cd8SNickeau                    return Call::INLINE_DISPLAY;
411*37748cd8SNickeau                }
412*37748cd8SNickeau
413*37748cd8SNickeau                if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) {
414*37748cd8SNickeau                    return Call::BlOCK_DISPLAY;
415*37748cd8SNickeau                }
416*37748cd8SNickeau
417*37748cd8SNickeau                LogUtility::msg("The display of the call with the mode " . $mode . " is unknown");
418*37748cd8SNickeau                return null;
419*37748cd8SNickeau
420*37748cd8SNickeau
421*37748cd8SNickeau            }
422*37748cd8SNickeau        }
423*37748cd8SNickeau
424*37748cd8SNickeau    }
425*37748cd8SNickeau
426*37748cd8SNickeau    /**
427*37748cd8SNickeau     * Same as {@link Call::getTagName()}
428*37748cd8SNickeau     * but fully qualified
429*37748cd8SNickeau     * @return string
430*37748cd8SNickeau     */
431*37748cd8SNickeau    public function getComponentName()
432*37748cd8SNickeau    {
433*37748cd8SNickeau        $mode = $this->call[0];
434*37748cd8SNickeau        if ($mode == "plugin") {
435*37748cd8SNickeau            $pluginDokuData = $this->call[1];
436*37748cd8SNickeau            return $pluginDokuData[0];
437*37748cd8SNickeau        } else {
438*37748cd8SNickeau            return $mode;
439*37748cd8SNickeau        }
440*37748cd8SNickeau    }
441*37748cd8SNickeau
442*37748cd8SNickeau    public function updateEolToSpace()
443*37748cd8SNickeau    {
444*37748cd8SNickeau        $mode = $this->call[0];
445*37748cd8SNickeau        if ($mode != "eol") {
446*37748cd8SNickeau            LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support");
447*37748cd8SNickeau        } else {
448*37748cd8SNickeau            $this->call[0] = "cdata";
449*37748cd8SNickeau            $this->call[1] = array(
450*37748cd8SNickeau                0 => " "
451*37748cd8SNickeau            );
452*37748cd8SNickeau        }
453*37748cd8SNickeau
454*37748cd8SNickeau    }
455*37748cd8SNickeau
456*37748cd8SNickeau    public function addAttribute($key, $value)
457*37748cd8SNickeau    {
458*37748cd8SNickeau        $mode = $this->call[0];
459*37748cd8SNickeau        if ($mode == "plugin") {
460*37748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value;
461*37748cd8SNickeau        } else {
462*37748cd8SNickeau            LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
463*37748cd8SNickeau        }
464*37748cd8SNickeau    }
465*37748cd8SNickeau
466*37748cd8SNickeau    public function getContext()
467*37748cd8SNickeau    {
468*37748cd8SNickeau        $mode = $this->call[0];
469*37748cd8SNickeau        if ($mode == "plugin") {
470*37748cd8SNickeau            return $this->call[1][1][PluginUtility::CONTEXT];
471*37748cd8SNickeau        } else {
472*37748cd8SNickeau            LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support");
473*37748cd8SNickeau            return null;
474*37748cd8SNickeau        }
475*37748cd8SNickeau    }
476*37748cd8SNickeau
477*37748cd8SNickeau    /**
478*37748cd8SNickeau     *
479*37748cd8SNickeau     * @return array
480*37748cd8SNickeau     */
481*37748cd8SNickeau    public function toCallArray()
482*37748cd8SNickeau    {
483*37748cd8SNickeau        return $this->call;
484*37748cd8SNickeau    }
485*37748cd8SNickeau
486*37748cd8SNickeau    public function __toString()
487*37748cd8SNickeau    {
488*37748cd8SNickeau        $name = $this->key;
489*37748cd8SNickeau        if (!empty($name)) {
490*37748cd8SNickeau            $name .= " - ";
491*37748cd8SNickeau        }
492*37748cd8SNickeau        $name .= $this->getTagName();
493*37748cd8SNickeau        return $name;
494*37748cd8SNickeau    }
495*37748cd8SNickeau
496*37748cd8SNickeau    public function getType()
497*37748cd8SNickeau    {
498*37748cd8SNickeau        if ($this->getState() == DOKU_LEXER_UNMATCHED) {
499*37748cd8SNickeau            LogUtility::msg("The unmatched tag ($this) does not have any attributes. Get its parent if you want the type", LogUtility::LVL_MSG_ERROR);
500*37748cd8SNickeau            return null;
501*37748cd8SNickeau        } else {
502*37748cd8SNickeau            /**
503*37748cd8SNickeau             * don't use {@link Call::getAttribute()} to get the type
504*37748cd8SNickeau             * as this function stack also depends on
505*37748cd8SNickeau             * this function {@link Call::getType()}
506*37748cd8SNickeau             * to return the value
507*37748cd8SNickeau             * Ie: if this is a boolean attribute without specified type
508*37748cd8SNickeau             * if the boolean value is in the type, we return it
509*37748cd8SNickeau             */
510*37748cd8SNickeau            return $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY];
511*37748cd8SNickeau        }
512*37748cd8SNickeau    }
513*37748cd8SNickeau
514*37748cd8SNickeau    /**
515*37748cd8SNickeau     * @param $key
516*37748cd8SNickeau     * @param null $default
517*37748cd8SNickeau     * @return string|null
518*37748cd8SNickeau     */
519*37748cd8SNickeau    public function getAttribute($key, $default = null)
520*37748cd8SNickeau    {
521*37748cd8SNickeau        $attributes = $this->getAttributes();
522*37748cd8SNickeau        if (isset($attributes[$key])) {
523*37748cd8SNickeau            return $attributes[$key];
524*37748cd8SNickeau        } else {
525*37748cd8SNickeau            // boolean attribute
526*37748cd8SNickeau            if ($this->getType() == $key) {
527*37748cd8SNickeau                return true;
528*37748cd8SNickeau            } else {
529*37748cd8SNickeau                return $default;
530*37748cd8SNickeau            }
531*37748cd8SNickeau        }
532*37748cd8SNickeau    }
533*37748cd8SNickeau
534*37748cd8SNickeau    public function getPayload()
535*37748cd8SNickeau    {
536*37748cd8SNickeau        $mode = $this->call[0];
537*37748cd8SNickeau        if ($mode == "plugin") {
538*37748cd8SNickeau            return $this->call[1][1][PluginUtility::PAYLOAD];
539*37748cd8SNickeau        } else {
540*37748cd8SNickeau            LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support");
541*37748cd8SNickeau            return null;
542*37748cd8SNickeau        }
543*37748cd8SNickeau    }
544*37748cd8SNickeau
545*37748cd8SNickeau    public function setContext($value)
546*37748cd8SNickeau    {
547*37748cd8SNickeau        $this->call[1][1][PluginUtility::CONTEXT] = $value;
548*37748cd8SNickeau        return $this;
549*37748cd8SNickeau    }
550*37748cd8SNickeau
551*37748cd8SNickeau    public function hasAttribute($attributeName)
552*37748cd8SNickeau    {
553*37748cd8SNickeau        $attributes = $this->getAttributes();
554*37748cd8SNickeau        if (isset($attributes[$attributeName])) {
555*37748cd8SNickeau            return true;
556*37748cd8SNickeau        } else {
557*37748cd8SNickeau            if ($this->getType() == $attributeName) {
558*37748cd8SNickeau                return true;
559*37748cd8SNickeau            } else {
560*37748cd8SNickeau                return false;
561*37748cd8SNickeau            }
562*37748cd8SNickeau        }
563*37748cd8SNickeau    }
564*37748cd8SNickeau
565*37748cd8SNickeau    public function isPluginCall()
566*37748cd8SNickeau    {
567*37748cd8SNickeau        return $this->call[0] === "plugin";
568*37748cd8SNickeau    }
569*37748cd8SNickeau
570*37748cd8SNickeau    /**
571*37748cd8SNickeau     * @return mixed|string the position (ie key) in the array
572*37748cd8SNickeau     */
573*37748cd8SNickeau    public function getKey()
574*37748cd8SNickeau    {
575*37748cd8SNickeau        return $this->key;
576*37748cd8SNickeau    }
577*37748cd8SNickeau
578*37748cd8SNickeau    public function &getCall()
579*37748cd8SNickeau    {
580*37748cd8SNickeau        return $this->call;
581*37748cd8SNickeau    }
582*37748cd8SNickeau
583*37748cd8SNickeau    public function setState($state)
584*37748cd8SNickeau    {
585*37748cd8SNickeau        if ($this->call[0] == "plugin") {
586*37748cd8SNickeau            // for dokuwiki
587*37748cd8SNickeau            $this->call[1][2] = $state;
588*37748cd8SNickeau            // for the combo plugin if any
589*37748cd8SNickeau            if (isset($this->call[1][1][PluginUtility::STATE])) {
590*37748cd8SNickeau                $this->call[1][1][PluginUtility::STATE] = $state;
591*37748cd8SNickeau            }
592*37748cd8SNickeau        } else {
593*37748cd8SNickeau            LogUtility::msg("This modification of state is not yet supported for a native call");
594*37748cd8SNickeau        }
595*37748cd8SNickeau    }
596*37748cd8SNickeau
597*37748cd8SNickeau
598*37748cd8SNickeau    /**
599*37748cd8SNickeau     * Return the position of the first matched character in the text file
600*37748cd8SNickeau     * @return mixed
601*37748cd8SNickeau     */
602*37748cd8SNickeau    public function getFirstMatchedCharacterPosition()
603*37748cd8SNickeau    {
604*37748cd8SNickeau
605*37748cd8SNickeau        return $this->call[2];
606*37748cd8SNickeau
607*37748cd8SNickeau    }
608*37748cd8SNickeau
609*37748cd8SNickeau    /**
610*37748cd8SNickeau     * Return the position of the last matched character in the text file
611*37748cd8SNickeau     *
612*37748cd8SNickeau     * This is the {@link Call::getFirstMatchedCharacterPosition()}
613*37748cd8SNickeau     * plus the length of the {@link Call::getCapturedContent()}
614*37748cd8SNickeau     * matched content
615*37748cd8SNickeau     * @return int|mixed
616*37748cd8SNickeau     */
617*37748cd8SNickeau    public function getLastMatchedCharacterPosition()
618*37748cd8SNickeau    {
619*37748cd8SNickeau        return $this->getFirstMatchedCharacterPosition() + strlen($this->getCapturedContent());
620*37748cd8SNickeau    }
621*37748cd8SNickeau
622*37748cd8SNickeau    /**
623*37748cd8SNickeau     * @param $value string the class string to add
624*37748cd8SNickeau     * @return Call
625*37748cd8SNickeau     */
626*37748cd8SNickeau    public function addClassName($value)
627*37748cd8SNickeau    {
628*37748cd8SNickeau        $class = $this->getAttribute("class");
629*37748cd8SNickeau        if ($class != null) {
630*37748cd8SNickeau            $value = "$class $value";
631*37748cd8SNickeau        }
632*37748cd8SNickeau        $this->addAttribute("class", $value);
633*37748cd8SNickeau        return $this;
634*37748cd8SNickeau
635*37748cd8SNickeau    }
636*37748cd8SNickeau
637*37748cd8SNickeau    /**
638*37748cd8SNickeau     * @param $key
639*37748cd8SNickeau     * @return mixed|null - the delete value of null if not found
640*37748cd8SNickeau     */
641*37748cd8SNickeau    public function removeAttribute($key)
642*37748cd8SNickeau    {
643*37748cd8SNickeau
644*37748cd8SNickeau        $data = &$this->getPluginData();
645*37748cd8SNickeau        if (isset($data[PluginUtility::ATTRIBUTES][$key])) {
646*37748cd8SNickeau            $value = $data[PluginUtility::ATTRIBUTES][$key];
647*37748cd8SNickeau            unset($data[PluginUtility::ATTRIBUTES][$key]);
648*37748cd8SNickeau            return $value;
649*37748cd8SNickeau        } else {
650*37748cd8SNickeau            // boolean attribute as first attribute
651*37748cd8SNickeau            if ($this->getType() == $key) {
652*37748cd8SNickeau                unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]);
653*37748cd8SNickeau                return true;
654*37748cd8SNickeau            }
655*37748cd8SNickeau            return null;
656*37748cd8SNickeau        }
657*37748cd8SNickeau
658*37748cd8SNickeau    }
659*37748cd8SNickeau
660*37748cd8SNickeau    public function setPayload($text)
661*37748cd8SNickeau    {
662*37748cd8SNickeau        if ($this->isPluginCall()) {
663*37748cd8SNickeau            $this->call[1][1][PluginUtility::PAYLOAD] = $text;
664*37748cd8SNickeau        } else {
665*37748cd8SNickeau            LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented");
666*37748cd8SNickeau        }
667*37748cd8SNickeau    }
668*37748cd8SNickeau
669*37748cd8SNickeau    /**
670*37748cd8SNickeau     * @return bool true if the call is a text call (same as dom text node)
671*37748cd8SNickeau     */
672*37748cd8SNickeau    public function isTextCall()
673*37748cd8SNickeau    {
674*37748cd8SNickeau        return (
675*37748cd8SNickeau            $this->getState() == DOKU_LEXER_UNMATCHED ||
676*37748cd8SNickeau            $this->getTagName() == "cdata" ||
677*37748cd8SNickeau            $this->getTagName() == "acronym"
678*37748cd8SNickeau        );
679*37748cd8SNickeau    }
680*37748cd8SNickeau
681*37748cd8SNickeau    public function setType($type)
682*37748cd8SNickeau    {
683*37748cd8SNickeau        if ($this->isPluginCall()) {
684*37748cd8SNickeau            $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type;
685*37748cd8SNickeau        } else {
686*37748cd8SNickeau            LogUtility::msg("This is not a plugin call ($this), you can't set the type");
687*37748cd8SNickeau        }
688*37748cd8SNickeau    }
689*37748cd8SNickeau
690*37748cd8SNickeau    public function addCssStyle($key, $value)
691*37748cd8SNickeau    {
692*37748cd8SNickeau        $style = $this->getAttribute("style");
693*37748cd8SNickeau        $cssValue = "$key:$value";
694*37748cd8SNickeau        if ($style != null) {
695*37748cd8SNickeau            $cssValue = "$style; $cssValue";
696*37748cd8SNickeau        }
697*37748cd8SNickeau        $this->addAttribute("style", $cssValue);
698*37748cd8SNickeau    }
699*37748cd8SNickeau
700*37748cd8SNickeau    public function setSyntaxComponentFromTag($tag)
701*37748cd8SNickeau    {
702*37748cd8SNickeau
703*37748cd8SNickeau        if ($this->isPluginCall()) {
704*37748cd8SNickeau            $this->call[1][0] = PluginUtility::getComponentName($tag);
705*37748cd8SNickeau        } else {
706*37748cd8SNickeau            LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)");
707*37748cd8SNickeau        }
708*37748cd8SNickeau    }
709*37748cd8SNickeau
710*37748cd8SNickeau    /**
711*37748cd8SNickeau     * @param Page $page
712*37748cd8SNickeau     * @return Call
713*37748cd8SNickeau     */
714*37748cd8SNickeau    public function render(Page $page)
715*37748cd8SNickeau    {
716*37748cd8SNickeau        return $this->renderFromData(TemplateUtility::getMetadataDataFromPage($page));
717*37748cd8SNickeau    }
718*37748cd8SNickeau
719*37748cd8SNickeau    public function renderFromData(array $array)
720*37748cd8SNickeau    {
721*37748cd8SNickeau        $state = $this->getState();
722*37748cd8SNickeau        if ($state == DOKU_LEXER_UNMATCHED) {
723*37748cd8SNickeau            if ($this->isPluginCall()) {
724*37748cd8SNickeau                $payload = $this->getPayload();
725*37748cd8SNickeau                if (!empty($payload)) {
726*37748cd8SNickeau                    $this->setPayload(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
727*37748cd8SNickeau                }
728*37748cd8SNickeau            }
729*37748cd8SNickeau        } else {
730*37748cd8SNickeau            $tagName = $this->getTagName();
731*37748cd8SNickeau            switch ($tagName) {
732*37748cd8SNickeau                case "eol":
733*37748cd8SNickeau                    break;
734*37748cd8SNickeau                case "cdata":
735*37748cd8SNickeau                    $payload = $this->getCapturedContent();
736*37748cd8SNickeau                    $this->setCapturedContent(TemplateUtility::renderStringTemplateFromDataArray($payload, $array));
737*37748cd8SNickeau                    break;
738*37748cd8SNickeau                case \syntax_plugin_combo_pipeline::TAG:
739*37748cd8SNickeau                    $pageTemplate = PluginUtility::getTagContent($this->getCapturedContent());
740*37748cd8SNickeau                    $script = TemplateUtility::renderStringTemplateFromDataArray($pageTemplate, $array);
741*37748cd8SNickeau                    $string = PipelineUtility::execute($script);
742*37748cd8SNickeau                    $this->setPayload($string);
743*37748cd8SNickeau                    break;
744*37748cd8SNickeau                case \syntax_plugin_combo_link::TAG:
745*37748cd8SNickeau                    switch ($this->getState()) {
746*37748cd8SNickeau                        case DOKU_LEXER_ENTER:
747*37748cd8SNickeau                            $ref = $this->getAttribute("ref");
748*37748cd8SNickeau                            $this->addAttribute("ref", TemplateUtility::renderStringTemplateFromDataArray($ref, $array));
749*37748cd8SNickeau                            break;
750*37748cd8SNickeau                    }
751*37748cd8SNickeau                    break;
752*37748cd8SNickeau            }
753*37748cd8SNickeau        }
754*37748cd8SNickeau        return $this;
755*37748cd8SNickeau    }
756*37748cd8SNickeau
757*37748cd8SNickeau    public function setCapturedContent($content)
758*37748cd8SNickeau    {
759*37748cd8SNickeau        $tagName = $this->getTagName();
760*37748cd8SNickeau        switch ($tagName) {
761*37748cd8SNickeau            case "cdata":
762*37748cd8SNickeau                $this->call[1][0] = $content;
763*37748cd8SNickeau                break;
764*37748cd8SNickeau            default:
765*37748cd8SNickeau                LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR);
766*37748cd8SNickeau        }
767*37748cd8SNickeau    }
768*37748cd8SNickeau
769*37748cd8SNickeau
770*37748cd8SNickeau}
771