1531e725cSNickeau<?php 2531e725cSNickeau 3531e725cSNickeauuse ComboStrap\CallStack; 4*04fd306cSNickeauuse ComboStrap\HeadingTag; 5531e725cSNickeauuse ComboStrap\LogUtility; 6531e725cSNickeauuse ComboStrap\PluginUtility; 7*04fd306cSNickeauuse ComboStrap\Site; 8*04fd306cSNickeauuse ComboStrap\SiteConfig; 9531e725cSNickeauuse ComboStrap\TagAttributes; 10531e725cSNickeau 11531e725cSNickeau 12531e725cSNickeau/** 13531e725cSNickeau * Class headingwiki 14531e725cSNickeau * Taking over {@link \dokuwiki\Parsing\ParserMode\Header} 15531e725cSNickeau */ 16531e725cSNickeauclass syntax_plugin_combo_headingwiki extends DokuWiki_Syntax_Plugin 17531e725cSNickeau{ 18531e725cSNickeau 19531e725cSNickeau /** 20531e725cSNickeau * Header pattern 21531e725cSNickeau * * Dokuwiki does not made a space mandatory after and before the opening an closing `=` character 22531e725cSNickeau * * No line break in the look ahead 23531e725cSNickeau * * The capture of the first spaces should be optional otherwise the {@link \dokuwiki\Parsing\ParserMode\Header} is taking over 24531e725cSNickeau * 25531e725cSNickeau * See also for information, 26531e725cSNickeau * the original heading pattern of Dokuwiki {@link \dokuwiki\Parsing\ParserMode\Header} 27531e725cSNickeau */ 28531e725cSNickeau const ENTRY_PATTERN = '[ \t]*={1,6}(?=[^\n]*={1,6}\s*\r??\n)'; 29531e725cSNickeau const EXIT_PATTERN = '={1,6}\s*(?=\r??\n)'; 30531e725cSNickeau const TAG = "headingwiki"; 31531e725cSNickeau 32531e725cSNickeau const CONF_WIKI_HEADING_ENABLE = "headingWikiEnable"; 33531e725cSNickeau const CONF_DEFAULT_WIKI_ENABLE_VALUE = 1; 34531e725cSNickeau 35*04fd306cSNickeau 36*04fd306cSNickeau /** 37*04fd306cSNickeau * When we takes over the dokuwiki heading 38*04fd306cSNickeau * we are also taking over the sectioning 39*04fd306cSNickeau * and allows {@link syntax_plugin_combo_section} 40*04fd306cSNickeau * @return int - 1 or 0 41*04fd306cSNickeau */ 42*04fd306cSNickeau public static function isEnabled(): int 43*04fd306cSNickeau { 44*04fd306cSNickeau return SiteConfig::getConfValue(self::CONF_WIKI_HEADING_ENABLE, self::CONF_DEFAULT_WIKI_ENABLE_VALUE); 45*04fd306cSNickeau } 46*04fd306cSNickeau 474cadd4f8SNickeau public function getSort(): int 48531e725cSNickeau { 49531e725cSNickeau /** 50531e725cSNickeau * It's 49 (on less than the original heading) 51531e725cSNickeau * {@link \dokuwiki\Parsing\ParserMode\Header::getSort()} 52531e725cSNickeau */ 53531e725cSNickeau return 49; 54531e725cSNickeau } 55531e725cSNickeau 564cadd4f8SNickeau public function getType(): string 57531e725cSNickeau { 58*04fd306cSNickeau return HeadingTag::SYNTAX_TYPE; 59531e725cSNickeau } 60531e725cSNickeau 61531e725cSNickeau 62531e725cSNickeau /** 63531e725cSNickeau * 64531e725cSNickeau * How Dokuwiki will add P element 65531e725cSNickeau * 66531e725cSNickeau * * 'normal' - The plugin can be used inside paragraphs (inline) 67531e725cSNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 68531e725cSNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 69531e725cSNickeau * 70531e725cSNickeau * @see DokuWiki_Syntax_Plugin::getPType() 71531e725cSNickeau * 72531e725cSNickeau * This is the equivalent of inline or block for css 73531e725cSNickeau */ 744cadd4f8SNickeau public function getPType(): string 75531e725cSNickeau { 76*04fd306cSNickeau return HeadingTag::SYNTAX_PTYPE; 77531e725cSNickeau } 78531e725cSNickeau 79531e725cSNickeau /** 80531e725cSNickeau * @return array 81531e725cSNickeau * Allow which kind of plugin inside 82531e725cSNickeau * 83531e725cSNickeau * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 84531e725cSNickeau * because we manage self the content and we call self the parser 85531e725cSNickeau * 86531e725cSNickeau * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 87531e725cSNickeau */ 884cadd4f8SNickeau function getAllowedTypes(): array 89531e725cSNickeau { 90531e725cSNickeau return array('formatting', 'substition', 'protected', 'disabled'); 91531e725cSNickeau } 92531e725cSNickeau 93531e725cSNickeau 94531e725cSNickeau public function connectTo($mode) 95531e725cSNickeau { 96531e725cSNickeau if ($this->enableWikiHeading($mode)) { 979337a630SNickeau $this->Lexer->addEntryPattern(self::ENTRY_PATTERN, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 98531e725cSNickeau } 99531e725cSNickeau } 100531e725cSNickeau 101531e725cSNickeau public function postConnect() 102531e725cSNickeau { 103531e725cSNickeau 1049337a630SNickeau $this->Lexer->addExitPattern(self::EXIT_PATTERN, PluginUtility::getModeFromTag($this->getPluginComponent())); 105531e725cSNickeau 106531e725cSNickeau } 107531e725cSNickeau 108531e725cSNickeau 109531e725cSNickeau /** 110531e725cSNickeau * Handle the syntax 111531e725cSNickeau * 112531e725cSNickeau * At the end of the parser, the `section_open` and `section_close` calls 113*04fd306cSNickeau * are created in {@link action_plugin_combo_instructionspostprocessing} 114531e725cSNickeau * and the text inside for the toc is captured 115531e725cSNickeau * 116531e725cSNickeau * @param string $match 117531e725cSNickeau * @param int $state 118531e725cSNickeau * @param int $pos 119531e725cSNickeau * @param Doku_Handler $handler 120531e725cSNickeau * @return array 121531e725cSNickeau */ 1224cadd4f8SNickeau public function handle($match, $state, $pos, Doku_Handler $handler): array 123531e725cSNickeau { 124531e725cSNickeau switch ($state) { 125531e725cSNickeau 126531e725cSNickeau case DOKU_LEXER_ENTER: 127531e725cSNickeau /** 128531e725cSNickeau * Title regexp 129531e725cSNickeau */ 130*04fd306cSNickeau $level = $this->getLevelFromMatch($match); 131531e725cSNickeau 132*04fd306cSNickeau $attributes = TagAttributes::createEmpty(self::TAG) 133*04fd306cSNickeau ->addComponentAttributeValue(HeadingTag::LEVEL,$level) 134*04fd306cSNickeau ->toCallStackArray(); 135*04fd306cSNickeau 136*04fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 137*04fd306cSNickeau $context = HeadingTag::getContext($callStack); 138531e725cSNickeau 139531e725cSNickeau return array( 140531e725cSNickeau PluginUtility::STATE => $state, 141531e725cSNickeau PluginUtility::ATTRIBUTES => $attributes, 142531e725cSNickeau PluginUtility::CONTEXT => $context, 143531e725cSNickeau PluginUtility::POSITION => $pos 144531e725cSNickeau ); 145531e725cSNickeau case DOKU_LEXER_UNMATCHED : 146531e725cSNickeau 147531e725cSNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 148531e725cSNickeau 149531e725cSNickeau case DOKU_LEXER_EXIT : 150531e725cSNickeau 151*04fd306cSNickeau $returnedData = HeadingTag::handleExit($handler); 152531e725cSNickeau 153531e725cSNickeau /** 154531e725cSNickeau * Control of the Number of `=` before and after 155531e725cSNickeau */ 156*04fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 157531e725cSNickeau $callStack->moveToEnd(); 158531e725cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 159531e725cSNickeau $levelFromMatch = $this->getLevelFromMatch($match); 160*04fd306cSNickeau $levelFromStartTag = $openingTag->getAttribute(HeadingTag::LEVEL); 161531e725cSNickeau if ($levelFromMatch != $levelFromStartTag) { 162531e725cSNickeau $content = ""; 163531e725cSNickeau while ($actualCall = $callStack->next()) { 164531e725cSNickeau $content .= $actualCall->getCapturedContent(); 165531e725cSNickeau } 166*04fd306cSNickeau LogUtility::msg("The number of `=` character for a wiki heading is not the same before ($levelFromStartTag) and after ($levelFromMatch) the content ($content).", LogUtility::LVL_MSG_INFO, HeadingTag::CANONICAL); 167531e725cSNickeau } 168531e725cSNickeau 169531e725cSNickeau return $returnedData; 170531e725cSNickeau 171531e725cSNickeau } 172531e725cSNickeau return array(); 173531e725cSNickeau } 174531e725cSNickeau 175c3437056SNickeau public function render($format, Doku_Renderer $renderer, $data): bool 176531e725cSNickeau { 177531e725cSNickeau 178*04fd306cSNickeau switch ($format) { 179*04fd306cSNickeau case "xhtml": 180531e725cSNickeau /** 181531e725cSNickeau * @var Doku_Renderer_xhtml $renderer 182531e725cSNickeau */ 183531e725cSNickeau $state = $data[PluginUtility::STATE]; 184*04fd306cSNickeau $context = $data[PluginUtility::CONTEXT]; 185531e725cSNickeau switch ($state) { 186531e725cSNickeau 187531e725cSNickeau case DOKU_LEXER_ENTER: 188531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 189*04fd306cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($callStackArray, HeadingTag::HEADING_TAG); 190531e725cSNickeau $pos = $data[PluginUtility::POSITION]; 191*04fd306cSNickeau HeadingTag::processRenderEnterXhtml($context, $tagAttributes, $renderer, $pos); 192531e725cSNickeau return true; 193531e725cSNickeau case DOKU_LEXER_UNMATCHED: 194531e725cSNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 195531e725cSNickeau return true; 196531e725cSNickeau case DOKU_LEXER_EXIT: 197531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 198531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($callStackArray); 199*04fd306cSNickeau $renderer->doc .= HeadingTag::renderClosingTag($tagAttributes, $context); 200531e725cSNickeau return true; 201531e725cSNickeau 202531e725cSNickeau } 203*04fd306cSNickeau return false; 204*04fd306cSNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 205531e725cSNickeau 206531e725cSNickeau /** 207531e725cSNickeau * @var renderer_plugin_combo_analytics $renderer 208531e725cSNickeau */ 209*04fd306cSNickeau HeadingTag::processMetadataAnalytics($data, $renderer); 210*04fd306cSNickeau return true; 211531e725cSNickeau 212*04fd306cSNickeau case "metadata": 213531e725cSNickeau 214531e725cSNickeau /** 215531e725cSNickeau * @var Doku_Renderer_metadata $renderer 216531e725cSNickeau */ 217*04fd306cSNickeau HeadingTag::processHeadingEnterMetadata($data, $renderer); 218*04fd306cSNickeau return true; 219531e725cSNickeau 220*04fd306cSNickeau case renderer_plugin_combo_xml::FORMAT: 221c3437056SNickeau $state = $data[PluginUtility::STATE]; 222c3437056SNickeau switch ($state) { 223c3437056SNickeau case DOKU_LEXER_ENTER: 224*04fd306cSNickeau $level = $data[PluginUtility::ATTRIBUTES][HeadingTag::LEVEL]; 225c3437056SNickeau $renderer->doc .= "<h$level>"; 226*04fd306cSNickeau return true; 227c3437056SNickeau case DOKU_LEXER_UNMATCHED: 228c3437056SNickeau $renderer->doc .= PluginUtility::renderUnmatchedXml($data); 229*04fd306cSNickeau return true; 230c3437056SNickeau case DOKU_LEXER_EXIT: 231*04fd306cSNickeau $level = $data[PluginUtility::ATTRIBUTES][HeadingTag::LEVEL]; 232c3437056SNickeau $renderer->doc .= "</h$level>"; 233*04fd306cSNickeau return true; 234c3437056SNickeau 235c3437056SNickeau } 236531e725cSNickeau return false; 237*04fd306cSNickeau default: 238*04fd306cSNickeau return false; 239*04fd306cSNickeau } 240*04fd306cSNickeau 241531e725cSNickeau } 242531e725cSNickeau 243531e725cSNickeau /** 244531e725cSNickeau * @param $match 245531e725cSNickeau * @return int 246531e725cSNickeau */ 247531e725cSNickeau public 248*04fd306cSNickeau function getLevelFromMatch($match): int 249531e725cSNickeau { 250531e725cSNickeau return 7 - strlen(trim($match)); 251531e725cSNickeau } 252531e725cSNickeau 253531e725cSNickeau 254531e725cSNickeau private 255531e725cSNickeau function enableWikiHeading($mode) 256531e725cSNickeau { 257531e725cSNickeau 258531e725cSNickeau 259531e725cSNickeau /** 260531e725cSNickeau * Basically all mode that are not `base` 261531e725cSNickeau * To not take the dokuwiki heading 262531e725cSNickeau */ 263531e725cSNickeau if (!(in_array($mode, ['base', 'header', 'table']))) { 264531e725cSNickeau return true; 265531e725cSNickeau } else { 266*04fd306cSNickeau return SiteConfig::getConfValue(self::CONF_WIKI_HEADING_ENABLE, self::CONF_DEFAULT_WIKI_ENABLE_VALUE); 267531e725cSNickeau } 268531e725cSNickeau 269531e725cSNickeau 270531e725cSNickeau } 271531e725cSNickeau 272531e725cSNickeau 273531e725cSNickeau} 274