1<?php 2 3if (!defined('DOKU_PLUGIN')) die('meh'); 4 5class siteexport_toc 6{ 7 private $emptyNSToc = true; 8 private $functions = null; 9 private $NS = null; 10 public $translation = null; 11 12 public function __construct($functions, $NS) 13 { 14 $this->doDebug = !empty($_REQUEST['tocDebug']); 15 $this->emptyNSToc = !empty($_REQUEST['emptyTocElem']); 16 $this->functions = $functions; 17 $this->NS = $NS; 18 } 19 20 private function shortenByTranslation(&$inputURL, $deepSearch = false) 21 { 22 // Mandatory: we allways want '/' insteadf of ':' here 23 $inputURL = str_replace(':', '/', $inputURL); 24 25 $checkArray = $this->translation ? $this->translation->translations : array(noNS($this->NS)); 26 27 $url = explode('/', $inputURL); 28 29 $URLcount = count($url); 30 for ($i = 0; $i < $URLcount ; $i++) 31 { 32 if (in_array($url[$i], $checkArray)) 33 { 34 // Rauswerfen und weg 35 $url[$i] = ''; 36 break; 37 } 38 39 if (!$deepSearch) 40 { 41 break; 42 } 43 44 // Ok, remove anyway 45 $url[$i] = ''; 46 } 47 48 $inputURL = implode('/', $url); 49 $inputURL = preg_replace("$\/+$", "/", $inputURL); 50 51 if (strlen($inputURL) > 0 && substr($inputURL, 0, 1) == '/') 52 { 53 $inputURL = substr($inputURL, 1); 54 } 55 56 return $inputURL; 57 } 58 59 /** 60 * Build the Java Documentation TOC XML 61 **/ 62 public function __getJavaHelpTOCXML($DATA) { 63 64 if (count($DATA) == 0) { 65 return false; 66 } 67 68 $this->debug("#### STARTING ####"); 69 $TOCXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<toc>"; 70 $MAPXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<map version=\"1.0\">"; 71 72 // Go through the pages 73 $CHECKDATA = array(); 74 $nData = $DATA; 75 $DATA = array(); 76 $check = array(); 77 $startPageID = null; 78 79 foreach ( $nData as $elem ) 80 { 81 // Check if available 82 $anchor = ( !empty($elem['anchor']) ? '#' . $elem['anchor'] : '' ); 83 $elem['url'] = $this->functions->getSiteName($elem['id'], true); // Override - we need a clean name 84 $elem['mapURL'] = $elem['url']; 85 $this->shortenByTranslation($elem['url']); 86 87 // only add an url once 88 if ( in_array($elem['url'], $CHECKDATA) ) { continue; } 89 90 if ( !isset($elem['exists']) ) { 91 resolve_pageid(getNS($elem['id']),$elem['id'],$elem['exists']); 92 $this->functions->debug->message("EXISTS previously not set.", $elem, 1); 93 } 94 95 // if not there, no map ids will be generated 96 $elem['mapID'] = intval($elem['exists']) == 1 ? $this->functions->getMapID($elem['id'], $elem['anchor'], $check) : array(); 97 98 if ( empty($elem['depth']) ) { 99 $elem['depth'] = count(explode('/', $elem['url'])); 100 } 101 $CHECKDATA[] = $elem['url']; 102 103 if ( $startPageID == null ) 104 { 105 $startPageID = $elem['mapID'][0]; 106 } 107 108 if ( empty( $elem['name'] ) || $elem['name'] == noNs($elem['id']) ) { 109 $elem['name'] = $this->functions->getSiteTitle($elem['id']); 110 $this->debug($elem); 111 } 112 113 // Go on building mapXML 114 $this->shortenByTranslation($elem['mapURL'], true); // true to already remove all language stuff - false if not 115 foreach ( $elem['mapID'] as $VIEWID ) { 116 $MAPXML .= "\n\t<mapID target=\"$VIEWID\" url=\"" . $elem['mapURL'] . $anchor . "\"/>"; 117 } 118 119 $elem['tocNS'] = getNS(cleanID($elem['url'])); 120 $elem['tocNS'] = $this->shortenByTranslation($elem['tocNS'], true); 121 $elem['tocNS'] = strlen($elem['tocNS']) > 0 ? explode('/', $elem['tocNS']) : array(); 122 $this->functions->debug->message("This will be the TOC elements data:", $elem, 1); 123 124 $this->__buildTOCTree($DATA, $elem['tocNS'], $elem); 125 } 126 127 $this->debug("#### Writing TOC Tree ####"); 128 $TOCXML .= $this->__writeTOCTree($DATA) . "\n</toc>"; 129 $this->debug("#### DONE: Writing TOC Tree ####"); 130 $MAPXML .= "\n</map>"; 131 132 $this->debug($DATA); 133 $this->debug($TOCXML); 134 $this->debug($MAPXML); 135 136 return array($TOCXML, $MAPXML, $startPageID); 137 } 138 139 /** 140 * Prepare the TOC Tree 141 **/ 142 private function __buildTOCTree(&$DATA, $currentNSArray, $elemToAdd) 143 { 144 global $conf; 145 146 // Actual level 147 if (empty($currentNSArray)) { 148 $elemToAdd['isStartPage'] = noNS($elemToAdd['id']) == $conf['start']; 149 // $key = empty($elemToAdd['name']) || 1==1 ? noNS($elemToAdd['id']) : $elemToAdd['name']; 150 $key = noNS($elemToAdd['id']); 151 $DATA[$key] = $elemToAdd; 152 return; 153 } 154 155 $currentLevel = array_shift($currentNSArray); 156 $nextLevel = &$DATA[$currentLevel]; 157 if (empty($nextLevel)) { 158 $nextLevel = array('pages' => array()); 159 } else { 160 $nextLevel = &$DATA[$currentLevel]['pages']; 161 } 162 163 $this->__buildTOCTree($nextLevel, $currentNSArray, $elemToAdd); 164 } 165 166 /** 167 * Create a single TOC Item 168 **/ 169 private function __TOCItem($item, $depth, $selfClosed = true) 170 { 171 $this->debug($item); 172 $targetID = $item['mapID'][0]; 173 if (empty($targetID)) { 174 $targetID = strtolower($item['name']); 175 } 176 return "\n" . str_repeat("\t", max($depth, 0)+1) . "<tocitem target=\"$targetID\"" . (intval($item['exists']) == 1 ? " text=\"" . $item['name'] . "\"" : "") . ($selfClosed ? '/' : '') . ">"; 177 } 178 179 /** 180 * Create a single TOC Item 181 **/ 182 private function __TOCItemClose($depth) 183 { 184 return "\n" . str_repeat("\t", max($depth, 0)+1) . "</tocitem>"; 185 } 186 187 /** 188 * Write the whole TOC TREE 189 **/ 190 private function __writeTOCTree($CURRENTNODE, $CURRENTNODENAME = null, $DEPTH = 0) { 191 global $conf; 192 193 $XML = ''; 194 $didOpenItem = false; 195 if (!is_array($CURRENTNODE) || empty($CURRENTNODE)) 196 { 197 // errr … no. 198 return $XML; 199 } 200 201 // This is an element! 202 if (!empty($CURRENTNODE['id']) && empty($CURRENTNODE['pages'])) 203 { 204 // This has to be an item - only -! 205 return $this->__TOCItem($CURRENTNODE, $DEPTH); 206 } 207 208 // Look for start page 209 if (!empty($CURRENTNODE[$conf['start']])) 210 { 211 // YAY! StartPage found. 212 $didOpenItem = !(count(empty($CURRENTNODE['pages']) ? $CURRENTNODE : $CURRENTNODE['pages']) == 0); 213 $XML .= $this->__TOCItem($CURRENTNODE[$conf['start']], $DEPTH, !$didOpenItem); 214 unset($CURRENTNODE[$conf['start']]); 215 } else if (!empty($CURRENTNODE['element'])) { 216 $didOpenItem = !(count($CURRENTNODE['pages']) == 0); 217 $XML .= $this->__TOCItem($CURRENTNODE['element'], $DEPTH, !$didOpenItem); 218 unset($CURRENTNODE['element']); 219 } else if ($CURRENTNODENAME != null) { 220 // We have a parent node for what is comming … lets honor that 221 $didOpenItem = !(count($CURRENTNODE) == 0); 222 $XML .= $this->__TOCItem(array('name' => ucwords($CURRENTNODENAME)), $DEPTH, !$didOpenItem); 223 } else { 224 // Woohoo … empty node? do not count up! 225 $DEPTH--; 226 } 227 228 $this->debug("-- This is the current node --"); 229 $this->debug($CURRENTNODE); 230 231 // Circle through the entries 232 foreach (empty($CURRENTNODE['pages']) ? $CURRENTNODE : $CURRENTNODE['pages'] as $NODENAME => $ELEM) 233 { 234 // a node should have more than only one entry … otherwise we will not tell our name! 235 $XML .= $this->__writeTOCTree($ELEM, count($ELEM) >= 1 ? ( !empty($ELEM['name']) ? $ELEM['name'] : $NODENAME ) : null, $DEPTH+1); 236 } 237 238 // Close and return 239 return $XML . ($didOpenItem ? $this->__TOCItemClose($DEPTH) : ''); 240 } 241 242 /** 243 * Build the Eclipse Documentation TOC XML 244 **/ 245 public function __getTOCXML($DATA, $XML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.toc\"?>\n") { 246 247 $pagesArray = array(); 248 249 // Go through the pages 250 foreach ($DATA as $elem) { 251 252 $site = $elem['id']; 253 $elems = explode('/', $this->functions->getSiteName($site)); 254 255 // Strip Site 256 array_pop($elems); 257 258 // build the topic Tree 259 $this->__buildTopicTree($pagesArray, $elems, $site); 260 } 261 262 $XML .= $this->__addXMLTopic($pagesArray, 'toc'); 263 264 return $XML; 265 266 } 267 268 /** 269 * Load the topic Tree for the TOC - recursive 270 **/ 271 private function __buildTopicTree(&$PAGES, $DATA, $SITE, $INSERTDATA = null) { 272 273 if (empty($DATA) || !is_array($DATA)) { 274 275 if ($INSERTDATA == null) 276 { 277 $INSERTDATA = $SITE; 278 } 279 280 // This is already a namespace 281 if (is_array($PAGES[noNS($SITE)])) { 282 // The root already exists! 283 if (!empty($PAGES[noNS($SITE)][noNS($SITE)])) { 284 if (strstr($PAGES[noNS($SITE)][noNS($SITE)], $SITE)) { 285 // The SITE is in the parent Namespace, and the current Namespace has an index with same name 286 $PAGES['__' . noNS($SITE)] = $INSERTDATA; 287 } else { 288 $PAGES['__' . noNS($SITE)] = $PAGES[noNS($SITE)][noNS($SITE)]; 289 $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA; 290 } 291 } else { 292 $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA; 293 } 294 } else { 295 // just a Page 296 $PAGES[noNS($SITE)] = $INSERTDATA; 297 } 298 return; 299 } 300 301 $NS = array_shift($DATA); 302 if (!is_array($PAGES[$NS])) $PAGES[$NS] = empty($PAGES[$NS]) ? array() : array($PAGES[$NS]); 303 $this->__buildTopicTree($PAGES[$NS], $DATA, $SITE, $INSERTDATA); 304 305 return; 306 } 307 308 /** 309 * Build the Topic Tree for TOC.xml 310 **/ 311 private function __addXMLTopic($DATA, $ITEM = 'topic', $LEVEL = 0, $NODENAME = '') { 312 global $conf; 313 314 $DEPTH = str_repeat("\t", $LEVEL); 315 316 if (!is_array($DATA)) { 317 return $DEPTH . '<' . $ITEM . ' label="' . $this->functions->getSiteTitle($DATA) . '" ' . ($ITEM != 'topic' ? 'topic' : 'href') . '="' . $this->functions->getSiteName($DATA) . "\" />\n"; 318 } 319 // Is array from this point on 320 list($indexTitle, $indexFile) = $this->__getIndexItem($DATA, $NODENAME); 321 322 if (empty($indexTitle)) $indexTitle = $this->functions->getSiteTitle($conf['start']); 323 if (!empty($indexFile)) $indexFile = ($ITEM != 'topic' ? 'topic' : 'href') . "=\"$indexFile\""; 324 325 $isEmptyNode = count($DATA) == 1 && empty($indexFile); 326 327 if (!$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0)) { 328 $XML = "$DEPTH<$ITEM label=\"$indexTitle\" $indexFile>"; 329 } else { 330 $XML = ""; 331 } 332 333 if (!$isEmptyNode && count($DATA) > 0) $XML .= "\n"; 334 335 foreach ($DATA as $NODENAME => $NS) { 336 $XML .= $this->__addXMLTopic($NS, (!($this->emptyNSToc || count($DATA) > 1) && $ITEM != 'topic' ? $ITEM : 'topic'), $LEVEL+(!$isEmptyNode ? 1 : 0), $NODENAME); 337 } 338 339 if (!$isEmptyNode && count($DATA) > 0) $XML .= "$DEPTH"; 340 if (!$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0)) { 341 $XML .= "</$ITEM>\n"; 342 } 343 344 return $XML; 345 } 346 347 348 /** 349 * Get the context XML 350 **/ 351 public function __getContextXML($DATA) { 352 353 $XML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.context\"?>\n<contexts>\n"; 354 355 $check = array(); 356 foreach ($DATA as $elem) 357 { 358 $ID = $elem['id']; 359 $meta = p_get_metadata($ID, 'context', true); 360 if (empty($meta['id'])) { continue; } 361 362 $TITLE = empty($meta['title']) ? $this->functions->getSiteTitle($ID) : $meta['title']; 363 364 // support more than one view IDs ... for more than one reference 365 $VIEWIDs = $this->functions->getMapID($elem['id'], $elem['anchor'], $check); 366 367 $DESCRIPTION = $this->functions->xmlEntities(p_get_metadata($ID, 'description abstract')); 368 369 // Build topic Links 370 $url = $this->functions->getSiteName($ID); 371 $this->shortenByTranslation($url); 372 373 $TOPICS = array($url => $TITLE . " (Details)"); 374 $REFS = p_get_metadata($ID, 'relation references', true); 375 if (is_array($REFS)) 376 foreach ($REFS as $REL => $EXISTS) { 377 if (!$EXISTS) { continue; } 378 $TOPICS[$this->functions->getSiteName($REL)] = $this->functions->getSiteTitle($REL); 379 } 380 381 // build XML - include multi view IDs 382 foreach ($VIEWIDs as $VIEWID) { 383 $XML .= "\t<context id=\"$VIEWID\" title=\"$TITLE\">\n"; 384 $XML .= "\t\t<description>$DESCRIPTION</description>\n"; 385 386 foreach ($TOPICS as $URL => $LABEL) { 387 $XML .= "\t\t<topic label=\"$LABEL\" href=\"$URL\" />\n"; 388 } 389 390 $XML .= "\t</context>\n"; 391 } 392 } 393 394 $XML .= "</contexts>"; 395 return $XML; 396 397 } 398 399 /** 400 * Determine if this is an index - and if so, find its Title 401 **/ 402 private function __getIndexItem(&$DATA, $NODENAME = '') { 403 global $conf; 404 405 if (!is_array($DATA)) { return; } 406 407 $indexTitle = ''; 408 $indexFile = ''; 409 foreach ($DATA as $NODE => $indexSearch) { 410 // Skip next Namespaces 411 if (is_array($indexSearch)) { continue; } 412 413 // Skip if this is not a start 414 if ($NODE != $conf['start']) { continue; } 415 416 $indexTitle = $this->functions->getSiteTitle($indexSearch); 417 $indexFile = $indexSearch; 418 unset($DATA[$NODE]); 419 break; 420 } 421 422 if (empty($indexFile) && !empty($DATA[$NODENAME])) { 423 $indexTitle = $this->functions->getSiteTitle($DATA[$NODENAME]); 424 $indexFile = $DATA[$NODENAME]; 425 unset($DATA[$NODENAME]); 426 } 427 428 return array($indexTitle, $this->functions->getSiteName($indexFile)); 429 } 430 431 private $doDebug = false; 432 private static $didDebug = false; 433 public function debug($data, $final = false) { 434 if ( ! $this->doDebug ) { return; } 435 436 if ( !$this->didDebug ) { 437 print "<html><pre>"; 438 $this->didDebug = true; 439 } 440 441 if ( is_array($data) ) { 442 print_r($data); 443 } else { 444 print str_replace("<", "<", str_replace(">", ">", $data));; 445 } 446 447 print "\n\n"; 448 449 if ( $final ) { 450 print "</pre></html>"; 451 exit; 452 } 453 } 454} 455