1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeaunamespace ComboStrap; 4*04fd306cSNickeau 5*04fd306cSNickeau 6*04fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName; 7*04fd306cSNickeauuse ComboStrap\Web\Url; 8*04fd306cSNickeau 9*04fd306cSNickeau/** 10*04fd306cSNickeau * Bundle page from the same namespace 11*04fd306cSNickeau * with {@link FetcherPageBundler::getBundledOutline() corrected outline} 12*04fd306cSNickeau * 13*04fd306cSNickeau * From a wiki app, just add: `?do=combo_pagebundler` 14*04fd306cSNickeau * 15*04fd306cSNickeau */ 16*04fd306cSNickeauclass FetcherPageBundler extends IFetcherAbs implements IFetcherString 17*04fd306cSNickeau{ 18*04fd306cSNickeau 19*04fd306cSNickeau use FetcherTraitWikiPath; 20*04fd306cSNickeau 21*04fd306cSNickeau const CANONICAL = self::NAME; 22*04fd306cSNickeau const NAME = "pagebundler"; 23*04fd306cSNickeau private Outline $bundledOutline; 24*04fd306cSNickeau 25*04fd306cSNickeau public static function createPageBundler(): FetcherPageBundler 26*04fd306cSNickeau { 27*04fd306cSNickeau return new FetcherPageBundler(); 28*04fd306cSNickeau } 29*04fd306cSNickeau 30*04fd306cSNickeau public function buildFromUrl(Url $url): FetcherPageBundler 31*04fd306cSNickeau { 32*04fd306cSNickeau /** 33*04fd306cSNickeau * Just to return the good type 34*04fd306cSNickeau */ 35*04fd306cSNickeau parent::buildFromUrl($url); 36*04fd306cSNickeau return $this; 37*04fd306cSNickeau } 38*04fd306cSNickeau 39*04fd306cSNickeau /** 40*04fd306cSNickeau * @throws ExceptionBadArgument 41*04fd306cSNickeau * @throws ExceptionBadSyntax 42*04fd306cSNickeau * @throws ExceptionNotExists 43*04fd306cSNickeau * @throws ExceptionNotFound 44*04fd306cSNickeau */ 45*04fd306cSNickeau public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherPageBundler 46*04fd306cSNickeau { 47*04fd306cSNickeau parent::buildFromTagAttributes($tagAttributes); 48*04fd306cSNickeau $this->buildOriginalPathFromTagAttributes($tagAttributes); 49*04fd306cSNickeau return $this; 50*04fd306cSNickeau } 51*04fd306cSNickeau 52*04fd306cSNickeau 53*04fd306cSNickeau function getBuster(): string 54*04fd306cSNickeau { 55*04fd306cSNickeau return ""; 56*04fd306cSNickeau } 57*04fd306cSNickeau 58*04fd306cSNickeau /** 59*04fd306cSNickeau * @return Mime 60*04fd306cSNickeau */ 61*04fd306cSNickeau public function getMime(): Mime 62*04fd306cSNickeau { 63*04fd306cSNickeau return Mime::getHtml(); 64*04fd306cSNickeau } 65*04fd306cSNickeau 66*04fd306cSNickeau public function getFetcherName(): string 67*04fd306cSNickeau { 68*04fd306cSNickeau return self::NAME; 69*04fd306cSNickeau } 70*04fd306cSNickeau 71*04fd306cSNickeau public function getFetchString(): string 72*04fd306cSNickeau { 73*04fd306cSNickeau 74*04fd306cSNickeau $outline = $this->getBundledOutline(); 75*04fd306cSNickeau $instructionsCalls = $outline->toHtmlSectionOutlineCalls(); 76*04fd306cSNickeau $mainContent = MarkupRenderer::createFromInstructions($instructionsCalls) 77*04fd306cSNickeau ->setRequestedExecutingPath($this->getStartPath()) 78*04fd306cSNickeau ->setRequestedContextPath($this->getRequestedContextPath()) 79*04fd306cSNickeau ->setRequestedMime($this->getMime()) 80*04fd306cSNickeau ->getOutput(); 81*04fd306cSNickeau 82*04fd306cSNickeau 83*04fd306cSNickeau $startMarkup = $this->getStartPath(); 84*04fd306cSNickeau $title = PageTitle::createForMarkup($startMarkup)->getValueOrDefault(); 85*04fd306cSNickeau $lang = Lang::createForMarkup($startMarkup); 86*04fd306cSNickeau try { 87*04fd306cSNickeau $startMarkupWikiPath = WikiPath::createFromPathObject($startMarkup->getPathObject()); 88*04fd306cSNickeau } catch (ExceptionBadArgument $e) { 89*04fd306cSNickeau /** 90*04fd306cSNickeau * should not happen as this class accepts only wiki path as {@link FetcherPageBundler::setContextPath() context path} 91*04fd306cSNickeau */ 92*04fd306cSNickeau throw new ExceptionRuntimeInternal("We were unable to get the start markup wiki path. Error:{$e->getMessage()}", self::CANONICAL); 93*04fd306cSNickeau } 94*04fd306cSNickeau 95*04fd306cSNickeau $layoutName = PageTemplateName::BLANK_TEMPLATE_VALUE; 96*04fd306cSNickeau try { 97*04fd306cSNickeau $toc = Toc::createEmpty() 98*04fd306cSNickeau ->setValue($this->getBundledOutline()->toTocDokuwikiFormat()); 99*04fd306cSNickeau } catch (ExceptionBadArgument $e) { 100*04fd306cSNickeau // this is an array 101*04fd306cSNickeau throw new ExceptionRuntimeInternal("The toc could not be created. Error:{$e->getMessage()}", self::CANONICAL, 1, $e); 102*04fd306cSNickeau } 103*04fd306cSNickeau try { 104*04fd306cSNickeau return TemplateForWebPage::create() 105*04fd306cSNickeau ->setRequestedTemplateName($layoutName) 106*04fd306cSNickeau ->setRequestedContextPath($startMarkupWikiPath) 107*04fd306cSNickeau ->setRequestedTitle($title) 108*04fd306cSNickeau ->setRequestedLang($lang) 109*04fd306cSNickeau ->setToc($toc) 110*04fd306cSNickeau ->setIsSocial(false) 111*04fd306cSNickeau ->setRequestedEnableTaskRunner(false) 112*04fd306cSNickeau ->setMainContent($mainContent) 113*04fd306cSNickeau ->render(); 114*04fd306cSNickeau } catch (ExceptionBadSyntax|ExceptionNotFound|ExceptionBadArgument $e) { 115*04fd306cSNickeau // layout should be good 116*04fd306cSNickeau throw new ExceptionRuntimeInternal("The $layoutName template returns an error", self::CANONICAL, 1, $e); 117*04fd306cSNickeau } 118*04fd306cSNickeau 119*04fd306cSNickeau 120*04fd306cSNickeau } 121*04fd306cSNickeau 122*04fd306cSNickeau public function getBundledOutline(): Outline 123*04fd306cSNickeau { 124*04fd306cSNickeau 125*04fd306cSNickeau if (isset($this->bundledOutline)) { 126*04fd306cSNickeau return $this->bundledOutline; 127*04fd306cSNickeau } 128*04fd306cSNickeau 129*04fd306cSNickeau $startPath = $this->getStartPath(); 130*04fd306cSNickeau if (FileSystems::exists($startPath)) { 131*04fd306cSNickeau $indexOutline = $this->addFirstSectionIfMissing($startPath->getOutline()); 132*04fd306cSNickeau } else { 133*04fd306cSNickeau $title = PageTitle::createForMarkup($startPath)->getValueOrDefault(); 134*04fd306cSNickeau $content = <<<EOF 135*04fd306cSNickeau====== $title ====== 136*04fd306cSNickeauEOF; 137*04fd306cSNickeau $indexOutline = Outline::createFromMarkup($content, $this->getStartPath(), $this->getRequestedContextPath()); 138*04fd306cSNickeau } 139*04fd306cSNickeau 140*04fd306cSNickeau $childrenPages = MarkupFileSystem::getOrCreate()->getChildren($startPath, FileSystems::LEAF); 141*04fd306cSNickeau foreach ($childrenPages as $child) { 142*04fd306cSNickeau $outer = $this->addFirstSectionIfMissing($child->getOutline()); 143*04fd306cSNickeau Outline::merge($indexOutline, $outer); 144*04fd306cSNickeau } 145*04fd306cSNickeau $this->bundledOutline = $indexOutline; 146*04fd306cSNickeau 147*04fd306cSNickeau return $this->bundledOutline; 148*04fd306cSNickeau 149*04fd306cSNickeau } 150*04fd306cSNickeau 151*04fd306cSNickeau /** 152*04fd306cSNickeau * The path from where the bundle should start 153*04fd306cSNickeau * If this is not an index markup, the index markup will be chosen {@link FetcherPageBundler::getStartPath()} 154*04fd306cSNickeau * 155*04fd306cSNickeau * @throws ExceptionBadArgument - if the path is not a {@link WikiPath web path} 156*04fd306cSNickeau */ 157*04fd306cSNickeau public function setContextPath(Path $requestedPath): FetcherPageBundler 158*04fd306cSNickeau { 159*04fd306cSNickeau $this->setSourcePath(WikiPath::createFromPathObject($requestedPath)); 160*04fd306cSNickeau return $this; 161*04fd306cSNickeau } 162*04fd306cSNickeau 163*04fd306cSNickeau private function getRequestedContextPath(): WikiPath 164*04fd306cSNickeau { 165*04fd306cSNickeau return $this->getSourcePath(); 166*04fd306cSNickeau } 167*04fd306cSNickeau 168*04fd306cSNickeau 169*04fd306cSNickeau /** 170*04fd306cSNickeau * 171*04fd306cSNickeau * @return MarkupPath The index path or the request path is none 172*04fd306cSNickeau * 173*04fd306cSNickeau */ 174*04fd306cSNickeau private function getStartPath(): MarkupPath 175*04fd306cSNickeau { 176*04fd306cSNickeau $requestedPath = MarkupPath::createPageFromPathObject($this->getRequestedContextPath()); 177*04fd306cSNickeau if ($requestedPath->isIndexPage()) { 178*04fd306cSNickeau return $requestedPath; 179*04fd306cSNickeau } 180*04fd306cSNickeau try { 181*04fd306cSNickeau /** 182*04fd306cSNickeau * Parent is an index path in the {@link MarkupFileSystem} 183*04fd306cSNickeau */ 184*04fd306cSNickeau return $requestedPath->getParent(); 185*04fd306cSNickeau } catch (ExceptionNotFound $e) { 186*04fd306cSNickeau // home markup case (should not happen - home page is a index page) 187*04fd306cSNickeau return $requestedPath; 188*04fd306cSNickeau } 189*04fd306cSNickeau 190*04fd306cSNickeau } 191*04fd306cSNickeau 192*04fd306cSNickeau /** 193*04fd306cSNickeau * If a page does not have any h1 194*04fd306cSNickeau * (Case of index page for instance) 195*04fd306cSNickeau * 196*04fd306cSNickeau * If this is the case, the outline is broken. 197*04fd306cSNickeau * @param Outline $outline 198*04fd306cSNickeau * @return Outline 199*04fd306cSNickeau */ 200*04fd306cSNickeau private function addFirstSectionIfMissing(Outline $outline): Outline 201*04fd306cSNickeau { 202*04fd306cSNickeau $rootOutlineSection = $outline->getRootOutlineSection(); 203*04fd306cSNickeau $addFirstSection = false; 204*04fd306cSNickeau try { 205*04fd306cSNickeau $firstChild = $rootOutlineSection->getFirstChild(); 206*04fd306cSNickeau if ($firstChild->getLevel() >= 2) { 207*04fd306cSNickeau $addFirstSection = true; 208*04fd306cSNickeau } 209*04fd306cSNickeau } catch (ExceptionNotFound $e) { 210*04fd306cSNickeau $addFirstSection = true; 211*04fd306cSNickeau } 212*04fd306cSNickeau if ($addFirstSection) { 213*04fd306cSNickeau $enterHeading = Call::createComboCall( 214*04fd306cSNickeau HeadingTag::HEADING_TAG, 215*04fd306cSNickeau DOKU_LEXER_ENTER, 216*04fd306cSNickeau array(HeadingTag::LEVEL => 1), 217*04fd306cSNickeau HeadingTag::TYPE_OUTLINE, 218*04fd306cSNickeau null, 219*04fd306cSNickeau null, 220*04fd306cSNickeau null, 221*04fd306cSNickeau \syntax_plugin_combo_xmlblocktag::TAG 222*04fd306cSNickeau ); 223*04fd306cSNickeau $title = PageTitle::createForMarkup($outline->getMarkupPath())->getValueOrDefault(); 224*04fd306cSNickeau $unmatchedHeading = Call::createComboCall( 225*04fd306cSNickeau HeadingTag::HEADING_TAG, 226*04fd306cSNickeau DOKU_LEXER_UNMATCHED, 227*04fd306cSNickeau [], 228*04fd306cSNickeau null, 229*04fd306cSNickeau $title, 230*04fd306cSNickeau $title, 231*04fd306cSNickeau null, 232*04fd306cSNickeau \syntax_plugin_combo_xmlblocktag::TAG 233*04fd306cSNickeau ); 234*04fd306cSNickeau $exitHeading = Call::createComboCall( 235*04fd306cSNickeau HeadingTag::HEADING_TAG, 236*04fd306cSNickeau DOKU_LEXER_EXIT, 237*04fd306cSNickeau array(HeadingTag::LEVEL => 1), 238*04fd306cSNickeau null, 239*04fd306cSNickeau null, 240*04fd306cSNickeau null, 241*04fd306cSNickeau null, 242*04fd306cSNickeau \syntax_plugin_combo_xmlblocktag::TAG 243*04fd306cSNickeau ); 244*04fd306cSNickeau $h1Section = OutlineSection::createFromEnterHeadingCall($enterHeading) 245*04fd306cSNickeau ->addHeaderCall($unmatchedHeading) 246*04fd306cSNickeau ->addHeaderCall($exitHeading); 247*04fd306cSNickeau $children = $rootOutlineSection->getChildren(); 248*04fd306cSNickeau foreach ($children as $child) { 249*04fd306cSNickeau $child->detachBeforeAppend(); 250*04fd306cSNickeau try { 251*04fd306cSNickeau $h1Section->appendChild($child); 252*04fd306cSNickeau } catch (ExceptionBadState $e) { 253*04fd306cSNickeau LogUtility::error("An error occurs when trying to move the h2 children below the recreated heading title ($title)", self::CANONICAL); 254*04fd306cSNickeau } 255*04fd306cSNickeau } 256*04fd306cSNickeau /** 257*04fd306cSNickeau * Without h1 258*04fd306cSNickeau * The content is in the root heading 259*04fd306cSNickeau */ 260*04fd306cSNickeau foreach ($rootOutlineSection->getContentCalls() as $rootHeadingCall) { 261*04fd306cSNickeau $h1Section->addContentCall($rootHeadingCall); 262*04fd306cSNickeau } 263*04fd306cSNickeau $rootOutlineSection->deleteContentCalls(); 264*04fd306cSNickeau try { 265*04fd306cSNickeau $rootOutlineSection->appendChild($h1Section); 266*04fd306cSNickeau } catch (ExceptionBadState $e) { 267*04fd306cSNickeau LogUtility::error("An error occurs when trying to add the recreated title heading ($title) to the root", self::CANONICAL); 268*04fd306cSNickeau } 269*04fd306cSNickeau } 270*04fd306cSNickeau return $outline; 271*04fd306cSNickeau } 272*04fd306cSNickeau 273*04fd306cSNickeau public function getLabel(): string 274*04fd306cSNickeau { 275*04fd306cSNickeau return self::CANONICAL; 276*04fd306cSNickeau } 277*04fd306cSNickeau} 278