1<?php 2 3namespace ComboStrap; 4 5 6use ComboStrap\TagAttribute\StyleAttribute; 7use Doku_Handler; 8 9 10class PageExplorerTag 11{ 12 public const PAGE_TYPE = "page"; 13 public const FOLDER_ICON = "images:page-explorer-folder"; 14 /** 15 * Page canonical and tag pattern 16 */ 17 public const CANONICAL = "page-explorer"; 18 public const HOME_TYPE = "home"; 19 public const PAGE_ATTRIBUTES = "page-attributes"; 20 public const INDEX_INSTRUCTIONS = "index-instructions"; 21 public const NAMESPACE_ATTRIBUTES = "namespace-attributes"; 22 public const TYPE_TREE = "tree"; 23 public const PAGE_INSTRUCTIONS = "page-instructions"; 24 public const PARENT_ATTRIBUTES = "parent-attributes"; 25 public const INDEX_ATTRIBUTES = "index-attributes"; 26 public const NAMESPACE_INSTRUCTIONS = "namespace-instructions"; 27 /** 28 * Tag in Dokuwiki cannot have a `-` 29 * This is the last part of the class 30 */ 31 public const LOGICAL_TAG = self::PAGE_EXPLORER_MARKUP; 32 /** 33 * Namespace attribute 34 * that contains the namespace information 35 * (ie 36 * * a namespace path 37 * * or current, for the namespace of the current requested page 38 */ 39 public const ATTR_NAMESPACE = "ns"; 40 public const LEVEL_UP_ICON = "images:page-explorer-icons8-level-up"; 41 42 public const NTOC_MARKUP = "ntoc"; 43 public const PAGE_EXPLORER_MARKUP = "page-explorer"; 44 45 public const PARENT_INSTRUCTIONS = "parent-instructions"; 46 /** 47 * Attributes on the home node 48 */ 49 public const LIST_TYPE = "list"; 50 public const INDEX_TAG = "index"; 51 /** 52 * The pattern 53 */ 54 public const INDEX_HOME_TAG = "home"; 55 /** 56 * Tag in Dokuwiki cannot have a `-` 57 * This is the last part of the class 58 */ 59 public const LOGICAL_INDEX_TAG = "pageexplorerhome"; 60 public const INDEXES_TAG = [PageExplorerTag::INDEX_HOME_TAG, PageExplorerTag::INDEX_TAG]; 61 /** 62 * Implementation of the namespace 63 * * ie the button (in a collapsible menu) http://localhost:63342/bootstrap-5.0.1-examples/sidebars/index.html 64 * * or the directory in a list menu 65 * 66 * This syntax is not a classic syntax plugin 67 * The instructions are captured at the {@link DOKU_LEXER_END} 68 * state of {@link syntax_plugin_combo_pageexplorer::handle()} 69 * to create/generate the namespaces 70 * 71 */ 72 public const NAMESPACE_LOGICAL_TAG = "pageexplorernamespace"; 73 public const NAMESPACE_SHORT_TAG = "ns"; 74 public const NAMESPACE_ITEM_TAG = "ns-item"; 75 public const NAMESPACE_LONG_TAG = "namespace"; 76 /** 77 * Tag in Dokuwiki cannot have a `-` 78 * This is the last part of the class 79 */ 80 public const PAGE_LOGICAL_TAG = self::PAGE_TAG; 81 /** 82 * The pattern 83 */ 84 public const PAGE_TAG = "page"; 85 public const PAGE_ITEM_TAG = "page-item"; 86 const PARENT_TAG = "parent"; 87 88 89 /** 90 * @param string $html 91 * @param MarkupPath $page 92 * @param array $data - the data array from the handler 93 * @param string $type 94 */ 95 public static function treeProcessLeaf(string &$html, MarkupPath $page, array $data, string $type) 96 { 97 /** 98 * In callstack instructions 99 * <li> 100 * $instructions 101 * </li> 102 */ 103 $pageAttributes = $data[self::PAGE_ATTRIBUTES]; 104 $pageInstructions = $data[self::PAGE_INSTRUCTIONS]; 105 if ($pageInstructions === null && $pageAttributes !== null) { 106 return; 107 } 108 if (!FileSystems::exists($page->getPathObject())) { 109 LogUtility::error("The given leaf page ($page) does not exist and was not added to the page-explorer tree", self::CANONICAL); 110 return; 111 } 112 if ($page->isHidden()) { 113 return; 114 } 115 116 $listItemEnterTag = TagAttributes::createEmpty() 117 ->setLogicalTag(self::CANONICAL . "-tree-$type") 118 ->toHtmlEnterTag("li"); 119 120 $listItemContent = ""; 121 if ($pageInstructions !== null) { 122 try { 123 $listItemContent = MarkupRenderUtility::renderInstructionsToXhtml($pageInstructions, $page->getMetadataForRendering()); 124 } catch (ExceptionCompile $e) { 125 LogUtility::error("Error while rendering the leaf. Error: {$e->getMessage()}", self::CANONICAL); 126 return; 127 } 128 } else { 129 try { 130 $listItemContent = LinkMarkup::createFromPageIdOrPath($page->getWikiId()) 131 ->toAttributes() 132 ->toHtmlEnterTag("a"); 133 $listItemContent .= "{$page->getNameOrDefault()}</a>"; 134 } catch (ExceptionCompile $e) { 135 LogUtility::error("Error while rendering the default tree page. Error: {$e->getMessage()}", self::CANONICAL); 136 } 137 } 138 /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */ 139 $html .= "{$listItemEnterTag}{$listItemContent}</li>"; 140 141 } 142 143 /** 144 * @param WikiPath $namespacePath 145 * @return string the last part with a uppercase letter and where underscore became a space 146 */ 147 public static function toNamespaceName(WikiPath $namespacePath): string 148 { 149 try { 150 return ucfirst(trim(str_replace("_", " ", $namespacePath->getLastNameWithoutExtension()))); 151 } catch (ExceptionNotFound $e) { 152 // root 153 return ""; 154 } 155 } 156 157 /** 158 * A class prefix added in elements 159 * @param string $type 160 * @return string 161 */ 162 public static function getClassPrefix(string $type): string 163 { 164 return self::CANONICAL . "-$type"; 165 } 166 167 public static function handleExit(Doku_Handler $handler) 168 { 169 $callStack = CallStack::createFromHandler($handler); 170 171 /** 172 * Capture the instructions for 173 * {@link syntax_plugin_combo_pageexplorerpage} 174 * {@link syntax_plugin_combo_pageexplorernamespace} 175 * {@link syntax_plugin_combo_pageexplorernamehome} 176 */ 177 $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 178 /** 179 * @var Call[] $namespaceInstructions 180 * @var array $namespaceAttributes 181 */ 182 $namespaceInstructions = null; 183 $namespaceAttributes = null; 184 /** 185 * @var Call[] $templatePageInstructions 186 * @var array $pageAttributes 187 */ 188 $templatePageInstructions = null; 189 $pageAttributes = null; 190 /** 191 * @var Call[] $templateHomeInstructions 192 * @var array $homeAttributes 193 */ 194 $templateHomeInstructions = null; 195 $homeAttributes = null; 196 /** 197 * The instructions for the parent item in a page explorer list 198 * if any 199 * @var Call[] $parentInstructions 200 * @var array $parentAttributes 201 */ 202 $parentInstructions = null; 203 $parentAttributes = null; 204 /** 205 * @var Call[] $actualInstructionsStack 206 */ 207 $actualInstructionsStack = []; 208 while ($callStack->next()) { 209 $actualCall = $callStack->getActualCall(); 210 $tagName = $actualCall->getTagName(); 211 switch ($actualCall->getState()) { 212 case DOKU_LEXER_ENTER: 213 switch ($tagName) { 214 case self::PAGE_LOGICAL_TAG: 215 $pageAttributes = $actualCall->getAttributes(); 216 continue 3; 217 case self::NAMESPACE_LOGICAL_TAG: 218 $namespaceAttributes = $actualCall->getAttributes(); 219 continue 3; 220 case self::LOGICAL_INDEX_TAG: 221 $homeAttributes = $actualCall->getAttributes(); 222 continue 3; 223 case self::PARENT_TAG: 224 $parentAttributes = $actualCall->getAttributes(); 225 continue 3; 226 default: 227 $actualInstructionsStack[] = $actualCall->toCallArray(); 228 continue 3; 229 } 230 case DOKU_LEXER_EXIT: 231 switch ($tagName) { 232 case self::PAGE_LOGICAL_TAG: 233 $templatePageInstructions = $actualInstructionsStack; 234 $actualInstructionsStack = []; 235 continue 3; 236 case self::NAMESPACE_LOGICAL_TAG: 237 $namespaceInstructions = $actualInstructionsStack; 238 $actualInstructionsStack = []; 239 continue 3; 240 case self::LOGICAL_INDEX_TAG: 241 $templateHomeInstructions = $actualInstructionsStack; 242 $actualInstructionsStack = []; 243 continue 3; 244 case PageExplorerTag::PARENT_TAG: 245 $parentInstructions = $actualInstructionsStack; 246 $actualInstructionsStack = []; 247 continue 3; 248 default: 249 $actualInstructionsStack[] = $actualCall->toCallArray(); 250 continue 3; 251 252 } 253 default: 254 $actualInstructionsStack[] = $actualCall->toCallArray(); 255 break; 256 257 } 258 } 259 /** 260 * Remove all callstack from the opening tag 261 */ 262 $callStack->deleteAllCallsAfter($openingTag); 263 264 /** 265 * The container/block should have at minimum an enter tag 266 * to be able to set the {@link action_plugin_combo_sideslotpostprocessing} auto collapsible 267 * parameter 268 */ 269 $openingTag->setPluginData(PageExplorerTag::NAMESPACE_INSTRUCTIONS, $namespaceInstructions); 270 $openingTag->setPluginData(PageExplorerTag::NAMESPACE_ATTRIBUTES, $namespaceAttributes); 271 $openingTag->setPluginData(PageExplorerTag::PAGE_INSTRUCTIONS, $templatePageInstructions); 272 $openingTag->setPluginData(PageExplorerTag::PAGE_ATTRIBUTES, $pageAttributes); 273 $openingTag->setPluginData(PageExplorerTag::INDEX_INSTRUCTIONS, $templateHomeInstructions); 274 $openingTag->setPluginData(PageExplorerTag::INDEX_ATTRIBUTES, $homeAttributes); 275 $openingTag->setPluginData(PageExplorerTag::PARENT_INSTRUCTIONS, $parentInstructions); 276 $openingTag->setPluginData(PageExplorerTag::PARENT_ATTRIBUTES, $parentAttributes); 277 } 278 279 public static function renderEnterTag(TagAttributes $pageExplorerTagAttributes, array $data) 280 { 281 282 $returnedXhtml = ""; 283 /** 284 * Id (id is mandatory for toggle) 285 */ 286 $id = $pageExplorerTagAttributes->getValue(TagAttributes::ID_KEY); 287 if ($id === null) { 288 $id = IdManager::getOrCreate()->generateNewHtmlIdForComponent(PageExplorerTag::CANONICAL); 289 $pageExplorerTagAttributes->setComponentAttributeValue(TagAttributes::ID_KEY, $id); 290 } 291 292 $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 293 294 295 try { 296 297 /** 298 * {@link MarkupCacheDependencies::PAGE_PRIMARY_META_DEPENDENCY} 299 * The cache output is composed of primary metadata 300 * (If it changes, the content change) 301 * 302 * {@link MarkupCacheDependencies::PAGE_SYSTEM_DEPENDENCY} 303 * The content depend on the file system tree 304 * (if a file is added or deleted, the content will change) 305 */ 306 $executionContext 307 ->getExecutingMarkupHandler() 308 ->getOutputCacheDependencies() 309 ->addDependency(MarkupCacheDependencies::PAGE_PRIMARY_META_DEPENDENCY) 310 ->addDependency(MarkupCacheDependencies::PAGE_SYSTEM_DEPENDENCY); 311 } catch (ExceptionNotFound $e) { 312 // no fetcher markup running 313 // ie markup 314 } 315 316 /** 317 * Context 318 */ 319 $requestedContextPath = $executionContext->getContextPath(); 320 321 /** 322 * NameSpacePath determination 323 */ 324 $pageExplorerType = $pageExplorerTagAttributes->getType(); 325 $namespaceAttribute = $pageExplorerTagAttributes->getValueAndRemove(PageExplorerTag::ATTR_NAMESPACE); 326 if ($namespaceAttribute !== null) { 327 WikiPath::addNamespaceEndSeparatorIfNotPresent($namespaceAttribute); 328 $namespacePath = WikiPath::createMarkupPathFromPath($namespaceAttribute); 329 } else { 330 try { 331 $namespacePath = $requestedContextPath->getParent(); 332 } catch (ExceptionNotFound $e) { 333 $namespacePath = WikiPath::createRootNamespacePathOnMarkupDrive(); 334 } 335 try { 336 $executionContext 337 ->getExecutingMarkupHandler() 338 ->getOutputCacheDependencies() 339 ->addDependency(MarkupCacheDependencies::REQUESTED_NAMESPACE_DEPENDENCY); 340 } catch (ExceptionNotFound $e) { 341 // ok 342 } 343 } 344 345 346 /** 347 * Class Prefix 348 */ 349 $componentClassPrefix = PageExplorerTag::getClassPrefix($pageExplorerType); 350 351 352 /** 353 * Rendering 354 */ 355 switch ($pageExplorerType) { 356 default: 357 case PageExplorerTag::LIST_TYPE: 358 359 /** 360 * Class 361 */ 362 $classContainer = "list-group"; 363 $classItem = "list-group-item"; 364 365 /** 366 * Css 367 */ 368 $executionContext 369 ->getSnippetSystem() 370 ->attachCssInternalStyleSheet($componentClassPrefix); 371 372 /** 373 * Create the enter content list tag 374 */ 375 $returnedXhtml .= $pageExplorerTagAttributes 376 ->addClassName($classContainer) 377 ->removeAttributeIfPresent(TagAttributes::WIKI_ID) 378 ->setLogicalTag(PageExplorerTag::CANONICAL) 379 ->toHtmlEnterTag("ul"); 380 381 382 /** 383 * Home 384 */ 385 $indexInstructions = $data[PageExplorerTag::INDEX_INSTRUCTIONS]; 386 $indexAttributes = $data[PageExplorerTag::INDEX_ATTRIBUTES]; 387 $currentIndexPage = MarkupPath::createPageFromPathObject($namespacePath); 388 if (!($indexInstructions === null && $indexAttributes !== null)) { 389 390 if (FileSystems::exists($currentIndexPage)) { 391 392 393 $indexTagAttributes = TagAttributes::createFromCallStackArray($indexAttributes); 394 395 396 /** 397 * Enter home tag 398 */ 399 $indexPageType = "index"; 400 $returnedXhtml .= $indexTagAttributes 401 ->addClassName($classItem) 402 ->setLogicalTag(PageExplorerTag::CANONICAL . "-{$pageExplorerType}-{$indexPageType}") 403 ->toHtmlEnterTag("li"); 404 /** 405 * Content 406 */ 407 if ($indexInstructions !== null) { 408 try { 409 $returnedXhtml .= MarkupRenderUtility::renderInstructionsToXhtml($indexInstructions, $currentIndexPage->getMetadataForRendering()); 410 } catch (ExceptionCompile $e) { 411 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the home. Error: {$e->getMessage()}"); 412 } 413 } else { 414 try { 415 $returnedXhtml .= LinkMarkup::createFromPageIdOrPath($currentIndexPage->getWikiId()) 416 ->toAttributes() 417 ->toHtmlEnterTag("a"); 418 $returnedXhtml .= "{$currentIndexPage->getNameOrDefault()}</a>"; 419 } catch (ExceptionCompile $e) { 420 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the default home. Error: {$e->getMessage()}"); 421 } 422 } 423 /** 424 * End home tag 425 */ 426 $returnedXhtml .= "</li>"; 427 } 428 429 } 430 431 /** 432 * Parent ? 433 */ 434 $parentInstructions = $data[PageExplorerTag::PARENT_INSTRUCTIONS]; 435 $parentAttributes = $data[PageExplorerTag::PARENT_ATTRIBUTES]; 436 if (!($parentInstructions === null && $indexAttributes !== null)) { 437 try { 438 $parentPage = $currentIndexPage->getParent(); 439 if ($parentPage->exists()) { 440 441 $parentTagAttributes = TagAttributes::createFromCallStackArray($parentAttributes); 442 /** 443 * Enter parent tag 444 */ 445 $pageType = "parent"; 446 $returnedXhtml .= $parentTagAttributes 447 ->addClassName($classItem) 448 ->setLogicalTag(PageExplorerTag::CANONICAL . "-{$pageExplorerType}-{$pageType}") 449 ->toHtmlEnterTag("li"); 450 /** 451 * Content 452 */ 453 if ($parentInstructions !== null) { 454 try { 455 $returnedXhtml .= MarkupRenderUtility::renderInstructionsToXhtml($parentInstructions, $parentPage->getMetadataForRendering()); 456 } catch (ExceptionCompile $e) { 457 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the parent instructions. Error: {$e->getMessage()}"); 458 } 459 } else { 460 try { 461 $parentWikiId = $parentPage->getPathObject()->getWikiId(); 462 $returnedXhtml .= LinkMarkup::createFromPageIdOrPath($parentWikiId) 463 ->toAttributes() 464 ->toHtmlEnterTag("a"); 465 $returnedXhtml .= Icon::createFromComboResource(PageExplorerTag::LEVEL_UP_ICON) 466 ->toHtml(); 467 $returnedXhtml .= " {$parentPage->getNameOrDefault()}</a>"; 468 } catch (ExceptionCompile $e) { 469 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the default parent. Error: {$e->getMessage()}"); 470 } 471 } 472 /** 473 * End parent tag 474 */ 475 $returnedXhtml .= "</li>"; 476 } 477 } catch (ExceptionNotFound $e) { 478 // no parent page 479 } 480 481 } 482 483 /** 484 * Children (Namespaces/Pages) 485 */ 486 487 $namespaceEnterTag = TagAttributes::createFromCallStackArray($data[PageExplorerTag::NAMESPACE_ATTRIBUTES]) 488 ->addClassName($classItem) 489 ->setLogicalTag(PageExplorerTag::CANONICAL . "-{$pageExplorerType}-namespace") 490 ->toHtmlEnterTag("li"); 491 $pageEnterTag = TagAttributes::createFromCallStackArray($data[PageExplorerTag::PAGE_ATTRIBUTES]) 492 ->addClassName($classItem) 493 ->setLogicalTag(PageExplorerTag::CANONICAL . "-{$pageExplorerType}-page") 494 ->toHtmlEnterTag("li"); 495 496 497 $pageInstructions = $data[PageExplorerTag::PAGE_INSTRUCTIONS]; 498 $pageAttributes = $data[PageExplorerTag::PAGE_ATTRIBUTES]; 499 $namespaceInstructions = $data[PageExplorerTag::NAMESPACE_INSTRUCTIONS]; 500 $namespaceAttributes = $data[PageExplorerTag::NAMESPACE_ATTRIBUTES]; 501 502 $pageNum = 0; 503 foreach (FileSystems::getChildrenContainer($namespacePath) as $subNamespacePath) { 504 505 // Namespace 506 if (!($namespaceInstructions === null && $namespaceAttributes !== null)) { 507 try { 508 $subNamespacePage = MarkupPath::getIndexPageFromNamespace($subNamespacePath->toAbsoluteId()); 509 } catch (ExceptionBadSyntax $e) { 510 LogUtility::msg("Bad syntax for the namespace $namespacePath. Error: {$e->getMessage()}", LogUtility::LVL_MSG_ERROR, PageExplorerTag::CANONICAL); 511 return false; 512 } 513 if ($subNamespacePage->isHidden()) { 514 continue; 515 } 516 if ($subNamespacePage->exists()) { 517 /** 518 * SubNamespace Enter tag 519 */ 520 $returnedXhtml .= $namespaceEnterTag; 521 522 /** 523 * SubNamespace Content 524 */ 525 if ($namespaceInstructions !== null) { 526 try { 527 $returnedXhtml .= MarkupRenderUtility::renderInstructionsToXhtml($namespaceInstructions, $subNamespacePage->getMetadataForRendering()); 528 } catch (ExceptionCompile $e) { 529 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the sub-namespace. Error: {$e->getMessage()}"); 530 } 531 } else { 532 try { 533 $returnedXhtml .= LinkMarkup::createFromPageIdOrPath($subNamespacePage->getWikiId()) 534 ->toAttributes() 535 ->toHtmlEnterTag("a"); 536 $returnedXhtml .= Icon::createFromComboResource(PageExplorerTag::FOLDER_ICON) 537 ->toHtml(); 538 $returnedXhtml .= " {$subNamespacePage->getNameOrDefault()}</a>"; 539 } catch (ExceptionCompile $e) { 540 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the default namespace. Error: {$e->getMessage()}"); 541 } 542 543 } 544 /** 545 * SubNamespace Exit tag 546 */ 547 $returnedXhtml .= "</li>"; 548 } 549 550 } 551 552 } 553 554 $childrenLeaf = FileSystems::getChildrenLeaf($namespacePath); 555 foreach ($childrenLeaf as $childPagePath) { 556 $childPage = MarkupPath::createPageFromPathObject($childPagePath); 557 if ($childPage->isHidden()) { 558 continue; 559 } 560 if (!($pageInstructions === null && $pageAttributes !== null)) { 561 $pageNum++; 562 if ($currentIndexPage !== null 563 && $childPagePath->getWikiId() !== $currentIndexPage->getWikiId() 564 && FileSystems::exists($childPagePath) 565 ) { 566 /** 567 * Page Enter tag 568 */ 569 $returnedXhtml .= $pageEnterTag; 570 /** 571 * Page Content 572 */ 573 if ($pageInstructions !== null) { 574 575 try { 576 $returnedXhtml .= MarkupRenderUtility::renderInstructionsToXhtmlFromPage($pageInstructions, $childPage); 577 } catch (ExceptionCompile $e) { 578 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the page. Error: {$e->getMessage()}"); 579 } 580 } else { 581 try { 582 $returnedXhtml .= LinkMarkup::createFromPageIdOrPath($childPagePath->getWikiId()) 583 ->toAttributes() 584 ->toHtmlEnterTag("a"); 585 $returnedXhtml .= "{$childPage->getNameOrDefault()}</a>"; 586 } catch (ExceptionCompile $e) { 587 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the default page. Error: {$e->getMessage()}"); 588 } 589 } 590 /** 591 * Page Exit tag 592 */ 593 $returnedXhtml .= "</li>"; 594 } 595 } 596 } 597 598 599 /** 600 * End container tag 601 */ 602 $returnedXhtml .= "</ul>"; 603 return $returnedXhtml; 604 605 case PageExplorerTag::TYPE_TREE: 606 607 /** 608 * Printing the tree 609 * 610 * data-wiki-id, needed for the 611 * javascript that open the tree 612 * to the actual page 613 */ 614 $namespaceId = $namespacePath->getWikiId(); 615 if (!blank($namespaceId)) { 616 $pageExplorerTagAttributes->addOutputAttributeValue("data-" . TagAttributes::WIKI_ID, $namespaceId); 617 } else { 618 $pageExplorerTagAttributes->addBooleanOutputAttributeValue("data-" . TagAttributes::WIKI_ID); 619 } 620 621 622 $snippetId = PageExplorerTag::CANONICAL . "-" . $pageExplorerType; 623 /** 624 * Open the tree until the current page 625 * and make it active 626 */ 627 PluginUtility::getSnippetManager()->attachJavascriptFromComponentId($snippetId); 628 /** 629 * Styling 630 */ 631 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet($snippetId); 632 $returnedXhtml .= $pageExplorerTagAttributes->toHtmlEnterTag("nav") . DOKU_LF; 633 $returnedXhtml .= "<ul>" . DOKU_LF; 634 635 try { 636 $tree = PathTreeNode::buildTreeViaFileSystemChildren($namespacePath); 637 self::treeProcessTree($returnedXhtml, $tree, $data); 638 } catch (ExceptionBadSyntax $e) { 639 $returnedXhtml .= LogUtility::wrapInRedForHtml("Error while rendering the tree sub-namespace. Error: {$e->getMessage()}"); 640 } 641 642 $returnedXhtml .= "</ul>"; 643 $returnedXhtml .= "</nav>"; 644 return $returnedXhtml; 645 646 } 647 } 648 649 /** 650 * Process the 651 * @param string $html - the callstack 652 * @param PathTreeNode $pathTreeNode 653 * @param array $data - the data array from the handler 654 * @throws ExceptionBadSyntax 655 */ 656 public static 657 function treeProcessTree(string &$html, PathTreeNode $pathTreeNode, array $data) 658 { 659 660 /** 661 * Home Page first 662 * @var MarkupPath $homePage 663 */ 664 $homePage = null; 665 /** 666 * @var TreeNode[] $containerTreeNodes 667 */ 668 $containerTreeNodes = []; 669 /** 670 * @var MarkupPath[] $nonHomePages 671 */ 672 $nonHomePages = []; 673 674 /** 675 * Scanning the tree node to 676 * categorize the children as home, page or namespace (container) 677 * @var PathTreeNode[] $children 678 */ 679 $children = $pathTreeNode->getChildren(); 680 foreach ($children as $child) { 681 682 /** 683 * Namespace 684 */ 685 if ($child->hasChildren()) { 686 687 $containerTreeNodes[] = $child; 688 689 } else { 690 /** 691 * Page 692 */ 693 $page = MarkupPath::createPageFromPathObject($child->getPath()); 694 if ($page->isIndexPage()) { 695 $homePage = $page; 696 } else { 697 $nonHomePages[] = $page; 698 } 699 700 } 701 702 } 703 704 /** 705 * First the home page 706 */ 707 if ($homePage !== null) { 708 PageExplorerTag::treeProcessLeaf($html, $homePage, $data, PageExplorerTag::HOME_TYPE); 709 } 710 711 /** 712 * The subdirectories 713 */ 714 $namespaceInstructions = $data[PageExplorerTag::NAMESPACE_INSTRUCTIONS]; 715 foreach ($containerTreeNodes as $containerTreeNode) { 716 717 /** 718 * @var WikiPath $containerPath 719 */ 720 $containerPath = $containerTreeNode->getPath(); 721 722 /** 723 * Entering: Creating in instructions form 724 * the same as in markup form 725 * 726 * <li> 727 * <button data-bs-target="#id" data-bs-collapse="collapse"> 728 * Label 729 * </button> 730 * <div id="id"> 731 * <ul> 732 * <li></li> 733 * <li></li> 734 * .... 735 * </div> 736 * ... 737 */ 738 $html .= "<li>"; 739 740 741 /** 742 * Keep the id unique on the page in order to be able to collapse 743 * the good HTML node 744 */ 745 $namespaceId = ExecutionContext::getActualOrCreateFromEnv()->getIdManager()->generateNewHtmlIdForComponent("page-explorer-{$containerPath->getWikiId()}"); 746 $id = StyleAttribute::addComboStrapSuffix($namespaceId); 747 $html .= TagAttributes::createEmpty() 748 ->addOutputAttributeValue("data-bs-target", "#$id") 749 ->addOutputAttributeValue("data-" . TagAttributes::WIKI_ID, $containerPath->getWikiId()) 750 ->addOutputAttributeValue("data-bs-toggle", "collapse") 751 ->addOutputAttributeValue("aria-expanded", "false") 752 ->addClassName("btn") 753 ->addClassName("align-items-center") 754 ->addClassName("rounded") 755 ->toHtmlEnterTag("button"); 756 757 // Button label 758 759 $subHomePage = MarkupPath::getIndexPageFromNamespace($containerPath->toAbsoluteId()); 760 if ($subHomePage->exists()) { 761 if ($namespaceInstructions !== null) { 762 try { 763 $html .= MarkupRenderUtility::renderInstructionsToXhtml($namespaceInstructions, $subHomePage->getMetadataForRendering()); 764 } catch (ExceptionCompile $e) { 765 $html .= LogUtility::wrapInRedForHtml("Error while rendering the child directory. Error: {$e->getMessage()}"); 766 } 767 } else { 768 $html .= $subHomePage->getNameOrDefault(); 769 } 770 } else { 771 $namespaceName = PageExplorerTag::toNamespaceName($containerPath); 772 $html .= $namespaceName; 773 } 774 // End button 775 $html .= "</button>"; 776 777 /** 778 * Sub and Recursion 779 */ 780 $html .= TagAttributes::createEmpty() 781 ->addClassName("collapse") 782 ->addOutputAttributeValue(TagAttributes::ID_KEY, "$id") 783 ->toHtmlEnterTag("div"); 784 $html .= "<ul>"; 785 self::treeProcessTree($html, $containerTreeNode, $data); 786 $html .= "</ul>"; 787 $html .= "</div>"; 788 789 /** 790 * Closing: Creating in instructions form 791 * the same as in markup form 792 * 793 * </$pageExplorerTreeListTag> 794 * </$pageExplorerTreeTag> 795 */ 796 $html .= "</li>"; 797 798 } 799 800 /** 801 * Then the other pages 802 */ 803 foreach ($nonHomePages as $page) { 804 PageExplorerTag::treeProcessLeaf($html, $page, $data, PageExplorerTag::PAGE_TYPE); 805 } 806 807 808 } 809} 810 811