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->functions->getMapID($elem['id'], $elem['anchor'], $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 /** 256 * internal Sort function 257 * @param unknown_type $a 258 * @param unknown_type $b 259 */ 260 private function sortFunction($a, $b) 261 { 262 $idA = $a['id']; 263 $idB = $b['id']; 264 265 $depthA = explode(':', getNS($idA)); 266 $depthB = explode(':', getNS($idB)); 267 268 for ( $i=0; $i < min(count($depthA), count($depthB)); $i++ ) 269 { 270 $NSCMP = strcmp($depthA[$i], $depthB[$i]); 271 if ( $NSCMP != 0 ) 272 { 273 // Something is different! 274 return $NSCMP; 275 } 276 } 277 278 // There is mor in B, than in A! 279 if ( count($depthA) < count($depthB) ) 280 { 281 return -1; 282 } else if ( count($depthA) > count($depthB) ) 283 { 284 // there is more in A than in B 285 return 1; 286 } 287 288 if ( $NSCMP == 0 ) 289 { 290 // Something is different! 291 return strcmp(noNS($idA), noNS($idB)); 292 } 293 294 return 0; 295 } 296 297 /** 298 * Build the Eclipse Documentation TOC XML 299 **/ 300 public function __getTOCXML($DATA, $XML="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.toc\"?>\n") { 301 302 $pagesArray = array(); 303 304 // Go through the pages 305 foreach ( $DATA as $elem ) { 306 307 $site = $elem['id']; 308 $elems = explode('/', $this->functions->getSiteName($site)); 309 310 // Strip Site 311 array_pop( $elems ); 312 313 // build the topic Tree 314 $this->__buildTopicTree($pagesArray, $elems, $site); 315 } 316 317 $XML .= $this->__addXMLTopic($pagesArray, 'toc'); 318 319 return $XML; 320 321 } 322 323 /** 324 * Load the topic Tree for the TOC - recursive 325 **/ 326 private function __buildTopicTree( &$PAGES, $DATA, $SITE, $INSERTDATA = null ) { 327 328 if ( empty( $DATA ) || !is_array($DATA) ) { 329 330 if ( $INSERTDATA == null ) 331 { 332 $INSERTDATA = $SITE; 333 } 334 335 // This is already a namespace 336 if ( is_array($PAGES[noNS($SITE)]) ) { 337 // The root already exists! 338 if ( !empty($PAGES[noNS($SITE)][noNS($SITE)]) ) { 339 if ( strstr($PAGES[noNS($SITE)][noNS($SITE)], $SITE) ) { 340 // The SITE is in the parent Namespace, and the current Namespace has an index with same name 341 $PAGES['__' . noNS($SITE)] = $INSERTDATA; 342 } else { 343 $PAGES['__' . noNS($SITE)] = $PAGES[noNS($SITE)][noNS($SITE)]; 344 $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA; 345 } 346 } else { 347 $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA; 348 } 349 } else { 350 // just a Page 351 $PAGES[noNS($SITE)] = $INSERTDATA; 352 } 353 return; 354 } 355 356 $NS = array_shift($DATA); 357 if ( !is_array( $PAGES[$NS] ) ) $PAGES[$NS] = empty($PAGES[$NS]) ? array() : array($PAGES[$NS]); 358 $this->__buildTopicTree( $PAGES[$NS], $DATA, $SITE, $INSERTDATA ); 359 360 return; 361 } 362 363 /** 364 * Build the Topic Tree for TOC.xml 365 **/ 366 private function __addXMLTopic($DATA, $ITEM='topic', $LEVEL=0, $NODENAME='') { 367 global $conf; 368 369 $DEPTH = str_repeat("\t", $LEVEL); 370 371 if ( !is_array($DATA) ) { 372 return $DEPTH . '<' . $ITEM . ' label="' . $this->functions->getSiteTitle($DATA) . '" ' . ($ITEM != 'topic' ? 'topic' : 'href' ) . '="' . $this->functions->getSiteName($DATA) . "\" />\n"; 373 } 374 // Is array from this point on 375 list($indexTitle, $indexFile) = $this->__getIndexItem($DATA, $NODENAME); 376 377 if ( empty( $indexTitle) ) $indexTitle = $this->functions->getSiteTitle( $conf['start'] ); 378 if ( !empty( $indexFile) ) $indexFile = ($ITEM != 'topic' ? 'topic' : 'href' ) . "=\"$indexFile\""; 379 380 $isEmptyNode = count($DATA) == 1 && empty($indexFile); 381 382 if ( !$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0) ) 383 $XML = "$DEPTH<$ITEM label=\"$indexTitle\" $indexFile>"; 384 385 if ( !$isEmptyNode && count ($DATA) > 0 ) $XML .= "\n"; 386 387 foreach( $DATA as $NODENAME => $NS ) { 388 389 $XML .= $this->__addXMLTopic($NS, ( !($this->emptyNSToc || count($DATA) > 1) && $ITEM != 'topic' ? $ITEM : 'topic' ), $LEVEL+(!$isEmptyNode ? 1 : 0), $NODENAME); 390 391 } 392 393 if ( !$isEmptyNode && count ($DATA) > 0 ) $XML .= "$DEPTH"; 394 if ( !$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0) ) { 395 $XML .= "</$ITEM>\n"; 396 } 397 398 return $XML; 399 } 400 401 402 /** 403 * Get the context XML 404 **/ 405 public function __getContextXML($DATA) { 406 407 $XML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.context\"?>\n<contexts>\n"; 408 409 $check = array(); 410 foreach ( $DATA as $elem ) 411 { 412 $ID = $elem['id']; 413 $meta = p_get_metadata($ID, 'context', true); 414 if ( empty( $meta['id'] ) ) { continue; } 415 416 $TITLE = empty( $meta['title'] ) ? $this->functions->getSiteTitle($ID) : $meta['title']; 417 418 // support more than one view IDs ... for more than one reference 419 $VIEWIDs = $this->functions->getMapID($elem['id'], $elem['anchor'], $check); 420 421 $DESCRIPTION = $this->functions->xmlEntities(p_get_metadata($ID, 'description abstract')); 422 423 // Build topic Links 424 $url = $this->functions->getSiteName($ID); 425 $this->shortenByTranslation($url); 426 427 $TOPICS = array( $url => $TITLE . " (Details)" ); 428 $REFS = p_get_metadata($ID, 'relation references', true); 429 if ( is_array($REFS) ) 430 foreach ( $REFS as $REL => $EXISTS ) { 431 if ( !$EXISTS ) { continue; } 432 $TOPICS[$this->functions->getSiteName($REL)] = $this->functions->getSiteTitle($REL); 433 } 434 435 // build XML - include multi view IDs 436 foreach ( $VIEWIDs as $VIEWID ) { 437 $XML .= "\t<context id=\"$VIEWID\" title=\"$TITLE\">\n"; 438 $XML .= "\t\t<description>$DESCRIPTION</description>\n"; 439 440 foreach ( $TOPICS as $URL => $LABEL ) { 441 $XML .= "\t\t<topic label=\"$LABEL\" href=\"$URL\" />\n"; 442 } 443 444 $XML .= "\t</context>\n"; 445 } 446 } 447 448 $XML .= "</contexts>"; 449 return $XML; 450 451 } 452 453 /** 454 * Determine if this is an index - and if so, find its Title 455 **/ 456 private function __getIndexItem(&$DATA, $NODENAME='') { 457 global $conf; 458 459 if ( !is_array($DATA) ) { return; } 460 461 $indexTitle = ''; 462 $indexFile = ''; 463 foreach ( $DATA as $NODE => $indexSearch) { 464 // Skip next Namespaces 465 if ( is_array($indexSearch) ) { continue; } 466 467 // Skip if this is not a start 468 if ( $NODE != $conf['start'] ) { continue; } 469 470 $indexTitle = $this->functions->getSiteTitle( $indexSearch ); 471 $indexFile = $indexSearch; 472 unset($DATA[$NODE]); 473 break; 474 } 475 476 if ( empty($indexFile) && !empty($DATA[$NODENAME]) ) { 477 $indexTitle = $this->functions->getSiteTitle( $DATA[$NODENAME] ); 478 $indexFile = $DATA[$NODENAME]; 479 unset($DATA[$NODENAME]); 480 } 481 482 return array($indexTitle, $this->functions->getSiteName($indexFile)); 483 } 484} 485 486?> 487