1<?php 2 3namespace ComboStrap; 4 5 6/** 7 * Utility class for breadcrumb 8 * 9 * 10 * 11 * https://en.wikipedia.org/wiki/Breadcrumb_navigation#Websites 12 */ 13class BreadcrumbTag 14{ 15 /** 16 * The type of breadcrumb 17 * 18 * Navigation is a markup that should be present 19 * only once in a page 20 */ 21 public const NAVIGATION_TYPE = "nav"; 22 /** 23 * Typography is when a breadcrumb is used in a iterator 24 * for instance as sub-title 25 */ 26 public const TYPOGRAPHY_TYPE = "typo"; 27 public const MARKUP_BLOCK = "breadcrumb"; 28 public const LOGICAL_TAG = "breadcrumb"; 29 public const CANONICAL_HIERARCHICAL = "breadcrumb-hierarchical"; 30 public const DEPTH_ATTRIBUTE = "depth"; 31 const TYPES = [self::TYPOGRAPHY_TYPE, self::NAVIGATION_TYPE]; 32 33 /** 34 * Hierarchical breadcrumbs (you are here) 35 * 36 * This will return the Hierarchical breadcrumbs. 37 * 38 * Config: 39 * - $conf['youarehere'] must be true 40 * - add $lang['youarehere'] if $printPrefix is true 41 * 42 * Metadata comes from here 43 * https://developers.google.com/search/docs/data-types/breadcrumb 44 * 45 * @param TagAttributes|null $tagAttributes 46 * @return string 47 */ 48 public static function toBreadCrumbHtml(TagAttributes $tagAttributes = null): string 49 { 50 51 if ($tagAttributes === null) { 52 $tagAttributes = TagAttributes::createEmpty(self::MARKUP_BLOCK); 53 } 54 55 56 /** 57 * Get the page 58 */ 59 $path = \syntax_plugin_combo_iterator::getContextPathForComponentThatMayBeInFragment($tagAttributes); 60 $actualPath = MarkupPath::createPageFromPathObject($path); 61 62 $type = $tagAttributes->getType(); 63 64 65 /** 66 * Print in function of the depth 67 */ 68 switch ($type) { 69 case self::NAVIGATION_TYPE: 70 /** 71 * https://www.w3.org/TR/wai-aria-practices/examples/breadcrumb/index.html 72 * Arial-label Provides a label that describes the type of navigation provided in the nav element. 73 */ 74 $tagAttributes->addOutputAttributeValue("aria-label", "Hierarchical breadcrumb"); 75 $htmlOutput = $tagAttributes->toHtmlEnterTag("nav"); 76 $htmlOutput .= '<ol class="breadcrumb">'; 77 78 $lisHtmlOutput = self::getLiHtmlOutput($actualPath, true); 79 while (true) { 80 try { 81 $actualPath = $actualPath->getParent(); 82 } catch (ExceptionNotFound $e) { 83 break; 84 } 85 $liHtmlOutput = self::getLiHtmlOutput($actualPath); 86 $lisHtmlOutput = $liHtmlOutput . $lisHtmlOutput; 87 } 88 $htmlOutput .= $lisHtmlOutput; 89 // close the breadcrumb 90 $htmlOutput .= '</ol>'; 91 $htmlOutput .= '</nav>'; 92 return $htmlOutput; 93 case self::TYPOGRAPHY_TYPE: 94 95 try { 96 $requiredDepth = DataType::toInteger($tagAttributes->getValueAndRemoveIfPresent(self::DEPTH_ATTRIBUTE)); 97 } catch (ExceptionBadArgument $e) { 98 LogUtility::error("We were unable to determine the depth attribute. The depth was set to 1. Error: {$e->getMessage()}"); 99 $requiredDepth = 1; 100 } 101 if ($requiredDepth > 1) { 102 SnippetSystem::getFromContext()->attachCssInternalStyleSheet("breadcrumb-$type"); 103 } 104 $htmlOutput = $tagAttributes->toHtmlEnterTag("span"); 105 $lisHtmlOutput = ""; 106 $actualDepth = 0; 107 while (true) { 108 try { 109 $actualPath = $actualPath->getParent(); 110 } catch (ExceptionNotFound $e) { 111 break; 112 } 113 $actualDepth = $actualDepth + 1; 114 $nameOrDefault = $actualPath->getNameOrDefault(); 115 $liHtmlOutput = "<span class=\"breadcrumb-$type-item\">$nameOrDefault</span>"; 116 $lisHtmlOutput = $liHtmlOutput . $lisHtmlOutput; 117 if ($actualDepth >= $requiredDepth) { 118 break; 119 } 120 } 121 $htmlOutput .= $lisHtmlOutput; 122 $htmlOutput .= '</span>'; 123 return $htmlOutput; 124 default: 125 // internal error 126 LogUtility::error("The breadcrumb type ($type) is unknown"); 127 return ""; 128 129 } 130 131 132 } 133 134 /** 135 * @param MarkupPath $page 136 * @param bool $current 137 * @param bool $link 138 * @return string - the list item for the page 139 */ 140 public static function getLiHtmlOutput(MarkupPath $page, bool $current = false, bool $link = true): string 141 { 142 $liClass = ""; 143 $liArial = ""; 144 if ($current) { 145 $liClass = " active"; 146 /** 147 * https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/ 148 * Applied to a link in the breadcrumb set to indicate that it represents the current page. 149 */ 150 $liArial = " aria-current=\"page\""; 151 } 152 $liHtmlOutput = "<li class=\"breadcrumb-item$liClass\"$liArial>"; 153 154 if (FileSystems::exists($page->getPathObject()) && $current === false) { 155 if ($link) { 156 $liHtmlOutput .= $page->getHtmlAnchorLink(self::CANONICAL_HIERARCHICAL); 157 } else { 158 $liHtmlOutput .= $page->getNameOrDefault(); 159 } 160 } else { 161 $liHtmlOutput .= $page->getNameOrDefault(); 162 } 163 $liHtmlOutput .= '</li>'; 164 return $liHtmlOutput; 165 } 166 167 /** 168 * Same rendering for typographic or navigational breadcrumb 169 * @param TagAttributes $tagAttributes 170 * @return string 171 */ 172 public static function render(TagAttributes $tagAttributes): string 173 { 174 175 try { 176 ExecutionContext::getActualOrCreateFromEnv() 177 ->getExecutingMarkupHandler() 178 ->getOutputCacheDependencies() 179 // the output has the data from the requested page 180 ->addDependency(MarkupCacheDependencies::REQUESTED_PAGE_DEPENDENCY) 181 // the data from the requested page is dependent on the name, title or description of the page 182 ->addDependency(MarkupCacheDependencies::PAGE_PRIMARY_META_DEPENDENCY); 183 } catch (ExceptionNotFound $e) { 184 // not a fetcher markup run 185 } 186 187 return BreadcrumbTag::toBreadCrumbHtml($tagAttributes); 188 189 } 190 191 public static function getDefaultBlockAttributes(): array 192 { 193 return [TagAttributes::TYPE_KEY => BreadcrumbTag::NAVIGATION_TYPE]; 194 } 195 196 public static function handleEnter(TagAttributes $tagAttributes): array 197 { 198 if ($tagAttributes->getType() === self::TYPOGRAPHY_TYPE) { 199 return [PluginUtility::DISPLAY => Call::INLINE_DISPLAY]; 200 } else { 201 return [PluginUtility::DISPLAY => Call::BlOCK_DISPLAY]; 202 } 203 } 204 205} 206