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