1<?php 2/** 3 * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15 16use ComboStrap\Meta\Api\Metadata; 17use ComboStrap\TagAttribute\StyleAttribute; 18use Doku_Renderer; 19use DokuWiki_Admin_Plugin; 20use syntax_plugin_combo_toc; 21 22class Toc extends Metadata 23{ 24 25 26 const CANONICAL = syntax_plugin_combo_toc::CANONICAL; 27 private ?array $tocData = null; 28 29 30 public static function createForRequestedPage(): Toc 31 { 32 return self::createForPage(MarkupPath::createFromRequestedPage()); 33 } 34 35 public static function getClass(): string 36 { 37 return StyleAttribute::addComboStrapSuffix(self::CANONICAL); 38 } 39 40 /** 41 * @throws ExceptionBadArgument - if the TOC is not an array 42 * @throws ExceptionNotFound - if the TOC variable was not found 43 */ 44 public static function createFromGlobalVariable(): Toc 45 { 46 global $TOC; 47 if ($TOC === null) { 48 throw new ExceptionNotFound("No global TOC variable found"); 49 } 50 return (new Toc()) 51 ->setValue($TOC); 52 } 53 54 public static function createEmpty(): Toc 55 { 56 return new Toc(); 57 } 58 59 60 public function toXhtml(): string 61 { 62 63 $this->buildCheck(); 64 65 if ($this->tocData === null) { 66 return ""; 67 } 68 69 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(self::CANONICAL); 70 71 $toc = $this->tocData; 72 73 $tocMinHeads = Site::getTocMinHeadings(); 74 if (count($toc) < $tocMinHeads) { 75 return ""; 76 } 77 78 /** 79 * Adding toc number style 80 */ 81 try { 82 $css = Outline::getCssNumberingRulesFor(Outline::TOC_NUMBERING); 83 PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(Outline::TOC_NUMBERING, $css); 84 } catch (ExceptionNotEnabled $e) { 85 // not enabled 86 } catch (ExceptionBadSyntax $e) { 87 LogUtility::error("The toc numbering type was unknown", self::CANONICAL); 88 } 89 90 /** 91 * Creating the html 92 */ 93 global $lang; 94 95 // To keep track of the HTML level (levels may be badly encoded) 96 $htmlLevel = 0; 97 $previousLevel = 0; 98 $topTocLevel = Site::getTopTocLevel(); 99 $ulMarkup = ""; 100 foreach ($toc as $tocItem) { 101 102 $actualLevel = $tocItem["level"]; 103 104 /** 105 * Skipping to the first top level 106 */ 107 if ($actualLevel < $topTocLevel) { 108 $previousLevel = $actualLevel; 109 continue; 110 } 111 112 /** 113 * Closing 114 */ 115 $levelDiff = $previousLevel - $actualLevel; 116 switch (true) { 117 case $levelDiff === 0 && (!empty($ulMarkup)): 118 /** 119 * Same level 120 */ 121 $ulMarkup .= "</li>"; 122 break; 123 case ($actualLevel < $previousLevel && !empty($ulMarkup)): 124 /** 125 * One or multiple level up 126 * (from 4 to 2) 127 */ 128 $htmlLevel += $levelDiff; 129 $ulMarkup .= str_repeat("</li></ul>", $levelDiff); 130 $ulMarkup .= "</li>"; 131 break; 132 default: 133 /** 134 * One level down 135 * (We can't go multiple at once) 136 */ 137 $htmlLevel -= 1; 138 $ulMarkup .= "<ul>"; 139 break; 140 } 141 142 $href = $tocItem['link']; 143 $label = $tocItem['title']; 144 $tocLevelClass = StyleAttribute::addComboStrapSuffix("toc-level-$actualLevel"); 145 $ulMarkup .= "<li><a href=\"$href\" class=\"$tocLevelClass\">$label</a>"; 146 /** 147 * Close 148 */ 149 $previousLevel = $actualLevel; 150 } 151 // grand closing 152 $ulMarkup .= str_repeat("</li></ul>", abs($htmlLevel)); 153 $tocHeaderLang = $lang['toc']; 154 $tocHeaderClass = StyleAttribute::addComboStrapSuffix("toc-header"); 155 return <<<EOF 156<p class="$tocHeaderClass">$tocHeaderLang</p> 157$ulMarkup 158EOF; 159 160 161 } 162 163 164 /** 165 * @param Doku_Renderer $renderer 166 * @return bool if the toc need to be shown 167 * 168 * From {@link Doku_Renderer::notoc()} 169 * $this->info['toc'] = false; 170 * when 171 * ~~NOTOC~~ 172 */ 173 public static function showToc(Doku_Renderer $renderer): bool 174 { 175 176 global $ACT; 177 178 /** 179 * Search page, no toc 180 */ 181 if ($ACT === 'search') { 182 return false; 183 } 184 185 /** 186 * If this is another template such as Dokuwiki, we get two TOC. 187 */ 188 if (!Site::isStrapTemplate()) { 189 return false; 190 } 191 192 /** 193 * On the admin page 194 */ 195 if ($ACT === 'admin') { 196 197 global $INPUT; 198 $plugin = null; 199 $class = $INPUT->str('page'); 200 if (!empty($class)) { 201 202 $pluginList = plugin_list('admin'); 203 204 if (in_array($class, $pluginList)) { 205 // attempt to load the plugin 206 /** @var $plugin DokuWiki_Admin_Plugin */ 207 $plugin = plugin_load('admin', $class); 208 } 209 210 if ($plugin !== null) { 211 global $TOC; 212 if (!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet 213 if (!is_array($TOC)) { 214 return false; 215 } else { 216 return true; 217 } 218 219 } 220 221 } 222 223 } 224 225 // return it if set otherwise return true 226 return $renderer->info['toc'] ?? true; 227 228 } 229 230 public function shouldTocBePrinted(): bool 231 { 232 global $conf; 233 return $conf['tocminheads'] && count($this->tocData) >= $conf['tocminheads']; 234 } 235 236 public static function createForPage($page): Toc 237 { 238 return (new Toc()) 239 ->setResource($page); 240 } 241 242 243 /** 244 * @throws ExceptionBadArgument 245 */ 246 public function setValue($value): Toc 247 { 248 if (!is_array($value)) { 249 throw new ExceptionBadArgument("The toc value ($value) is not an array"); 250 } 251 $this->tocData = $value; 252 /** 253 * We don't set the global TOC because 254 * if the global TOC is set {@link tpl_admin()}, will not 255 * ask the toc to the admin plugin 256 */ 257// global $TOC; 258// $TOC = $value; 259 return $this; 260 } 261 262 public function valueIsNotNull(): bool 263 { 264 return $this->tocData !== null; 265 } 266 267 static public function getDataType(): string 268 { 269 return DataType::ARRAY_VALUE; 270 } 271 272 static public function getDescription(): string 273 { 274 return "Table of Contents"; 275 } 276 277 static public function getLabel(): string 278 { 279 return "The table of content for the page"; 280 } 281 282 public static function getName(): string 283 { 284 return "toc"; 285 } 286 287 static public function getPersistenceType(): string 288 { 289 return Metadata::DERIVED_METADATA; 290 } 291 292 static public function isMutable(): bool 293 { 294 return true; 295 } 296 297 public function setFromStoreValueWithoutException($value): Metadata 298 { 299 $this->tocData = $value; 300 return $this; 301 // We can't modify the toc of dokuwiki 302 // This data shows how to get the table of content from dokuwiki 303 // $description = $metaDataStore->getCurrentFromName("description"); 304 // if($description!==null) { 305 // $this->tocData = $description["tableofcontents"]; 306 // } 307 308 } 309 310 /** 311 * @return array 312 * @throws ExceptionNotFound 313 */ 314 public function getValue(): array 315 { 316 $this->buildCheck(); 317 if ($this->tocData === null) { 318 throw new ExceptionNotFound("No toc"); 319 } 320 return $this->tocData; 321 } 322 323 public function getDefaultValue(): array 324 { 325 return []; 326 } 327} 328