1531e725cSNickeau<?php 2531e725cSNickeau 3531e725cSNickeauuse ComboStrap\CallStack; 4*034808a5SNicouse ComboStrap\ExceptionNotFound; 504fd306cSNickeauuse ComboStrap\HeadingTag; 6531e725cSNickeauuse ComboStrap\LogUtility; 7531e725cSNickeauuse ComboStrap\PluginUtility; 804fd306cSNickeauuse 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 3504fd306cSNickeau 3604fd306cSNickeau /** 3704fd306cSNickeau * When we takes over the dokuwiki heading 3804fd306cSNickeau * we are also taking over the sectioning 3904fd306cSNickeau * and allows {@link syntax_plugin_combo_section} 4004fd306cSNickeau * @return int - 1 or 0 4104fd306cSNickeau */ 4204fd306cSNickeau public static function isEnabled(): int 4304fd306cSNickeau { 4404fd306cSNickeau return SiteConfig::getConfValue(self::CONF_WIKI_HEADING_ENABLE, self::CONF_DEFAULT_WIKI_ENABLE_VALUE); 4504fd306cSNickeau } 4604fd306cSNickeau 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 { 5804fd306cSNickeau 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 { 7604fd306cSNickeau 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 11304fd306cSNickeau * 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 */ 13004fd306cSNickeau $level = $this->getLevelFromMatch($match); 131531e725cSNickeau 132*034808a5SNico 133*034808a5SNico $tagAttributes = TagAttributes::createEmpty(self::TAG) 134*034808a5SNico ->addComponentAttributeValue(HeadingTag::LEVEL, $level); 135*034808a5SNico 136*034808a5SNico if ($level === 1) { 137*034808a5SNico try { 138*034808a5SNico $tagAttributes->addComponentAttributeValueIfNotEmpty("id", HeadingTag::getIdForLevel1()); 139*034808a5SNico } catch (ExceptionNotFound $e) { 140*034808a5SNico // dynamic execution 141*034808a5SNico } 142*034808a5SNico } 143*034808a5SNico 144*034808a5SNico $attributes = $tagAttributes->toCallStackArray(); 14504fd306cSNickeau 14604fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 14704fd306cSNickeau $context = HeadingTag::getContext($callStack); 148531e725cSNickeau 149531e725cSNickeau return array( 150531e725cSNickeau PluginUtility::STATE => $state, 151531e725cSNickeau PluginUtility::ATTRIBUTES => $attributes, 152531e725cSNickeau PluginUtility::CONTEXT => $context, 153531e725cSNickeau PluginUtility::POSITION => $pos 154531e725cSNickeau ); 155531e725cSNickeau case DOKU_LEXER_UNMATCHED : 156531e725cSNickeau 157531e725cSNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 158531e725cSNickeau 159531e725cSNickeau case DOKU_LEXER_EXIT : 160531e725cSNickeau 16104fd306cSNickeau $returnedData = HeadingTag::handleExit($handler); 162531e725cSNickeau 163531e725cSNickeau /** 164531e725cSNickeau * Control of the Number of `=` before and after 165531e725cSNickeau */ 16604fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 167531e725cSNickeau $callStack->moveToEnd(); 168531e725cSNickeau $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 169531e725cSNickeau $levelFromMatch = $this->getLevelFromMatch($match); 17004fd306cSNickeau $levelFromStartTag = $openingTag->getAttribute(HeadingTag::LEVEL); 171531e725cSNickeau if ($levelFromMatch != $levelFromStartTag) { 172531e725cSNickeau $content = ""; 173531e725cSNickeau while ($actualCall = $callStack->next()) { 174531e725cSNickeau $content .= $actualCall->getCapturedContent(); 175531e725cSNickeau } 17604fd306cSNickeau 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); 177531e725cSNickeau } 178531e725cSNickeau 179531e725cSNickeau return $returnedData; 180531e725cSNickeau 181531e725cSNickeau } 182531e725cSNickeau return array(); 183531e725cSNickeau } 184531e725cSNickeau 185c3437056SNickeau public function render($format, Doku_Renderer $renderer, $data): bool 186531e725cSNickeau { 187531e725cSNickeau 18804fd306cSNickeau switch ($format) { 18904fd306cSNickeau case "xhtml": 190531e725cSNickeau /** 191531e725cSNickeau * @var Doku_Renderer_xhtml $renderer 192531e725cSNickeau */ 193531e725cSNickeau $state = $data[PluginUtility::STATE]; 19404fd306cSNickeau $context = $data[PluginUtility::CONTEXT]; 195531e725cSNickeau switch ($state) { 196531e725cSNickeau 197531e725cSNickeau case DOKU_LEXER_ENTER: 198531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 19904fd306cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($callStackArray, HeadingTag::HEADING_TAG); 200531e725cSNickeau $pos = $data[PluginUtility::POSITION]; 20104fd306cSNickeau HeadingTag::processRenderEnterXhtml($context, $tagAttributes, $renderer, $pos); 202531e725cSNickeau return true; 203531e725cSNickeau case DOKU_LEXER_UNMATCHED: 204531e725cSNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 205531e725cSNickeau return true; 206531e725cSNickeau case DOKU_LEXER_EXIT: 207531e725cSNickeau $callStackArray = $data[PluginUtility::ATTRIBUTES]; 208531e725cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($callStackArray); 20904fd306cSNickeau $renderer->doc .= HeadingTag::renderClosingTag($tagAttributes, $context); 210531e725cSNickeau return true; 211531e725cSNickeau 212531e725cSNickeau } 21304fd306cSNickeau return false; 21404fd306cSNickeau case renderer_plugin_combo_analytics::RENDERER_FORMAT: 215531e725cSNickeau 216531e725cSNickeau /** 217531e725cSNickeau * @var renderer_plugin_combo_analytics $renderer 218531e725cSNickeau */ 21904fd306cSNickeau HeadingTag::processMetadataAnalytics($data, $renderer); 22004fd306cSNickeau return true; 221531e725cSNickeau 22204fd306cSNickeau case "metadata": 223531e725cSNickeau 224531e725cSNickeau /** 225531e725cSNickeau * @var Doku_Renderer_metadata $renderer 226531e725cSNickeau */ 22704fd306cSNickeau HeadingTag::processHeadingEnterMetadata($data, $renderer); 22804fd306cSNickeau return true; 229531e725cSNickeau 23004fd306cSNickeau case renderer_plugin_combo_xml::FORMAT: 231c3437056SNickeau $state = $data[PluginUtility::STATE]; 232c3437056SNickeau switch ($state) { 233c3437056SNickeau case DOKU_LEXER_ENTER: 23404fd306cSNickeau $level = $data[PluginUtility::ATTRIBUTES][HeadingTag::LEVEL]; 235c3437056SNickeau $renderer->doc .= "<h$level>"; 23604fd306cSNickeau return true; 237c3437056SNickeau case DOKU_LEXER_UNMATCHED: 238c3437056SNickeau $renderer->doc .= PluginUtility::renderUnmatchedXml($data); 23904fd306cSNickeau return true; 240c3437056SNickeau case DOKU_LEXER_EXIT: 24104fd306cSNickeau $level = $data[PluginUtility::ATTRIBUTES][HeadingTag::LEVEL]; 242c3437056SNickeau $renderer->doc .= "</h$level>"; 24304fd306cSNickeau return true; 244c3437056SNickeau 245c3437056SNickeau } 246531e725cSNickeau return false; 24704fd306cSNickeau default: 24804fd306cSNickeau return false; 24904fd306cSNickeau } 25004fd306cSNickeau 251531e725cSNickeau } 252531e725cSNickeau 253531e725cSNickeau /** 254531e725cSNickeau * @param $match 255531e725cSNickeau * @return int 256531e725cSNickeau */ 257531e725cSNickeau public 25804fd306cSNickeau function getLevelFromMatch($match): int 259531e725cSNickeau { 260531e725cSNickeau return 7 - strlen(trim($match)); 261531e725cSNickeau } 262531e725cSNickeau 263531e725cSNickeau 264531e725cSNickeau private 265531e725cSNickeau function enableWikiHeading($mode) 266531e725cSNickeau { 267531e725cSNickeau 268531e725cSNickeau 269531e725cSNickeau /** 270531e725cSNickeau * Basically all mode that are not `base` 271531e725cSNickeau * To not take the dokuwiki heading 272531e725cSNickeau */ 273531e725cSNickeau if (!(in_array($mode, ['base', 'header', 'table']))) { 274531e725cSNickeau return true; 275531e725cSNickeau } else { 27604fd306cSNickeau return SiteConfig::getConfValue(self::CONF_WIKI_HEADING_ENABLE, self::CONF_DEFAULT_WIKI_ENABLE_VALUE); 277531e725cSNickeau } 278531e725cSNickeau 279531e725cSNickeau 280531e725cSNickeau } 281531e725cSNickeau 282531e725cSNickeau 283531e725cSNickeau} 284