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 = null; 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 $actualLevel = 0; 131 $this->buildOutlineRecursive($startPath,$actualLevel); 132 133 return $this->bundledOutline; 134 135 } 136 137 /** 138 * The path from where the bundle should start 139 * If this is not an index markup, the index markup will be chosen {@link FetcherPageBundler::getStartPath()} 140 * 141 * @throws ExceptionBadArgument - if the path is not a {@link WikiPath web path} 142 */ 143 public function setContextPath(Path $requestedPath): FetcherPageBundler 144 { 145 $this->setSourcePath(WikiPath::createFromPathObject($requestedPath)); 146 return $this; 147 } 148 149 private function getRequestedContextPath(): WikiPath 150 { 151 return $this->getSourcePath(); 152 } 153 154 155 /** 156 * 157 * @return MarkupPath The index path or the request path is none 158 * 159 */ 160 private function getStartPath(): MarkupPath 161 { 162 $requestedPath = MarkupPath::createPageFromPathObject($this->getRequestedContextPath()); 163 if ($requestedPath->isIndexPage()) { 164 return $requestedPath; 165 } 166 try { 167 /** 168 * Parent is an index path in the {@link MarkupFileSystem} 169 */ 170 return $requestedPath->getParent(); 171 } catch (ExceptionNotFound $e) { 172 // home markup case (should not happen - home page is a index page) 173 return $requestedPath; 174 } 175 176 } 177 178 /** 179 * If a page does not have any h1 180 * (Case of index page for instance) 181 * 182 * If this is the case, the outline is broken. 183 * @param Outline $outline 184 * @return Outline 185 */ 186 private function addFirstSectionIfMissing(Outline $outline): Outline 187 { 188 $rootOutlineSection = $outline->getRootOutlineSection(); 189 $addFirstSection = false; 190 try { 191 $firstChild = $rootOutlineSection->getFirstChild(); 192 if ($firstChild->getLevel() >= 2) { 193 $addFirstSection = true; 194 } 195 } catch (ExceptionNotFound $e) { 196 $addFirstSection = true; 197 } 198 if ($addFirstSection) { 199 $enterHeading = Call::createComboCall( 200 HeadingTag::HEADING_TAG, 201 DOKU_LEXER_ENTER, 202 array(HeadingTag::LEVEL => 1), 203 HeadingTag::TYPE_OUTLINE, 204 null, 205 null, 206 null, 207 \syntax_plugin_combo_xmlblocktag::TAG 208 ); 209 $title = PageTitle::createForMarkup($outline->getMarkupPath())->getValueOrDefault(); 210 $unmatchedHeading = Call::createComboCall( 211 HeadingTag::HEADING_TAG, 212 DOKU_LEXER_UNMATCHED, 213 [], 214 null, 215 $title, 216 $title, 217 null, 218 \syntax_plugin_combo_xmlblocktag::TAG 219 ); 220 $exitHeading = Call::createComboCall( 221 HeadingTag::HEADING_TAG, 222 DOKU_LEXER_EXIT, 223 array(HeadingTag::LEVEL => 1), 224 null, 225 null, 226 null, 227 null, 228 \syntax_plugin_combo_xmlblocktag::TAG 229 ); 230 $h1Section = OutlineSection::createFromEnterHeadingCall($enterHeading) 231 ->addHeaderCall($unmatchedHeading) 232 ->addHeaderCall($exitHeading); 233 $children = $rootOutlineSection->getChildren(); 234 foreach ($children as $child) { 235 $child->detachBeforeAppend(); 236 try { 237 $h1Section->appendChild($child); 238 } catch (ExceptionBadState $e) { 239 LogUtility::error("An error occurs when trying to move the h2 children below the recreated heading title ($title)", self::CANONICAL); 240 } 241 } 242 /** 243 * Without h1 244 * The content is in the root heading 245 */ 246 foreach ($rootOutlineSection->getContentCalls() as $rootHeadingCall) { 247 $h1Section->addContentCall($rootHeadingCall); 248 } 249 $rootOutlineSection->deleteContentCalls(); 250 try { 251 $rootOutlineSection->appendChild($h1Section); 252 } catch (ExceptionBadState $e) { 253 LogUtility::error("An error occurs when trying to add the recreated title heading ($title) to the root", self::CANONICAL); 254 } 255 } 256 return $outline; 257 } 258 259 public function getLabel(): string 260 { 261 return self::CANONICAL; 262 } 263 264 private function buildOutlineRecursive(MarkupPath $indexPath, int $actualLevel) 265 { 266 /** 267 * Index Page 268 */ 269 if (FileSystems::exists($indexPath)) { 270 $indexOutline = $this->addFirstSectionIfMissing($indexPath->getOutline()); 271 } else { 272 $title = PageTitle::createForMarkup($indexPath)->getValueOrDefault(); 273 $content = <<<EOF 274====== $title ====== 275EOF; 276 $indexOutline = Outline::createFromMarkup($content, $this->getStartPath(), $this->getRequestedContextPath()); 277 } 278 279 if(!$this->bundledOutline){ 280 $this->bundledOutline = $indexOutline; 281 } else { 282 Outline::merge($this->bundledOutline, $indexOutline, $actualLevel); 283 } 284 285 /** 286 * Children Pages (Same level) 287 */ 288 $childrenPages = MarkupFileSystem::getOrCreate()->getChildren($indexPath, FileSystems::LEAF); 289 foreach ($childrenPages as $child) { 290 if ($child->isSlot()) { 291 continue; 292 } 293 $outer = $this->addFirstSectionIfMissing($child->getOutline()); 294 Outline::merge($this->bundledOutline, $outer, $actualLevel); 295 } 296 $containerPages = MarkupFileSystem::getOrCreate()->getChildren($indexPath, FileSystems::CONTAINER); 297 $nextLevel = $actualLevel+1; 298 foreach ($containerPages as $child) { 299 $this->buildOutlineRecursive($child,$nextLevel); 300 } 301 302 } 303} 304