1<?php 2 3namespace ComboStrap; 4 5use syntax_plugin_combo_link; 6use syntax_plugin_combo_menubar; 7 8class 9BrandTag 10{ 11 12 13 const MARKUP = "brand"; 14 public const BRAND_TEXT_FOUND_INDICATOR = "brand_text_found"; 15 public const BRAND_IMAGE_FOUND_INDICATOR = "brand_image_found"; 16 /** 17 * Class needed 18 * https://getbootstrap.com/docs/5.1/components/navbar/#image-and-text 19 */ 20 public const BOOTSTRAP_NAV_BAR_IMAGE_AND_TEXT_CLASS = "d-inline-block align-text-top"; 21 public const URL_ATTRIBUTE = "url"; 22 public const WIDGET_ATTRIBUTE = "widget"; 23 public const ICON_ATTRIBUTE = "icon"; 24 25 public static function handleSpecialEnter(TagAttributes $tagAttributes, \Doku_Handler $handler): array 26 { 27 28 29 /** 30 * Brand text found is updated on exit if there is one 31 * Otherwise by default, false 32 */ 33 $returnedArray = [ 34 self::BRAND_TEXT_FOUND_INDICATOR => false, 35 PluginUtility::CONTEXT => "root" 36 ]; 37 38 39 /** 40 * Context 41 */ 42 $callStack = CallStack::createFromHandler($handler); 43 $parent = $callStack->moveToParent(); 44 if ($parent === false) { 45 return $returnedArray; 46 } 47 $context = $parent->getTagName(); 48 $returnedArray[PluginUtility::CONTEXT] = $context; 49 50 /** 51 * A brand in a menubar is button 52 * by default, if there is a value, we return 53 * if not we check where the brand is 54 */ 55 $value = $tagAttributes->getComponentAttributeValue(self::WIDGET_ATTRIBUTE); 56 if ($value === null) { 57 if ($context === syntax_plugin_combo_menubar::TAG) { 58 $defaultWidget = BrandButton::WIDGET_LINK_VALUE; 59 } else { 60 $defaultWidget = BrandButton::WIDGET_BUTTON_VALUE; 61 } 62 $tagAttributes->addComponentAttributeValue(self::WIDGET_ATTRIBUTE, $defaultWidget); 63 } 64 65 return $returnedArray; 66 67 } 68 69 public static function render(TagAttributes $tagAttributes, int $state, array $data): string 70 { 71 72 /** 73 * Brand Object creation 74 */ 75 $brandName = $tagAttributes->getType(); 76 try { 77 $brandButton = self::createButtonFromAttributes($tagAttributes); 78 } catch (ExceptionCompile $e) { 79 return LogUtility::wrapInRedForHtml("Error while reading the brand data for the brand ($brandName). Error: {$e->getMessage()}"); 80 } 81 82 /** 83 * Add the Brand button Icon / CSS / Javascript snippet 84 */ 85 try { 86 $style = $brandButton->getStyle(); 87 } catch (ExceptionCompile $e) { 88 return LogUtility::wrapInRedForHtml("The style of the {$brandButton->getType()} button ($brandButton) could not be determined. Error: {$e->getMessage()}"); 89 } 90 $snippetId = $brandButton->getStyleScriptIdentifier(); 91 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet($snippetId, $style); 92 93 /** 94 * Link 95 */ 96 try { 97 $tagAttributes = self::mixBrandButtonToTagAttributes($tagAttributes, $brandButton); 98 } catch (ExceptionCompile $e) { 99 return LogUtility::wrapInRedForHtml("Error while getting the link data for the the brand ($brandName). Error: {$e->getMessage()}"); 100 } 101 $context = $data[PluginUtility::CONTEXT]; 102 if ($context === syntax_plugin_combo_menubar::TAG) { 103 $tagAttributes->addOutputAttributeValue("accesskey", "h"); 104 $tagAttributes->addClassName("navbar-brand"); 105 } 106 // Width does not apply to link (otherwise the link got a max-width of 30) 107 $tagAttributes->removeComponentAttributeIfPresent(Dimension::WIDTH_KEY); 108 // Widget also 109 $tagAttributes->removeComponentAttributeIfPresent(self::WIDGET_ATTRIBUTE); 110 $enterAnchor = $tagAttributes 111 ->setType(self::MARKUP) 112 ->setLogicalTag(syntax_plugin_combo_link::TAG) 113 ->toHtmlEnterTag("a"); 114 115 $textFound = $data[BrandTag::BRAND_TEXT_FOUND_INDICATOR]; 116 117 $htmlOutput = ""; 118 /** 119 * In a link widget, we don't want the logo inside 120 * the anchor tag otherwise the underline make a link between the text 121 * and the icon and that's ugly 122 */ 123 $logoShouldBeInAnchorElement = !($brandButton->getWidget() === BrandButton::WIDGET_LINK_VALUE && $textFound); 124 if ($logoShouldBeInAnchorElement) { 125 $htmlOutput .= $enterAnchor; 126 } 127 128 /** 129 * Logo 130 */ 131 $brandImageFound = $data[BrandTag::BRAND_IMAGE_FOUND_INDICATOR] ?? null; 132 if (!$brandImageFound && $brandButton->hasIcon()) { 133 try { 134 $iconAttributes = $brandButton->getIconAttributes(); 135 $iconAttributes = TagAttributes::createFromCallStackArray($iconAttributes); 136 if ($textFound && $context === syntax_plugin_combo_menubar::TAG) { 137 $iconAttributes->addClassName(self::BOOTSTRAP_NAV_BAR_IMAGE_AND_TEXT_CLASS); 138 } 139 $htmlOutput .= Icon::createFromTagAttributes($iconAttributes) 140 ->toHtml(); 141 } catch (ExceptionCompile $e) { 142 143 if ($brandButton->getBrand()->getName() === Brand::CURRENT_BRAND) { 144 145 $documentationLink = PluginUtility::getDocumentationHyperLink("logo", "documentation"); 146 LogUtility::msg("A svg logo icon is not installed on your website. Check the corresponding $documentationLink.", LogUtility::LVL_MSG_INFO); 147 148 } else { 149 150 $htmlOutput .= "The brand icon returns an error. Error: {$e->getMessage()}"; 151 // we don't return because the link is not closed 152 153 } 154 155 } 156 } 157 158 if (!$logoShouldBeInAnchorElement) { 159 $htmlOutput .= $enterAnchor; 160 } 161 162 /** 163 * Special case: 164 * Current brand, no logo, no text 165 * For current brand 166 */ 167 if ( 168 $brandButton->getBrand()->getName() === Brand::CURRENT_BRAND 169 && !$brandButton->hasIcon() 170 && $textFound === false 171 ) { 172 $htmlOutput .= Site::getName(); 173 } 174 175 /** 176 * End of link 177 */ 178 if ($state === DOKU_LEXER_SPECIAL) { 179 $htmlOutput .= "</a>"; 180 } 181 return $htmlOutput; 182 183 } 184 185 /** 186 * An utility constructor to be sure that we build the brand button 187 * with the same data in the handle and render function 188 * @throws ExceptionCompile 189 */ 190 public static function createButtonFromAttributes(TagAttributes $brandAttributes, $type = BrandButton::TYPE_BUTTON_BRAND): BrandButton 191 { 192 $brandName = $brandAttributes->getValue(TagAttributes::TYPE_KEY, Brand::CURRENT_BRAND); 193 $widget = $brandAttributes->getValue(self::WIDGET_ATTRIBUTE, BrandButton::WIDGET_BUTTON_VALUE); 194 $icon = $brandAttributes->getValue(self::ICON_ATTRIBUTE, BrandButton::ICON_SOLID_VALUE); 195 196 $brandButton = (new BrandButton($brandName, $type)) 197 ->setWidget($widget) 198 ->setIconType($icon); 199 200 $width = $brandAttributes->getValueAsInteger(Dimension::WIDTH_KEY); 201 if ($width !== null) { 202 $brandButton->setWidth($width); 203 } 204 $title = $brandAttributes->getValueAndRemoveIfPresent(syntax_plugin_combo_link::TITLE_ATTRIBUTE); 205 if ($title !== null) { 206 $brandButton->setLinkTitle($title); 207 } 208 $color = $brandAttributes->getValueAndRemoveIfPresent(ColorRgb::PRIMARY_VALUE); 209 if ($color !== null) { 210 $brandButton->setPrimaryColor($color); 211 } 212 $secondaryColor = $brandAttributes->getValueAndRemoveIfPresent(ColorRgb::SECONDARY_VALUE); 213 if ($secondaryColor !== null) { 214 $brandButton->setSecondaryColor($secondaryColor); 215 } 216 $handle = $brandAttributes->getValueAndRemoveIfPresent(Tag\FollowTag::HANDLE_ATTRIBUTE); 217 if ($handle !== null) { 218 $brandButton->setHandle($handle); 219 } 220 return $brandButton; 221 } 222 223 /** 224 * @throws ExceptionCompile 225 */ 226 public static function mixBrandButtonToTagAttributes(TagAttributes $tagAttributes, BrandButton $brandButton): TagAttributes 227 { 228 229 /** 230 * Url 231 */ 232 $urlAttribute = self::URL_ATTRIBUTE; 233 $url = $tagAttributes->getValueAndRemoveIfPresent($urlAttribute); 234 if ($url !== null) { 235 $urlTemplate = Template::create($url); 236 $variableDetected = $urlTemplate->getVariablesDetected(); 237 if (sizeof($variableDetected) === 1 && $variableDetected[0] === "path") { 238 try { 239 ExecutionContext::getActualOrCreateFromEnv() 240 ->getExecutingMarkupHandler() 241 ->getOutputCacheDependencies() 242 ->addDependency(MarkupCacheDependencies::REQUESTED_PAGE_DEPENDENCY); 243 } catch (ExceptionNotFound $e) { 244 // not a fetcher markup run 245 } 246 $page = MarkupPath::createFromRequestedPage(); 247 $relativePath = str_replace(":", "/", $page->getWikiId()); 248 $url = $urlTemplate 249 ->setProperty("path", $relativePath) 250 ->render(); 251 } 252 $tagAttributes->addOutputAttributeValue("href", $url); 253 } 254 $tagAttributes->mergeWithCallStackArray($brandButton->getHtmlAttributes()->toCallStackArray()); 255 return $tagAttributes; 256 } 257 258 259} 260