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