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