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