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