104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeaunamespace ComboStrap; 404fd306cSNickeau 504fd306cSNickeau 604fd306cSNickeauuse ComboStrap\Meta\Field\PageH1; 704fd306cSNickeauuse ComboStrap\Tag\TableTag; 804fd306cSNickeauuse ComboStrap\TagAttribute\StyleAttribute; 904fd306cSNickeauuse dokuwiki\Extension\SyntaxPlugin; 1004fd306cSNickeauuse syntax_plugin_combo_analytics; 1104fd306cSNickeauuse syntax_plugin_combo_header; 1204fd306cSNickeauuse syntax_plugin_combo_headingatx; 1304fd306cSNickeauuse syntax_plugin_combo_headingwiki; 1404fd306cSNickeauuse syntax_plugin_combo_media; 1504fd306cSNickeau 1604fd306cSNickeau/** 1704fd306cSNickeau * The outline is creating a XML like document 1804fd306cSNickeau * with section 1904fd306cSNickeau * 2004fd306cSNickeau * It's also the post-processing of the instructions 2104fd306cSNickeau */ 2204fd306cSNickeauclass Outline 2304fd306cSNickeau{ 2404fd306cSNickeau 2504fd306cSNickeau 2604fd306cSNickeau const CANONICAL = "outline"; 2704fd306cSNickeau private const OUTLINE_HEADING_PREFIX = "outline-heading"; 2804fd306cSNickeau const CONTEXT = self::CANONICAL; 2904fd306cSNickeau public const OUTLINE_HEADING_NUMBERING = "outline-heading-numbering"; 3004fd306cSNickeau public const TOC_NUMBERING = "toc-numbering"; 3104fd306cSNickeau /** 3204fd306cSNickeau * As seen on 3304fd306cSNickeau * https://drafts.csswg.org/css-counter-styles-3/#predefined-counters 3404fd306cSNickeau */ 3504fd306cSNickeau public const CONF_COUNTER_STYLES_CHOICES = [ 3604fd306cSNickeau 'arabic-indic', 3704fd306cSNickeau 'bengali', 3804fd306cSNickeau 'cambodian/khmer', 3904fd306cSNickeau 'cjk-decimal', 4004fd306cSNickeau 'decimal', 4104fd306cSNickeau 'decimal-leading-zero', 4204fd306cSNickeau 'devanagari', 4304fd306cSNickeau 'georgian', 4404fd306cSNickeau 'gujarati', 4504fd306cSNickeau 'gurmukhi', 4604fd306cSNickeau 'hebrew', 4704fd306cSNickeau 'hiragana', 4804fd306cSNickeau 'hiragana-iroha', 4904fd306cSNickeau 'kannada', 5004fd306cSNickeau 'katakana', 5104fd306cSNickeau 'katakana-iroha', 5204fd306cSNickeau 'lao', 5304fd306cSNickeau 'lower-alpha', 5404fd306cSNickeau 'lower-armenian', 5504fd306cSNickeau 'lower-greek', 5604fd306cSNickeau 'lower-roman', 5704fd306cSNickeau 'malayalam', 5804fd306cSNickeau 'mongolian', 5904fd306cSNickeau 'myanmar', 6004fd306cSNickeau 'oriya', 6104fd306cSNickeau 'persian', 6204fd306cSNickeau 'tamil', 6304fd306cSNickeau 'telugu', 6404fd306cSNickeau 'thai', 6504fd306cSNickeau 'tibetan', 6604fd306cSNickeau 'upper-alpha', 6704fd306cSNickeau 'upper-armenian', 6804fd306cSNickeau 'upper-roman' 6904fd306cSNickeau ]; 7004fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL4 = "outlineNumberingCounterStyleLevel4"; 7104fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL3 = "outlineNumberingCounterStyleLevel3"; 7204fd306cSNickeau public const CONF_OUTLINE_NUMBERING_SUFFIX = "outlineNumberingSuffix"; 7304fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL2 = "outlineNumberingCounterStyleLevel2"; 7404fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_SEPARATOR = "outlineNumberingCounterSeparator"; 7504fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL6 = "outlineNumberingCounterStyleLevel6"; 7604fd306cSNickeau public const CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL5 = "outlineNumberingCounterStyleLevel5"; 7704fd306cSNickeau public const CONF_OUTLINE_NUMBERING_PREFIX = "outlineNumberingPrefix"; 7804fd306cSNickeau public const CONF_OUTLINE_NUMBERING_ENABLE = "outlineNumberingEnable"; 7904fd306cSNickeau /** 8004fd306cSNickeau * To add hash tag to heading 8104fd306cSNickeau */ 8204fd306cSNickeau public const OUTLINE_ANCHOR = "outline-anchor"; 8304fd306cSNickeau const CONF_OUTLINE_NUMBERING_ENABLE_DEFAULT = 1; 84912a1845Sgerardnico 85912a1845Sgerardnico /** 86912a1845Sgerardnico * The dokuwiki heading call is called the header... 87912a1845Sgerardnico */ 88912a1845Sgerardnico const DOKUWIKI_HEADING_CALL_NAME = "header"; 8904fd306cSNickeau private OutlineSection $rootSection; 9004fd306cSNickeau 9104fd306cSNickeau private OutlineSection $actualSection; // the actual section that is created 9204fd306cSNickeau private Call $actualHeadingCall; // the heading that is parsed 9304fd306cSNickeau private int $actualHeadingParsingState = DOKU_LEXER_EXIT; // the state of the heading parsed (enter, closed), enter if we have entered an heading, exit if not; 9404fd306cSNickeau private ?MarkupPath $markupPath = null; 9504fd306cSNickeau private bool $isFragment; 9604fd306cSNickeau private bool $metaHeaderCapture; 9704fd306cSNickeau 9804fd306cSNickeau /** 9904fd306cSNickeau * @param CallStack $callStack 10004fd306cSNickeau * @param MarkupPath|null $markup - needed to store the parsed toc, h1, ... (null if the markup is dynamic) 10104fd306cSNickeau * @param bool $isFragment - needed to control the structure of the outline (if this is a preview, the first heading may be not h1) 10204fd306cSNickeau * @return void 10304fd306cSNickeau */ 10404fd306cSNickeau public function __construct(CallStack $callStack, MarkupPath $markup = null, bool $isFragment = false) 10504fd306cSNickeau { 10604fd306cSNickeau if ($markup !== null) { 10704fd306cSNickeau $this->markupPath = $markup; 10804fd306cSNickeau } 10904fd306cSNickeau $this->isFragment = $isFragment; 11004fd306cSNickeau $this->buildOutline($callStack); 11104fd306cSNickeau $this->storeH1(); 11204fd306cSNickeau $this->storeTocForMarkupIfAny(); 11304fd306cSNickeau } 11404fd306cSNickeau 11504fd306cSNickeau /** 11604fd306cSNickeau * @param CallStack $callStack 11704fd306cSNickeau * @param MarkupPath|null $markupPath - needed to store the parsed toc, h1, ... (null if the markup is dynamic) 11804fd306cSNickeau * @param bool $isFragment - needed to control the structure of the outline (if this is a preview, the first heading may be not h1) 11904fd306cSNickeau * @return Outline 12004fd306cSNickeau */ 12104fd306cSNickeau public static function createFromCallStack(CallStack $callStack, MarkupPath $markupPath = null, bool $isFragment = false): Outline 12204fd306cSNickeau { 12304fd306cSNickeau return new Outline($callStack, $markupPath, $isFragment); 12404fd306cSNickeau } 12504fd306cSNickeau 12604fd306cSNickeau private function buildOutline(CallStack $callStack) 12704fd306cSNickeau { 12804fd306cSNickeau 12904fd306cSNickeau /** 13004fd306cSNickeau * {@link syntax_plugin_combo_analytics tag analytics} 13104fd306cSNickeau * By default, in a test environment 13204fd306cSNickeau * this is set to 0 13304fd306cSNickeau * to speed test and to not pollute 13404fd306cSNickeau */ 13504fd306cSNickeau $analtyicsEnabled = SiteConfig::getConfValue(syntax_plugin_combo_analytics::CONF_SYNTAX_ANALYTICS_ENABLE, true); 13604fd306cSNickeau $analyticsTagUsed = []; 13704fd306cSNickeau 13804fd306cSNickeau /** 13904fd306cSNickeau * Processing variable about the context 14004fd306cSNickeau */ 14104fd306cSNickeau $this->rootSection = OutlineSection::createOutlineRoot() 142*dff3a8c8SNico ->setStartPosition(0) 143*dff3a8c8SNico ->setOutlineContext($this); 14404fd306cSNickeau $this->actualSection = $this->rootSection; 14504fd306cSNickeau $actualLastPosition = 0; 14604fd306cSNickeau $callStack->moveToStart(); 14704fd306cSNickeau while ($actualCall = $callStack->next()) { 14804fd306cSNickeau 14904fd306cSNickeau 15004fd306cSNickeau $state = $actualCall->getState(); 15104fd306cSNickeau 15204fd306cSNickeau /** 15304fd306cSNickeau * Block Post Processing 15404fd306cSNickeau * to not get any unwanted p 15504fd306cSNickeau * to counter {@link Block::process()} 15604fd306cSNickeau * setting dynamically the {@link SyntaxPlugin::getPType()} 15704fd306cSNickeau * 15804fd306cSNickeau * Unfortunately, it can't work because this is called after 15904fd306cSNickeau * {@link Block::process()} 16004fd306cSNickeau */ 16104fd306cSNickeau if ($analtyicsEnabled) { 16204fd306cSNickeau 16304fd306cSNickeau if (in_array($state, CallStack::TAG_STATE)) { 164aea52b49Sgerardnico $tagName = $actualCall->getTagName(); 16504fd306cSNickeau // The dokuwiki component name have open in their name 16604fd306cSNickeau $tagName = str_replace("_open", "", $tagName); 167aea52b49Sgerardnico $actual = $analyticsTagUsed[$tagName] ?? 0; 168aea52b49Sgerardnico $analyticsTagUsed[$tagName] = $actual + 1; 16904fd306cSNickeau } 17004fd306cSNickeau 17104fd306cSNickeau } 17204fd306cSNickeau 17304fd306cSNickeau /** 17404fd306cSNickeau * We don't take the outline and document call if any 17504fd306cSNickeau * This is the case when we build from an actual stored instructions 17604fd306cSNickeau * (to bundle multiple page for instance) 17704fd306cSNickeau */ 17804fd306cSNickeau $tagName = $actualCall->getTagName(); 17904fd306cSNickeau switch ($tagName) { 18004fd306cSNickeau case "document_start": 18104fd306cSNickeau case "document_end": 18204fd306cSNickeau case SectionTag::TAG: 18304fd306cSNickeau continue 2; 18404fd306cSNickeau case syntax_plugin_combo_header::TAG: 18504fd306cSNickeau if ($actualCall->isPluginCall() && $actualCall->getContext() === self::CONTEXT) { 18604fd306cSNickeau continue 2; 18704fd306cSNickeau } 18804fd306cSNickeau } 18904fd306cSNickeau 19004fd306cSNickeau /** 19104fd306cSNickeau * Wrap the table 19204fd306cSNickeau */ 19304fd306cSNickeau $componentName = $actualCall->getComponentName(); 19404fd306cSNickeau if ($componentName === "table_open") { 19504fd306cSNickeau $position = $actualCall->getFirstMatchedCharacterPosition(); 19604fd306cSNickeau $originalInstructionCall = &$actualCall->getInstructionCall(); 19704fd306cSNickeau $originalInstructionCall = Call::createComboCall( 19804fd306cSNickeau TableTag::TAG, 19904fd306cSNickeau DOKU_LEXER_ENTER, 20004fd306cSNickeau [PluginUtility::POSITION => $position], 20104fd306cSNickeau null, 20204fd306cSNickeau null, 20304fd306cSNickeau null, 20404fd306cSNickeau $position, 20504fd306cSNickeau \syntax_plugin_combo_xmlblocktag::TAG 20604fd306cSNickeau )->toCallArray(); 20704fd306cSNickeau } 20804fd306cSNickeau 20904fd306cSNickeau /** 21004fd306cSNickeau * Enter new section ? 21104fd306cSNickeau */ 21204fd306cSNickeau $shouldWeCreateASection = false; 21304fd306cSNickeau switch ($tagName) { 21404fd306cSNickeau case syntax_plugin_combo_headingatx::TAG: 21504fd306cSNickeau $actualCall->setState(DOKU_LEXER_ENTER); 21604fd306cSNickeau if ($actualCall->getContext() === HeadingTag::TYPE_OUTLINE) { 21704fd306cSNickeau $shouldWeCreateASection = true; 21804fd306cSNickeau } 21904fd306cSNickeau $this->enterHeading($actualCall); 22004fd306cSNickeau break; 22104fd306cSNickeau case HeadingTag::HEADING_TAG: 22204fd306cSNickeau case syntax_plugin_combo_headingwiki::TAG: 22304fd306cSNickeau if ($state == DOKU_LEXER_ENTER 22404fd306cSNickeau && $actualCall->getContext() === HeadingTag::TYPE_OUTLINE) { 22504fd306cSNickeau $shouldWeCreateASection = true; 22604fd306cSNickeau $this->enterHeading($actualCall); 22704fd306cSNickeau } 22804fd306cSNickeau break; 229912a1845Sgerardnico case self::DOKUWIKI_HEADING_CALL_NAME: 23004fd306cSNickeau // Should happen only on outline section 23104fd306cSNickeau // we take over inside a component 23204fd306cSNickeau if (!$actualCall->isPluginCall()) { 23304fd306cSNickeau /** 23404fd306cSNickeau * ie not {@link syntax_plugin_combo_header} 23504fd306cSNickeau * but the dokuwiki header (ie heading) 23604fd306cSNickeau */ 23704fd306cSNickeau $shouldWeCreateASection = true; 23804fd306cSNickeau $this->enterHeading($actualCall); 239912a1845Sgerardnico // The dokuiki heading call (header) is a one call for the whole heading, 240912a1845Sgerardnico // It enters and exits at the same time 241912a1845Sgerardnico $this->exitHeading(); 24204fd306cSNickeau } 24304fd306cSNickeau break; 24404fd306cSNickeau } 24504fd306cSNickeau if ($shouldWeCreateASection) { 24604fd306cSNickeau if ($this->actualSection->hasParent()) { 24704fd306cSNickeau // -1 because the actual position is the start of the next section 24804fd306cSNickeau $this->actualSection->setEndPosition($actualCall->getFirstMatchedCharacterPosition() - 1); 24904fd306cSNickeau } 25004fd306cSNickeau $actualSectionLevel = $this->actualSection->getLevel(); 25104fd306cSNickeau 25204fd306cSNickeau if ($actualCall->isPluginCall()) { 25304fd306cSNickeau try { 25404fd306cSNickeau $newSectionLevel = DataType::toInteger($actualCall->getAttribute(HeadingTag::LEVEL)); 25504fd306cSNickeau } catch (ExceptionBadArgument $e) { 25604fd306cSNickeau LogUtility::internalError("The level was not present on the heading call", self::CANONICAL); 25704fd306cSNickeau $newSectionLevel = $actualSectionLevel; 25804fd306cSNickeau } 25904fd306cSNickeau } else { 26004fd306cSNickeau $headerTagName = $tagName; 261912a1845Sgerardnico if ($headerTagName !== self::DOKUWIKI_HEADING_CALL_NAME) { 26204fd306cSNickeau throw new ExceptionRuntimeInternal("This is not a dokuwiki header call", self::CANONICAL); 26304fd306cSNickeau } 26404fd306cSNickeau $newSectionLevel = $actualCall->getInstructionCall()[1][1]; 26504fd306cSNickeau } 26604fd306cSNickeau 26704fd306cSNickeau 26804fd306cSNickeau $newOutlineSection = OutlineSection::createFromEnterHeadingCall($actualCall); 26904fd306cSNickeau $sectionDiff = $newSectionLevel - $actualSectionLevel; 27004fd306cSNickeau if ($sectionDiff > 0) { 27104fd306cSNickeau 27204fd306cSNickeau /** 27304fd306cSNickeau * A child of the actual section 27404fd306cSNickeau * We append it first before the message check to 27504fd306cSNickeau * build the {@link TreeNode::getTreeIdentifier()} 27604fd306cSNickeau */ 27704fd306cSNickeau try { 27804fd306cSNickeau $this->actualSection->appendChild($newOutlineSection); 27904fd306cSNickeau } catch (ExceptionBadState $e) { 28004fd306cSNickeau throw new ExceptionRuntimeInternal("The node is not added multiple time, this error should not fired. Error:{$e->getMessage()}", self::CANONICAL, 1, $e); 28104fd306cSNickeau } 28204fd306cSNickeau 28304fd306cSNickeau if ($sectionDiff > 1 & !($actualSectionLevel === 0 && $newSectionLevel === 2)) { 28404fd306cSNickeau $expectedLevel = $actualSectionLevel + 1; 28504fd306cSNickeau if ($actualSectionLevel === 0) { 28604fd306cSNickeau /** 28704fd306cSNickeau * In a fragment run (preview), 28804fd306cSNickeau * the first heading may not be the first one 28904fd306cSNickeau */ 29004fd306cSNickeau if (!$this->isFragment) { 29104fd306cSNickeau $message = "The first section heading should have the level 1 or 2 (not $newSectionLevel)."; 29204fd306cSNickeau } 29304fd306cSNickeau } else { 29404fd306cSNickeau $message = "The child section heading ($actualSectionLevel) has the level ($newSectionLevel) but is parent ({$this->actualSection->getLabel()}) has the level ($actualSectionLevel). The expected level is ($expectedLevel)."; 29504fd306cSNickeau } 29604fd306cSNickeau if (isset($message)) { 29704fd306cSNickeau LogUtility::warning($message, self::CANONICAL); 29804fd306cSNickeau } 29904fd306cSNickeau $actualCall->setAttribute(HeadingTag::LEVEL, $newSectionLevel); 30004fd306cSNickeau } 30104fd306cSNickeau 30204fd306cSNickeau } else { 30304fd306cSNickeau 30404fd306cSNickeau /** 30504fd306cSNickeau * A child of the parent section, A sibling of the actual session 30604fd306cSNickeau */ 30704fd306cSNickeau try { 30804fd306cSNickeau $parent = $this->actualSection->getParent(); 30904fd306cSNickeau for ($i = 0; $i < abs($sectionDiff); $i++) { 31004fd306cSNickeau $parent = $parent->getParent(); 31104fd306cSNickeau } 31204fd306cSNickeau try { 31304fd306cSNickeau $parent->appendChild($newOutlineSection); 31404fd306cSNickeau } catch (ExceptionBadState $e) { 31504fd306cSNickeau throw new ExceptionRuntimeInternal("The node is not added multiple time, this error should not fired. Error:{$e->getMessage()}", self::CANONICAL, 1, $e); 31604fd306cSNickeau } 31704fd306cSNickeau } catch (ExceptionNotFound $e) { 31804fd306cSNickeau /** 31904fd306cSNickeau * no parent 32004fd306cSNickeau * May happen in preview (ie fragment) 32104fd306cSNickeau */ 32204fd306cSNickeau if (!$this->isFragment) { 32304fd306cSNickeau LogUtility::internalError("Due to the level logic, the actual section should have a parent"); 32404fd306cSNickeau } 32504fd306cSNickeau try { 32604fd306cSNickeau $this->actualSection->appendChild($newOutlineSection); 32704fd306cSNickeau } catch (ExceptionBadState $e) { 32804fd306cSNickeau throw new ExceptionRuntimeInternal("The node is not added multiple time, this error should not fired. Error:{$e->getMessage()}", self::CANONICAL, 1, $e); 32904fd306cSNickeau } 33004fd306cSNickeau } 33104fd306cSNickeau 33204fd306cSNickeau } 33304fd306cSNickeau 33404fd306cSNickeau $this->actualSection = $newOutlineSection; 33504fd306cSNickeau continue; 33604fd306cSNickeau } 33704fd306cSNickeau 33804fd306cSNickeau /** 33904fd306cSNickeau * Track the number of lines 34004fd306cSNickeau * to inject ads 34104fd306cSNickeau */ 34204fd306cSNickeau switch ($tagName) { 34304fd306cSNickeau case "linebreak": 34404fd306cSNickeau case "tablerow": 34504fd306cSNickeau // linebreak is an inline component 34604fd306cSNickeau $this->actualSection->incrementLineNumber(); 34704fd306cSNickeau break; 34804fd306cSNickeau default: 34904fd306cSNickeau $display = $actualCall->getDisplay(); 35004fd306cSNickeau if ($display === Call::BlOCK_DISPLAY) { 35104fd306cSNickeau $this->actualSection->incrementLineNumber(); 35204fd306cSNickeau } 35304fd306cSNickeau break; 35404fd306cSNickeau } 35504fd306cSNickeau 35604fd306cSNickeau /** 35704fd306cSNickeau * Track the position in the file 35804fd306cSNickeau */ 35904fd306cSNickeau $currentLastPosition = $actualCall->getLastMatchedCharacterPosition(); 36004fd306cSNickeau if ($currentLastPosition > $actualLastPosition) { 36104fd306cSNickeau // the position in the stack is not always good 36204fd306cSNickeau $actualLastPosition = $currentLastPosition; 36304fd306cSNickeau } 36404fd306cSNickeau 36504fd306cSNickeau 36604fd306cSNickeau switch ($actualCall->getComponentName()) { 36704fd306cSNickeau case \action_plugin_combo_instructionspostprocessing::EDIT_SECTION_OPEN: 36804fd306cSNickeau case \action_plugin_combo_instructionspostprocessing::EDIT_SECTION_CLOSE: 36904fd306cSNickeau // we don't store them 37004fd306cSNickeau continue 2; 37104fd306cSNickeau } 37204fd306cSNickeau 37304fd306cSNickeau /** 37404fd306cSNickeau * Close/Process the heading description 37504fd306cSNickeau */ 37604fd306cSNickeau if ($this->actualHeadingParsingState === DOKU_LEXER_ENTER) { 37704fd306cSNickeau switch ($tagName) { 37804fd306cSNickeau 37904fd306cSNickeau case HeadingTag::HEADING_TAG: 38004fd306cSNickeau case syntax_plugin_combo_headingwiki::TAG: 38104fd306cSNickeau if ($state == DOKU_LEXER_EXIT) { 38204fd306cSNickeau $this->addCallToSection($actualCall); 38304fd306cSNickeau $this->exitHeading(); 38404fd306cSNickeau continue 2; 38504fd306cSNickeau } 38604fd306cSNickeau break; 38704fd306cSNickeau 38804fd306cSNickeau case "internalmedia": 38904fd306cSNickeau // no link for media in heading 39004fd306cSNickeau $actualCall->getInstructionCall()[1][6] = MediaMarkup::LINKING_NOLINK_VALUE; 39104fd306cSNickeau break; 39204fd306cSNickeau case syntax_plugin_combo_media::TAG: 39304fd306cSNickeau // no link for media in heading 39404fd306cSNickeau $actualCall->addAttribute(MediaMarkup::LINKING_KEY, MediaMarkup::LINKING_NOLINK_VALUE); 39504fd306cSNickeau break; 39604fd306cSNickeau 397912a1845Sgerardnico case self::DOKUWIKI_HEADING_CALL_NAME: 39804fd306cSNickeau if (SiteConfig::getConfValue(syntax_plugin_combo_headingwiki::CONF_WIKI_HEADING_ENABLE, syntax_plugin_combo_headingwiki::CONF_DEFAULT_WIKI_ENABLE_VALUE) == 1) { 39904fd306cSNickeau LogUtility::msg("The combo heading wiki is enabled, we should not see `header` calls in the call stack"); 40004fd306cSNickeau } 40104fd306cSNickeau break; 40204fd306cSNickeau 40304fd306cSNickeau case "p": 40404fd306cSNickeau 40504fd306cSNickeau if ($this->actualHeadingCall->getTagName() === syntax_plugin_combo_headingatx::TAG) { 40604fd306cSNickeau // A new p is the end of an atx call 40704fd306cSNickeau switch ($actualCall->getComponentName()) { 40804fd306cSNickeau case "p_open": 40904fd306cSNickeau // We don't take the p tag inside atx heading 41004fd306cSNickeau // therefore we continue 41104fd306cSNickeau continue 3; 41204fd306cSNickeau case "p_close": 41304fd306cSNickeau $endAtxCall = Call::createComboCall( 41404fd306cSNickeau syntax_plugin_combo_headingatx::TAG, 41504fd306cSNickeau DOKU_LEXER_EXIT, 41604fd306cSNickeau $this->actualHeadingCall->getAttributes(), 41704fd306cSNickeau $this->actualHeadingCall->getContext(), 41804fd306cSNickeau ); 41904fd306cSNickeau $this->addCallToSection($endAtxCall); 42004fd306cSNickeau $this->exitHeading(); 42104fd306cSNickeau // We don't take the p tag inside atx heading 42204fd306cSNickeau // therefore we continue 42304fd306cSNickeau continue 3; 42404fd306cSNickeau } 42504fd306cSNickeau } 42604fd306cSNickeau break; 42704fd306cSNickeau 42804fd306cSNickeau } 42904fd306cSNickeau } 43004fd306cSNickeau $this->addCallToSection($actualCall); 43104fd306cSNickeau } 43204fd306cSNickeau 43304fd306cSNickeau // empty text 43404fd306cSNickeau if (sizeof($analyticsTagUsed) > 0) { 43504fd306cSNickeau $pluginAnalyticsCall = Call::createComboCall( 43604fd306cSNickeau syntax_plugin_combo_analytics::TAG, 43704fd306cSNickeau DOKU_LEXER_SPECIAL, 43804fd306cSNickeau $analyticsTagUsed 43904fd306cSNickeau ); 44004fd306cSNickeau $this->addCallToSection($pluginAnalyticsCall); 44104fd306cSNickeau } 44204fd306cSNickeau 44304fd306cSNickeau // Add label the heading text to the metadata 44404fd306cSNickeau $this->saveOutlineToMetadata(); 44504fd306cSNickeau 44604fd306cSNickeau 44704fd306cSNickeau } 44804fd306cSNickeau 44904fd306cSNickeau public static function getOutlineHeadingClass(): string 45004fd306cSNickeau { 45104fd306cSNickeau return StyleAttribute::addComboStrapSuffix(self::OUTLINE_HEADING_PREFIX); 45204fd306cSNickeau } 45304fd306cSNickeau 45404fd306cSNickeau public function getRootOutlineSection(): OutlineSection 45504fd306cSNickeau { 45604fd306cSNickeau return $this->rootSection; 45704fd306cSNickeau 45804fd306cSNickeau } 45904fd306cSNickeau 46004fd306cSNickeau /** 461*dff3a8c8SNico * Merge into a flat outline 46204fd306cSNickeau */ 4637dbcdecdSNico public static function merge(Outline $inner, Outline $outer, int $actualLevel) 46404fd306cSNickeau { 46504fd306cSNickeau /** 46604fd306cSNickeau * Get the inner section where the outer section will be added 46704fd306cSNickeau */ 46804fd306cSNickeau $innerRootOutlineSection = $inner->getRootOutlineSection(); 46904fd306cSNickeau $innerTopSections = $innerRootOutlineSection->getChildren(); 47004fd306cSNickeau if (count($innerTopSections) === 0) { 47104fd306cSNickeau $firstInnerSection = $innerRootOutlineSection; 47204fd306cSNickeau } else { 47304fd306cSNickeau $firstInnerSection = $innerTopSections[count($innerTopSections)]; 47404fd306cSNickeau } 47504fd306cSNickeau $firstInnerSectionLevel = $firstInnerSection->getLevel(); 47604fd306cSNickeau 47704fd306cSNickeau /** 47804fd306cSNickeau * Add the outer sections 47904fd306cSNickeau */ 48004fd306cSNickeau $outerRootOutlineSection = $outer->getRootOutlineSection(); 48104fd306cSNickeau foreach ($outerRootOutlineSection->getChildren() as $childOuterSection) { 48204fd306cSNickeau /** 48304fd306cSNickeau * One level less than where the section is included 48404fd306cSNickeau */ 485*dff3a8c8SNico $level = $firstInnerSectionLevel + $actualLevel + 1; 486*dff3a8c8SNico $childOuterSection->setLevel($level); 487*dff3a8c8SNico $childOuterSection->updatePageLinkToInternal($inner->markupPath); 48804fd306cSNickeau $childOuterSection->detachBeforeAppend(); 489*dff3a8c8SNico 49004fd306cSNickeau try { 49104fd306cSNickeau $firstInnerSection->appendChild($childOuterSection); 49204fd306cSNickeau } catch (ExceptionBadState $e) { 49304fd306cSNickeau // We add the node only once. This error should not happen 49404fd306cSNickeau throw new ExceptionRuntimeInternal("Error while adding a section during the outline merge. Error: {$e->getMessage()}", self::CANONICAL, 1, $e); 49504fd306cSNickeau } 496*dff3a8c8SNico 49704fd306cSNickeau } 49804fd306cSNickeau 49904fd306cSNickeau } 50004fd306cSNickeau 50104fd306cSNickeau public static function mergeRecurse(Outline $inner, Outline $outer) 50204fd306cSNickeau { 50304fd306cSNickeau $innerRootOutlineSection = $inner->getRootOutlineSection(); 50404fd306cSNickeau $outerRootOutlineSection = $outer->getRootOutlineSection(); 50504fd306cSNickeau 50604fd306cSNickeau } 50704fd306cSNickeau 50804fd306cSNickeau /** 50904fd306cSNickeau * Utility class to create a outline from a markup string 51004fd306cSNickeau * @param string $content 511*dff3a8c8SNico * @param MarkupPath $contentPath 512*dff3a8c8SNico * @param WikiPath $contextPath 51304fd306cSNickeau * @return Outline 51404fd306cSNickeau */ 515*dff3a8c8SNico public static function createFromMarkup(string $content, MarkupPath $contentPath, WikiPath $contextPath): Outline 51604fd306cSNickeau { 51704fd306cSNickeau $instructions = MarkupRenderer::createFromMarkup($content, $contentPath, $contextPath) 51804fd306cSNickeau ->setRequestedMimeToInstruction() 51904fd306cSNickeau ->getOutput(); 52004fd306cSNickeau $callStack = CallStack::createFromInstructions($instructions); 521*dff3a8c8SNico return Outline::createFromCallStack($callStack, $contentPath); 52204fd306cSNickeau } 52304fd306cSNickeau 52404fd306cSNickeau /** 52504fd306cSNickeau * Get the heading numbering snippet 52604fd306cSNickeau * @param string $type heading or toc - for {@link Outline::TOC_NUMBERING} or {@link Outline::OUTLINE_HEADING_NUMBERING} 52704fd306cSNickeau * @return string - the css internal stylesheet 52804fd306cSNickeau * @throws ExceptionNotEnabled 52904fd306cSNickeau * @throws ExceptionBadSyntax 53004fd306cSNickeau * Page on DokuWiki 53104fd306cSNickeau * https://www.dokuwiki.org/tips:numbered_headings 53204fd306cSNickeau */ 53304fd306cSNickeau public static function getCssNumberingRulesFor(string $type): string 53404fd306cSNickeau { 53504fd306cSNickeau 53604fd306cSNickeau $enable = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_ENABLE, Outline::CONF_OUTLINE_NUMBERING_ENABLE_DEFAULT); 53704fd306cSNickeau if (!$enable) { 53804fd306cSNickeau throw new ExceptionNotEnabled(); 53904fd306cSNickeau } 54004fd306cSNickeau 54104fd306cSNickeau $level2CounterStyle = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL2, "decimal"); 54204fd306cSNickeau $level3CounterStyle = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL3, "decimal"); 54304fd306cSNickeau $level4CounterStyle = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL4, "decimal"); 54404fd306cSNickeau $level5CounterStyle = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL5, "decimal"); 54504fd306cSNickeau $level6CounterStyle = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_STYLE_LEVEL6, "decimal"); 54604fd306cSNickeau $counterSeparator = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_COUNTER_SEPARATOR, "."); 54704fd306cSNickeau $prefix = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_PREFIX, ""); 54804fd306cSNickeau $suffix = SiteConfig::getConfValue(self::CONF_OUTLINE_NUMBERING_SUFFIX, " - "); 54904fd306cSNickeau 55004fd306cSNickeau switch ($type) { 55104fd306cSNickeau 55204fd306cSNickeau case self::OUTLINE_HEADING_NUMBERING: 55304fd306cSNickeau global $ACT; 55404fd306cSNickeau /** 55504fd306cSNickeau * Because the HTML file structure is not really fixed 55604fd306cSNickeau * (we may have section HTML element with a bar, the sectioning heading 55704fd306cSNickeau * may be not enabled) 55804fd306cSNickeau * We can't select via html structure 55904fd306cSNickeau * the outline heading consistently 56004fd306cSNickeau * We do it then with the class value 56104fd306cSNickeau */ 56204fd306cSNickeau $outlineClass = Outline::getOutlineHeadingClass(); 563ef115533Sgerardnico if ($ACT === "preview") { 564ef115533Sgerardnico $mainContainerSelector = ".pad"; 565ef115533Sgerardnico } else { 566ef115533Sgerardnico $mainContainerSelector = "#" . TemplateSlot::MAIN_CONTENT_ID; 567484d95f4Sgerardnico } 568484d95f4Sgerardnico /** 569484d95f4Sgerardnico * Counter inheritance works by sibling and if not found on parents 570484d95f4Sgerardnico * we therefore needs to take into account the 2 HTML structure 571484d95f4Sgerardnico * * one counter on h1 if this is the flat structure 572484d95f4Sgerardnico * one counter on the section if this is the section structure 573484d95f4Sgerardnico */ 574ef115533Sgerardnico $reset = <<<EOF 575ef115533Sgerardnico$mainContainerSelector { counter-reset: h2; } 576484d95f4Sgerardnico$mainContainerSelector > h2.$outlineClass { counter-increment: h2 1; counter-reset: h3 h4 h5 h6;} 577484d95f4Sgerardnico$mainContainerSelector > h3.$outlineClass { counter-increment: h3 1; counter-reset: h4 h5 h6;} 578484d95f4Sgerardnico$mainContainerSelector > h4.$outlineClass { counter-increment: h4 1; counter-reset: h5 h6;} 579484d95f4Sgerardnico$mainContainerSelector > h5.$outlineClass { counter-increment: h5 1; counter-reset: h6;} 580484d95f4Sgerardnico$mainContainerSelector > h6.$outlineClass { counter-increment: h6 1; } 5819557b9a7Sgerardnico$mainContainerSelector section.outline-level-2-cs { counter-increment: h2; counter-reset: h3 h4 h5 h6;} 5829557b9a7Sgerardnico$mainContainerSelector section.outline-level-3-cs { counter-increment: h3; counter-reset: h4 h5 h6;} 5839557b9a7Sgerardnico$mainContainerSelector section.outline-level-4-cs { counter-increment: h4; counter-reset: h5 h6;} 5849557b9a7Sgerardnico$mainContainerSelector section.outline-level-5-cs { counter-increment: h5; counter-reset: h6;} 585ef115533SgerardnicoEOF; 58604fd306cSNickeau return <<<EOF 587ef115533Sgerardnico$reset 588effabefcSgerardnico$mainContainerSelector h2.$outlineClass::before { content: "$prefix" counter(h2, $level2CounterStyle) "$suffix\A"; } 589effabefcSgerardnico$mainContainerSelector h3.$outlineClass::before { content: "$prefix" counter(h2, $level2CounterStyle) "$counterSeparator" counter(h3,$level3CounterStyle) "$suffix\A"; } 590effabefcSgerardnico$mainContainerSelector h4.$outlineClass::before { content: "$prefix" counter(h2, $level2CounterStyle) "$counterSeparator" counter(h3,$level3CounterStyle) "$counterSeparator" counter(h4,$level4CounterStyle) "$suffix\A"; } 591effabefcSgerardnico$mainContainerSelector h5.$outlineClass::before { content: "$prefix" counter(h2, $level2CounterStyle) "$counterSeparator" counter(h3,$level3CounterStyle) "$counterSeparator" counter(h4,$level4CounterStyle) "$counterSeparator" counter(h5,$level5CounterStyle) "$suffix\A"; } 592effabefcSgerardnico$mainContainerSelector h6.$outlineClass::before { content: "$prefix" counter(h2, $level2CounterStyle) "$counterSeparator" counter(h3,$level3CounterStyle) "$counterSeparator" counter(h4,$level4CounterStyle) "$counterSeparator" counter(h5,$level5CounterStyle) "$counterSeparator" counter(h6,$level6CounterStyle) "$suffix\A"; } 59304fd306cSNickeauEOF; 594ef115533Sgerardnico 595ef115533Sgerardnico 59604fd306cSNickeau case self::TOC_NUMBERING: 59704fd306cSNickeau /** 59804fd306cSNickeau * The level counter on the toc are based 59904fd306cSNickeau * on the https://www.dokuwiki.org/config:toptoclevel 60004fd306cSNickeau * configuration 60104fd306cSNickeau * if toptoclevel = 2, then level1 = h2 and not h1 60204fd306cSNickeau * @deprecated 60304fd306cSNickeau */ 60404fd306cSNickeau // global $conf; 60504fd306cSNickeau // $topTocLevel = $conf['toptoclevel']; 60604fd306cSNickeau 60704fd306cSNickeau $tocSelector = "." . Toc::getClass() . " ul"; 60804fd306cSNickeau return <<<EOF 60904fd306cSNickeau$tocSelector li { counter-increment: toc2; } 61004fd306cSNickeau$tocSelector li li { counter-increment: toc3; } 61104fd306cSNickeau$tocSelector li li li { counter-increment: toc4; } 61204fd306cSNickeau$tocSelector li li li li { counter-increment: toc5; } 61304fd306cSNickeau$tocSelector li li li li li { counter-increment: toc6; } 61404fd306cSNickeau$tocSelector li a::before { content: "$prefix" counter(toc2, $level2CounterStyle) "$suffix\A"; } 61504fd306cSNickeau$tocSelector li li a::before { content: "$prefix" counter(toc2, $level2CounterStyle) "$counterSeparator" counter(toc3,$level3CounterStyle) "$suffix\A"; } 61604fd306cSNickeau$tocSelector li li li a::before { content: "$prefix" counter(toc2, $level2CounterStyle) "$counterSeparator" counter(toc3,$level3CounterStyle) "$counterSeparator" counter(toc4,$level4CounterStyle) "$suffix\A"; } 61704fd306cSNickeau$tocSelector li li li li a::before { content: "$prefix" counter(toc2, $level2CounterStyle) "$counterSeparator" counter(toc3,$level3CounterStyle) "$counterSeparator" counter(toc4,$level4CounterStyle) "$counterSeparator" counter(toc5,$level5CounterStyle) "$suffix\A"; } 61804fd306cSNickeau$tocSelector li li li li li a::before { content: "$prefix" counter(toc2, $level2CounterStyle) "$counterSeparator" counter(toc3,$level3CounterStyle) "$counterSeparator" counter(toc4,$level4CounterStyle) "$counterSeparator" counter(toc5,$level5CounterStyle) "$counterSeparator" counter(toc6,$level6CounterStyle) "$suffix\A"; } 61904fd306cSNickeauEOF; 62004fd306cSNickeau 62104fd306cSNickeau default: 62204fd306cSNickeau throw new ExceptionBadSyntax("The type ($type) is unknown"); 62304fd306cSNickeau } 62404fd306cSNickeau 62504fd306cSNickeau 62604fd306cSNickeau } 62704fd306cSNickeau 62804fd306cSNickeau /** 62904fd306cSNickeau * @throws ExceptionNotFound 63004fd306cSNickeau */ 63104fd306cSNickeau public static function createFromMarkupPath(MarkupPath $markupPath): Outline 63204fd306cSNickeau { 63304fd306cSNickeau $path = $markupPath->getPathObject(); 63404fd306cSNickeau if (!($path instanceof WikiPath)) { 63504fd306cSNickeau throw new ExceptionRuntimeInternal("The path is not a wiki path"); 63604fd306cSNickeau } 63704fd306cSNickeau $markup = FileSystems::getContent($path); 63804fd306cSNickeau $instructions = MarkupRenderer::createFromMarkup($markup, $path, $path) 63904fd306cSNickeau ->setRequestedMimeToInstruction() 64004fd306cSNickeau ->getOutput(); 64104fd306cSNickeau $callStack = CallStack::createFromInstructions($instructions); 64204fd306cSNickeau return new Outline($callStack, $markupPath); 64304fd306cSNickeau } 64404fd306cSNickeau 64504fd306cSNickeau public function getInstructionCalls(): array 64604fd306cSNickeau { 64704fd306cSNickeau $totalInstructionCalls = []; 64804fd306cSNickeau $collectCalls = function (OutlineSection $outlineSection) use (&$totalInstructionCalls) { 64904fd306cSNickeau $instructionCalls = array_map(function (Call $element) { 65004fd306cSNickeau return $element->getInstructionCall(); 65104fd306cSNickeau }, $outlineSection->getCalls()); 65204fd306cSNickeau $totalInstructionCalls = array_merge($totalInstructionCalls, $instructionCalls); 65304fd306cSNickeau }; 65404fd306cSNickeau TreeVisit::visit($this->rootSection, $collectCalls); 65504fd306cSNickeau return $totalInstructionCalls; 65604fd306cSNickeau } 65704fd306cSNickeau 65804fd306cSNickeau public function toDokuWikiTemplateInstructionCalls(): array 65904fd306cSNickeau { 66004fd306cSNickeau $totalInstructionCalls = []; 66104fd306cSNickeau $sectionSequenceId = 0; 66204fd306cSNickeau $collectCalls = function (OutlineSection $outlineSection) use (&$totalInstructionCalls, &$sectionSequenceId) { 66304fd306cSNickeau 66404fd306cSNickeau $wikiSectionOpen = Call::createNativeCall( 66504fd306cSNickeau \action_plugin_combo_instructionspostprocessing::EDIT_SECTION_OPEN, 66604fd306cSNickeau array($outlineSection->getLevel()), 66704fd306cSNickeau $outlineSection->getStartPosition() 66804fd306cSNickeau ); 66904fd306cSNickeau $wikiSectionClose = Call::createNativeCall( 67004fd306cSNickeau \action_plugin_combo_instructionspostprocessing::EDIT_SECTION_CLOSE, 67104fd306cSNickeau array(), 67204fd306cSNickeau $outlineSection->getEndPosition() 67304fd306cSNickeau ); 67404fd306cSNickeau 67504fd306cSNickeau 67604fd306cSNickeau if ($outlineSection->hasParent()) { 67704fd306cSNickeau 67804fd306cSNickeau 67904fd306cSNickeau $sectionCalls = array_merge( 68004fd306cSNickeau $outlineSection->getHeadingCalls(), 68104fd306cSNickeau [$wikiSectionOpen], 68204fd306cSNickeau $outlineSection->getContentCalls(), 68304fd306cSNickeau [$wikiSectionClose], 68404fd306cSNickeau ); 68504fd306cSNickeau 68604fd306cSNickeau if ($this->isSectionEditingEnabled()) { 68704fd306cSNickeau 68804fd306cSNickeau /** 68904fd306cSNickeau * Adding sectionedit class to be conform 69004fd306cSNickeau * with the Dokuwiki {@link \Doku_Renderer_xhtml::header()} function 69104fd306cSNickeau */ 69204fd306cSNickeau $sectionSequenceId++; 69304fd306cSNickeau $headingCall = $outlineSection->getEnterHeadingCall(); 69404fd306cSNickeau if ($headingCall->isPluginCall()) { 69504fd306cSNickeau $level = DataType::toIntegerOrDefaultIfNull($headingCall->getAttribute(HeadingTag::LEVEL), 0); 69604fd306cSNickeau if ($level <= $this->getTocMaxLevel()) { 69704fd306cSNickeau $headingCall->addClassName("sectionedit$sectionSequenceId"); 69804fd306cSNickeau } 69904fd306cSNickeau } 70004fd306cSNickeau 70104fd306cSNickeau $editButton = EditButton::create($outlineSection->getLabel()) 70204fd306cSNickeau ->setStartPosition($outlineSection->getStartPosition()) 70304fd306cSNickeau ->setEndPosition($outlineSection->getEndPosition()) 70404fd306cSNickeau ->setOutlineHeadingId($outlineSection->getHeadingId()) 70504fd306cSNickeau ->setOutlineSectionId($sectionSequenceId) 70604fd306cSNickeau ->toComboCallDokuWikiForm(); 70704fd306cSNickeau $sectionCalls[] = $editButton; 70804fd306cSNickeau } 70904fd306cSNickeau 71004fd306cSNickeau } else { 71104fd306cSNickeau // dokuwiki seems to have no section for the content before the first heading 71204fd306cSNickeau $sectionCalls = $outlineSection->getContentCalls(); 71304fd306cSNickeau } 71404fd306cSNickeau 71504fd306cSNickeau $instructionCalls = array_map(function (Call $element) { 71604fd306cSNickeau return $element->getInstructionCall(); 71704fd306cSNickeau }, $sectionCalls); 71804fd306cSNickeau $totalInstructionCalls = array_merge($totalInstructionCalls, $instructionCalls); 71904fd306cSNickeau }; 72004fd306cSNickeau TreeVisit::visit($this->rootSection, $collectCalls); 72104fd306cSNickeau return $totalInstructionCalls; 72204fd306cSNickeau } 72304fd306cSNickeau 72404fd306cSNickeau private function addCallToSection(Call $actualCall) 72504fd306cSNickeau { 72604fd306cSNickeau if ($this->actualHeadingParsingState === DOKU_LEXER_ENTER && !$this->actualSection->hasContentCall()) { 72704fd306cSNickeau $this->actualSection->addHeaderCall($actualCall); 72804fd306cSNickeau } else { 72904fd306cSNickeau // an content heading (not outline) or another call 73004fd306cSNickeau $this->actualSection->addContentCall($actualCall); 73104fd306cSNickeau } 73204fd306cSNickeau } 73304fd306cSNickeau 73404fd306cSNickeau private function enterHeading(Call $actualCall) 73504fd306cSNickeau { 73604fd306cSNickeau $this->actualHeadingParsingState = DOKU_LEXER_ENTER; 73704fd306cSNickeau $this->actualHeadingCall = $actualCall; 73804fd306cSNickeau } 73904fd306cSNickeau 74004fd306cSNickeau private function exitHeading() 74104fd306cSNickeau { 74204fd306cSNickeau $this->actualHeadingParsingState = DOKU_LEXER_EXIT; 74304fd306cSNickeau } 74404fd306cSNickeau 74504fd306cSNickeau /** 74604fd306cSNickeau * @return array - Dokuwiki TOC array format 74704fd306cSNickeau */ 74804fd306cSNickeau public function toTocDokuwikiFormat(): array 74904fd306cSNickeau { 75004fd306cSNickeau 75104fd306cSNickeau $tableOfContent = []; 75204fd306cSNickeau $collectTableOfContent = function (OutlineSection $outlineSection) use (&$tableOfContent) { 75304fd306cSNickeau 75404fd306cSNickeau if (!$outlineSection->hasParent()) { 75504fd306cSNickeau // Root Section, no heading 75604fd306cSNickeau return; 75704fd306cSNickeau } 75804fd306cSNickeau $tableOfContent[] = [ 75904fd306cSNickeau 'link' => '#' . $outlineSection->getHeadingId(), 76004fd306cSNickeau 'title' => $outlineSection->getLabel(), 76104fd306cSNickeau 'type' => 'ul', 76204fd306cSNickeau 'level' => $outlineSection->getLevel() 76304fd306cSNickeau ]; 76404fd306cSNickeau 76504fd306cSNickeau }; 76604fd306cSNickeau TreeVisit::visit($this->rootSection, $collectTableOfContent); 76704fd306cSNickeau return $tableOfContent; 76804fd306cSNickeau 76904fd306cSNickeau } 77004fd306cSNickeau 77104fd306cSNickeau 77204fd306cSNickeau public 77304fd306cSNickeau function toHtmlSectionOutlineCalls(): array 77404fd306cSNickeau { 77504fd306cSNickeau return OutlineVisitor::create($this)->getCalls(); 77604fd306cSNickeau } 77704fd306cSNickeau 77804fd306cSNickeau 77904fd306cSNickeau /** 78004fd306cSNickeau * Fragment Rendering 78104fd306cSNickeau * * does not have any section/edit button 78204fd306cSNickeau * * no outline or edit button for dynamic rendering but closing of atx heading 78304fd306cSNickeau * 78404fd306cSNickeau * The outline processing ({@link Outline::buildOutline()} just close the atx heading 78504fd306cSNickeau * 78604fd306cSNickeau * @return array 78704fd306cSNickeau */ 78804fd306cSNickeau public 78904fd306cSNickeau function toFragmentInstructionCalls(): array 79004fd306cSNickeau { 79104fd306cSNickeau $totalInstructionCalls = []; 79204fd306cSNickeau $collectCalls = function (OutlineSection $outlineSection) use (&$totalInstructionCalls) { 79304fd306cSNickeau 79404fd306cSNickeau $sectionCalls = array_merge( 79504fd306cSNickeau $outlineSection->getHeadingCalls(), 79604fd306cSNickeau $outlineSection->getContentCalls() 79704fd306cSNickeau ); 79804fd306cSNickeau 79904fd306cSNickeau $instructionCalls = array_map(function (Call $element) { 80004fd306cSNickeau return $element->getInstructionCall(); 80104fd306cSNickeau }, $sectionCalls); 80204fd306cSNickeau $totalInstructionCalls = array_merge($totalInstructionCalls, $instructionCalls); 80304fd306cSNickeau }; 80404fd306cSNickeau TreeVisit::visit($this->rootSection, $collectCalls); 80504fd306cSNickeau return $totalInstructionCalls; 80604fd306cSNickeau 80704fd306cSNickeau } 80804fd306cSNickeau 80904fd306cSNickeau /** 81004fd306cSNickeau * Add the label (ie heading text to the cal attribute) 81104fd306cSNickeau * 81204fd306cSNickeau * @return void 81304fd306cSNickeau */ 81404fd306cSNickeau private 81504fd306cSNickeau function saveOutlineToMetadata() 81604fd306cSNickeau { 81704fd306cSNickeau try { 81804fd306cSNickeau $firstChild = $this->rootSection->getFirstChild(); 81904fd306cSNickeau } catch (ExceptionNotFound $e) { 82004fd306cSNickeau // no child 82104fd306cSNickeau return; 82204fd306cSNickeau } 82304fd306cSNickeau if ($firstChild->getLevel() === 1) { 82404fd306cSNickeau $headingCall = $firstChild->getEnterHeadingCall(); 82504fd306cSNickeau // not dokuwiki header ? 82604fd306cSNickeau if ($headingCall->isPluginCall()) { 82704fd306cSNickeau $headingCall->setAttribute(HeadingTag::HEADING_TEXT_ATTRIBUTE, $firstChild->getLabel()); 82804fd306cSNickeau } 82904fd306cSNickeau } 83004fd306cSNickeau 83104fd306cSNickeau } 83204fd306cSNickeau 83304fd306cSNickeau 83404fd306cSNickeau private 83504fd306cSNickeau function storeH1() 83604fd306cSNickeau { 83704fd306cSNickeau try { 83804fd306cSNickeau $outlineSection = $this->getRootOutlineSection()->getFirstChild(); 83904fd306cSNickeau } catch (ExceptionNotFound $e) { 84004fd306cSNickeau // 84104fd306cSNickeau return; 84204fd306cSNickeau } 84304fd306cSNickeau if ($this->markupPath != null && $outlineSection->getLevel() === 1) { 84404fd306cSNickeau $label = $outlineSection->getLabel(); 84504fd306cSNickeau $call = $outlineSection->getEnterHeadingCall(); 84604fd306cSNickeau if ($call->isPluginCall()) { 84704fd306cSNickeau // we support also the dokwuiki header call that does not need the label 84804fd306cSNickeau $call->addAttribute(HeadingTag::PARSED_LABEL, $label); 84904fd306cSNickeau } 85004fd306cSNickeau PageH1::createForPage($this->markupPath)->setDefaultValue($label); 85104fd306cSNickeau } 85204fd306cSNickeau } 85304fd306cSNickeau 85404fd306cSNickeau private 85504fd306cSNickeau function storeTocForMarkupIfAny() 85604fd306cSNickeau { 85704fd306cSNickeau 85804fd306cSNickeau $toc = $this->toTocDokuwikiFormat(); 85904fd306cSNickeau 86004fd306cSNickeau try { 86104fd306cSNickeau $fetcherMarkup = ExecutionContext::getActualOrCreateFromEnv()->getExecutingMarkupHandler(); 86204fd306cSNickeau $fetcherMarkup->toc = $toc; 86304fd306cSNickeau if ($fetcherMarkup->isDocument()) { 86404fd306cSNickeau /** 86504fd306cSNickeau * We still update the global TOC Dokuwiki variables 86604fd306cSNickeau */ 86704fd306cSNickeau global $TOC; 86804fd306cSNickeau $TOC = $toc; 86904fd306cSNickeau } 87004fd306cSNickeau } catch (ExceptionNotFound $e) { 87104fd306cSNickeau // outline is not runnned from a markup handler 87204fd306cSNickeau } 87304fd306cSNickeau 87404fd306cSNickeau if (!isset($this->markupPath)) { 87504fd306cSNickeau return; 87604fd306cSNickeau } 87704fd306cSNickeau 87804fd306cSNickeau try { 87904fd306cSNickeau Toc::createForPage($this->markupPath) 88004fd306cSNickeau ->setValue($toc) 88104fd306cSNickeau ->persist(); 88204fd306cSNickeau } catch (ExceptionBadArgument $e) { 88304fd306cSNickeau LogUtility::error("The Toc could not be persisted. Error:{$e->getMessage()}"); 88404fd306cSNickeau } 88504fd306cSNickeau } 88604fd306cSNickeau 88704fd306cSNickeau public 88804fd306cSNickeau function getMarkupPath(): ?MarkupPath 88904fd306cSNickeau { 89004fd306cSNickeau return $this->markupPath; 89104fd306cSNickeau } 89204fd306cSNickeau 89304fd306cSNickeau 89404fd306cSNickeau private 89504fd306cSNickeau function getTocMaxLevel(): int 89604fd306cSNickeau { 89704fd306cSNickeau return ExecutionContext::getActualOrCreateFromEnv() 89804fd306cSNickeau ->getConfig()->getTocMaxLevel(); 89904fd306cSNickeau } 90004fd306cSNickeau 90104fd306cSNickeau public function setMetaHeaderCapture(bool $metaHeaderCapture): Outline 90204fd306cSNickeau { 90304fd306cSNickeau $this->metaHeaderCapture = $metaHeaderCapture; 90404fd306cSNickeau return $this; 90504fd306cSNickeau } 90604fd306cSNickeau 90704fd306cSNickeau public function getMetaHeaderCapture(): bool 90804fd306cSNickeau { 90904fd306cSNickeau if (isset($this->metaHeaderCapture)) { 91004fd306cSNickeau return $this->metaHeaderCapture; 91104fd306cSNickeau } 91204fd306cSNickeau try { 91304fd306cSNickeau if ($this->markupPath !== null) { 91404fd306cSNickeau $contextPath = $this->markupPath->getPathObject()->toWikiPath(); 91504fd306cSNickeau $hasMainHeaderElement = TemplateForWebPage::create() 91604fd306cSNickeau ->setRequestedContextPath($contextPath) 91704fd306cSNickeau ->hasElement(TemplateSlot::MAIN_HEADER_ID); 91804fd306cSNickeau $isThemeSystemEnabled = ExecutionContext::getActualOrCreateFromEnv() 91904fd306cSNickeau ->getConfig() 92004fd306cSNickeau ->isThemeSystemEnabled(); 92104fd306cSNickeau if ($isThemeSystemEnabled && $hasMainHeaderElement) { 92204fd306cSNickeau return true; 92304fd306cSNickeau } 92404fd306cSNickeau } 92504fd306cSNickeau } catch (ExceptionCast $e) { 92604fd306cSNickeau // to Wiki Path should be good 92704fd306cSNickeau } 92804fd306cSNickeau return false; 92904fd306cSNickeau } 93004fd306cSNickeau 93104fd306cSNickeau public function isSectionEditingEnabled(): bool 93204fd306cSNickeau { 93304fd306cSNickeau 93404fd306cSNickeau return ExecutionContext::getActualOrCreateFromEnv() 93504fd306cSNickeau ->getConfig()->isSectionEditingEnabled(); 93604fd306cSNickeau 93704fd306cSNickeau } 93804fd306cSNickeau 93904fd306cSNickeau 94004fd306cSNickeau} 941