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