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