1<?php
2
3namespace ComboStrap;
4
5
6use Doku_Handler;
7use syntax_plugin_combo_accordion;
8use syntax_plugin_combo_label;
9use syntax_plugin_combo_tab;
10
11/**
12 * A panel wrap content that is cached
13 * via tabs or accordon
14 */
15class PanelTag
16{
17
18
19    public const SELECTED = 'selected';
20    public const PANEL_MARKUP = 'panel';
21    public const CANONICAL = PanelTag::PANEL_LOGICAL_MARKUP;
22    public const PANEL_LOGICAL_MARKUP = 'panel';
23    public const STATE = 'state';
24    public const TAB_PANEL_MARKUP = 'tabpanel';
25    public const CONTEXT_PREVIEW_ALONE_ATTRIBUTES = array(
26        PanelTag::SELECTED => true,
27        TagAttributes::ID_KEY => "alone",
28        TagAttributes::TYPE_KEY => TabsTag::ENCLOSED_TABS_TYPE
29    );
30    public const CONF_ENABLE_SECTION_EDITING = "panelEnableSectionEditing";
31    /**
32     * When the panel is alone in the edit due to the sectioning
33     */
34    public const CONTEXT_PREVIEW_ALONE = "preview_alone";
35
36    public static function getSelectedValue(TagAttributes $tagAttributes)
37    {
38        $selected = $tagAttributes->getValueAndRemoveIfPresent(PanelTag::SELECTED);
39        if ($selected !== null) {
40            /**
41             * Value may be false/true
42             */
43            return DataType::toBoolean($selected);
44
45        }
46        if ($tagAttributes->hasComponentAttribute(TagAttributes::TYPE_KEY)) {
47            $type = $tagAttributes->getType();
48            if (strtolower($type) === "selected") {
49                return true;
50            }
51        }
52        return false;
53
54    }
55
56
57    public static function handleEnter(TagAttributes $tagAttributes, Doku_Handler $handler, string $markupTag): array
58    {
59
60        $callStack = CallStack::createFromHandler($handler);
61        $parent = $callStack->moveToParent();
62        if ($parent !== false) {
63            $context = $parent->getTagName();
64        } else {
65            /**
66             * The panel may be alone in preview
67             * due to the section edit button
68             */
69            global $ACT;
70            if ($ACT == "preview") {
71                $context = PanelTag::CONTEXT_PREVIEW_ALONE;
72            } else {
73                // to be able to see the old markup
74                $context = $markupTag;
75            }
76        }
77
78        $idManager = ExecutionContext::getActualOrCreateFromEnv()->getIdManager();
79
80        try {
81            $id = $tagAttributes->getId();
82        } catch (ExceptionNotFound $e) {
83            $id = $idManager->generateNewHtmlIdForComponent(self::PANEL_LOGICAL_MARKUP . "-" . $context);
84            $tagAttributes->setId($id);
85        }
86
87        /**
88         * Old deprecated syntax
89         */
90        if ($markupTag == PanelTag::TAB_PANEL_MARKUP) {
91
92            $context = PanelTag::TAB_PANEL_MARKUP;
93
94            $siblingTag = $callStack->moveToPreviousSiblingTag();
95            if ($siblingTag != null) {
96                if ($siblingTag->getTagName() === TabsTag::TAG) {
97                    $tagAttributes->setComponentAttributeValue(PanelTag::SELECTED, false);
98                    while ($descendant = $callStack->next()) {
99                        $descendantName = $descendant->getTagName();
100                        $descendantPanel = $descendant->getAttribute("panel");
101                        $descendantSelected = $descendant->getAttribute(PanelTag::SELECTED);
102                        if (
103                            $descendantName == syntax_plugin_combo_tab::TAG
104                            && $descendantPanel === $id
105                            && $descendantSelected === "true") {
106                            $tagAttributes->setComponentAttributeValue(PanelTag::SELECTED, true);
107                            break;
108                        }
109                    }
110                } else {
111                    LogUtility::msg("The direct element above a " . PanelTag::TAB_PANEL_MARKUP . " should be a `tabs` and not a `" . $siblingTag->getTagName() . "`", LogUtility::LVL_MSG_ERROR, "tabs");
112                }
113            }
114        }
115
116        $id = $idManager->generateNewHtmlIdForComponent(PanelTag::PANEL_LOGICAL_MARKUP);
117        return array(
118            PluginUtility::CONTEXT => $context,
119            TagAttributes::ID_KEY => $id
120        );
121
122    }
123
124    public static function handleExit(Doku_Handler $handler, int $pos, string $markupTag, string $match): array
125    {
126
127        $callStack = CallStack::createFromHandler($handler);
128        $openingTag = $callStack->moveToPreviousCorrespondingOpeningCall();
129        if ($openingTag === false) {
130            LogUtility::error("An exit panel tag does not have any opening tag and was discarded");
131            return [PluginUtility::CONTEXT => "root"];
132        }
133
134        /**
135         * Label is Mandatory in the new syntax. We check it
136         * (Only the presence of at minimum 1 and not the presence in each panel)
137         */
138        if ($markupTag !== PanelTag::TAB_PANEL_MARKUP) {
139            $labelCall = null;
140            while ($actualCall = $callStack->next()) {
141                if ($actualCall->getTagName() === syntax_plugin_combo_label::TAG) {
142                    $labelCall = $actualCall;
143                    break;
144                }
145            }
146            if ($labelCall === null) {
147                LogUtility::error("No label was found in the panel (number " . $openingTag->getIdOrDefault() . "). They are mandatory to create tabs or accordion", PanelTag::PANEL_LOGICAL_MARKUP);
148            }
149        }
150
151
152        /**
153         * End section
154         */
155        $sectionEditing = ExecutionContext::getActualOrCreateFromEnv()
156            ->getConfig()
157            ->getValue(PanelTag::CONF_ENABLE_SECTION_EDITING, 1);
158        if ($sectionEditing) {
159            /**
160             * Section
161             * +1 to go at the line
162             */
163            $startPosition = $openingTag->getPluginData(PluginUtility::POSITION);
164            $id = $openingTag->getAttribute(TagAttributes::ID_KEY);
165            $endPosition = $pos + strlen($match) + 1;
166            $editButtonCall = EditButton::create("Edit panel $id")
167                ->setStartPosition($startPosition)
168                ->setEndPosition($endPosition)
169                ->toComboCallComboFormat();
170            $callStack->moveToEnd();
171            $callStack->insertBefore($editButtonCall);
172        }
173
174        /**
175         * Add p
176         */
177        $callStack->moveToEnd();
178        $callStack->moveToPreviousCorrespondingOpeningCall();
179        $callStack->processEolToEndStack();
180        return array(PluginUtility::CONTEXT => $openingTag->getContext());
181    }
182
183    public static function renderEnterXhtml(TagAttributes $tagAttributes, array $data): string
184    {
185        /**
186         * Section (Edit button)
187         */
188        if (SiteConfig::getConfValue(PanelTag::CONF_ENABLE_SECTION_EDITING, 1)) {
189            $position = $data[PluginUtility::POSITION];
190            $name = IdManager::getOrCreate()->generateNewHtmlIdForComponent(PanelTag::PANEL_LOGICAL_MARKUP);
191            EditButtonManager::getOrCreate()->createAndAddEditButtonToStack($name, $position);
192        }
193
194        $context = $data[PluginUtility::CONTEXT];
195        switch ($context) {
196            case syntax_plugin_combo_accordion::TAG:
197                // A panel in a accordion
198                return "<div class=\"card\">";
199            case PanelTag::TAB_PANEL_MARKUP: // Old deprecated syntax
200            case TabsTag::TAG: // new syntax
201
202
203                try {
204                    $ariaLabelledValue = $tagAttributes->getId() . "-tab";
205                } catch (ExceptionNotFound $e) {
206                    LogUtility::error("No id was found for a panel in the tabs");
207                    $ariaLabelledValue = "unknwon-id-tab";
208                }
209                $tagAttributes
210                    ->addClassName("tab-pane fade")
211                    ->addOutputAttributeValue("role", "tabpanel")
212                    ->addOutputAttributeValue("aria-labelledby", $ariaLabelledValue);
213                $selected = PanelTag::getSelectedValue($tagAttributes);
214                if ($selected) {
215                    $tagAttributes->addClassName("show active");
216                }
217                return $tagAttributes->toHtmlEnterTag("div");
218
219            case PanelTag::CONTEXT_PREVIEW_ALONE:
220                $aloneAttributes = TagAttributes::createFromCallStackArray(PanelTag::CONTEXT_PREVIEW_ALONE_ATTRIBUTES);
221                return TabsTag::openTabPanelsElement($aloneAttributes);
222            default:
223                LogUtility::log2FrontEnd("The context ($context) is unknown in enter rendering", LogUtility::LVL_MSG_ERROR, PanelTag::PANEL_LOGICAL_MARKUP);
224                return "";
225        }
226    }
227
228    public static function renderExitXhtml(array $data): string
229    {
230
231        $xhtml = "";
232        $context = $data[PluginUtility::CONTEXT];
233        switch ($context) {
234            case syntax_plugin_combo_accordion::TAG:
235                $xhtml .= '</div></div>';
236                break;
237            case PanelTag::CONTEXT_PREVIEW_ALONE:
238                $aloneVariable = TagAttributes::createFromCallStackArray(PanelTag::CONTEXT_PREVIEW_ALONE_ATTRIBUTES);
239                $xhtml .= TabsTag::closeTabPanelsElement($aloneVariable);
240                break;
241        }
242
243        /**
244         * End panel
245         */
246        $xhtml .= "</div>";
247        return $xhtml;
248    }
249}
250
251