1<?php 2/** 3 * Search with Scopes 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author i-net software <tools@inetsoftware.de> 7 * @author Gerry Weissbach <gweissbach@inetsoftware.de> 8 */ 9 10// must be run within Dokuwiki 11if (!defined('DOKU_INC')) die(); 12if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 13 14require_once(DOKU_PLUGIN . 'syntax.php'); 15 16class syntax_plugin_siteexport_toc extends DokuWiki_Syntax_Plugin { 17 18 private $insideToc = false; 19 private $savedToc = array(); 20 private $options = array(); 21 22 private $mergedPages = array(); 23 private $includedPages = array(); 24 private $merghintIds = array(); 25 26 public function getType() { return 'protected'; } 27 public function getPType() { return 'block'; } 28 public function getAllowedTypes() { return array('container'); } 29 public function getSort() { return 100; } 30 31 /** 32 * Connect pattern to lexer 33 */ 34 public function connectTo($mode) { 35 $this->Lexer->addEntryPattern('<toc>(?=.*?</toc>)', $mode, 'plugin_siteexport_toc'); 36 $this->Lexer->addEntryPattern('<toc .+?>(?=.*?</toc>)', $mode, 'plugin_siteexport_toc'); 37 $this->Lexer->addSpecialPattern("\[\[.+?\]\]", $mode, 'plugin_siteexport_toc'); 38 } 39 40 public function postConnect() { 41 $this->Lexer->addExitPattern('</toc.*?>', 'plugin_siteexport_toc'); 42 } 43 44 public function handle($match, $state, $pos, Doku_Handler $handler) { 45 global $ID, $INFO; 46 47 switch ($state) { 48 case DOKU_LEXER_ENTER: 49 50 $this->insideToc = true; 51 $this->options = explode(' ', substr($match, 5, -1)?:""); 52 return array('start' => true, 'pos' => $pos, 'options' => $this->options); 53 54 case DOKU_LEXER_SPECIAL: 55 56 if ($this->insideToc) { 57 58 $link = preg_replace(array('/^\[\[/', '/\]\]$/u'), '', $match); 59 // Split title from URL 60 $link = explode('|', $link, 2); 61 if (!isset($link[1])) { 62 $link[1] = NULL; 63 } else if (preg_match('/^\{\{[^\}]+\}\}$/', $link[1])) { 64 // If the title is an image, convert it to an array containing the image details 65 $link[1] = Doku_Handler_Parse_Media($link[1]); 66 } 67 $link[0] = trim($link[0]); 68 69 if (!(preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u', $link[0]) || 70 preg_match('/^\\\\\\\\[\w.:?\-;,]+?\\\\/u', $link[0]) || 71 preg_match('#^([a-z0-9\-\.+]+?)://#i', $link[0]) || 72 preg_match('<' . PREG_PATTERN_VALID_EMAIL . '>', $link[0]) || 73 preg_match('!^#.+!', $link[0])) 74 ) { 75 76 // Get current depth from call stack 77 $depth = 1; 78 if ($handler->CallWriter instanceof Doku_Handler_List) { 79 80 $calls = array_reverse($handler->CallWriter->calls); 81 $call = $calls[0]; 82 foreach ($calls as $item) { 83 if (in_array($item[0], array('list_item', 'list_open'))) { $call = $item; break; } 84 } 85 86 $listType = null; 87 $depth = $handler->CallWriter->interpretSyntax($call[1][0], $listType)-1; // Minus one because of plus one inside the interpret function 88 } 89 90 if (empty($link[0])) { break; } // No empty elements. This would lead to problems 91 return array($link[0], $link[1], $depth); 92 } else { 93 // use parser! - but with another p 94 $handler->internallink($match, $state, $pos); 95 } 96 } else { 97 // use parser! 98 $handler->internallink($match, $state, $pos); 99 } 100 101 return false; 102 case DOKU_LEXER_UNMATCHED: 103 104 $handler->_addCall('cdata', array($match), $pos); 105 106 return false; 107 case DOKU_LEXER_EXIT: 108 109 $this->insideToc = false; 110 return 'save__meta'; 111 } 112 return false; 113 } 114 115 public function render($mode, Doku_Renderer $renderer, $data) { 116 global $ID, $lang, $INFO; 117 118 list($SID, $NAME, $DEPTH) = $data; 119 120 resolve_pageid(getNS($ID), $SID, $exists = null); 121// $SID = cleanID($SID); // hier kein cleanID, da sonst moeglicherweise der anker verloren geht 122 123 // Render XHTML and ODT 124 if ($mode == 'xhtml' || $mode == 'odt') { 125 126 // TOC Title 127 if (is_array($data) && $data['start'] == true) { 128 129 if (is_Array($data['options'])) { 130 foreach ($data['options'] as $opt) { 131 switch ($opt) { 132 case 'description' : $renderer->meta['sitetoc']['showDescription'] = true; break; 133 case 'notoc' : $renderer->meta['sitetoc']['noTOC'] = true; break; 134 case 'merge' : $renderer->meta['sitetoc']['mergeDoc'] = true; break; 135 case 'nohead' : $renderer->meta['sitetoc']['noTocHeader'] = true; break; 136 case 'mergeheader' : $renderer->meta['sitetoc']['mergeHeader'] = true; break; 137 case 'pagebreak' : $renderer->meta['sitetoc']['pagebreak'] = true; break; 138 case 'mergehint' : $renderer->meta['sitetoc']['mergehint'] = true; break; 139 } 140 } 141 } 142 143 $renderer->section_open("1 sitetoc"); 144 if ($renderer->meta['sitetoc']['noTocHeader'] === false) { 145 $renderer->header($lang['toc'], 1, $data['pos']); 146 } 147 148 return true; 149 } else 150 151 // All Output has been done 152 if (!is_array($data) && $data == 'save__meta') { 153 154 // Close TOC 155 $renderer->section_close(); 156 157 if ($renderer->meta['sitetoc']['noTOC'] === true) { 158 $renderer->doc = preg_replace("/<div.*?sitetoc.*?$/si", "", $renderer->doc); 159 } 160 161 // If this is not set, we may have it as Metadata 162 if (empty($this->mergedPages) && $renderer->meta['sitetoc']['mergeDoc']) { 163 $toc = $renderer->meta['sitetoc']['siteexportTOC']; 164 165 if (is_array($toc)) { 166 foreach ($toc as $tocItem) { 167 $this->mergedPages[] = array($tocItem['id'], $tocItem['depth']); 168 } 169 } 170 171 } 172 173 // If there is some data to be merged 174 if (count($this->mergedPages) > 0) { 175 176 $renderer->doc = ''; // Start fresh! 177 178 $renderer->section_open("1 mergedsite" . ($renderer->meta['sitetoc']['mergehint'] && count($this->mergedPages) > 1 ? ' mergehint' : '')); 179 180 // Prepare lookup Array 181 foreach ($this->mergedPages as $tocItem) { 182 list($this->includedPages[]) = explode('#', $tocItem[0]); 183 } 184 185 // Load the instructions 186 $instr = array(); 187 foreach ($this->mergedPages as $tocElement) { 188 189 list($tocItem, $depth) = $tocElement; 190 $file = wikiFN($tocItem); 191 192 if (@file_exists($file)) { 193 $instructions = p_cached_instructions($file, false, $tocItem); 194 } else { 195 $instructions = p_get_instructions(io_readWikiPage($file, $tocItem)); 196 } 197 198 // Convert Link and header instructions 199 $instructions = $this->_convertInstructions($instructions, $addID = null, $renderer, $depth); 200 201 if ($renderer->meta['sitetoc']['mergeHeader'] && count($this->mergedPages) > 1) { 202 // get a hint for merged pages 203 if ($renderer->meta['sitetoc']['mergehint']) { 204 // only if the first section is already there 205 $mergeHint = p_get_metadata($tocItem, 'mergehint', METADATA_RENDER_USING_SIMPLE_CACHE); 206 if (empty($mergeHint)) { $mergeHint = p_get_metadata($tocItem, 'thema', METADATA_RENDER_USING_SIMPLE_CACHE); } 207 if (empty($mergeHint)) { $mergeHint = tpl_pagetitle($tocItem, true); } 208 $instructions = $this->_mergeWithHeaders($this->_initialHeaderStructure($instructions), $instructions, 1, $mergeHint); 209 } 210 // Merge 211 $instr = $this->_mergeWithHeaders($instr, $instructions, 1); 212 213 } else 214 if ($renderer->meta['sitetoc']['pagebreak']) { 215 $sitepagebreak = array(array( 216 'plugin', 217 array( 218 'siteexport_toctools', 219 array( 220 'pagebreak', 221 null, 222 null 223 ) 224 ) 225 )); 226 $instr = array_merge($instr, $instructions, $sitepagebreak); 227 } else { 228 // Concat 229 $instr = array_merge($instr, $instructions); 230 } 231 } 232 233 if (!empty($instr)) { 234 $this->_cleanAllInstructions($instr, true); 235 236 // print "<pre>"; print_r($instr); print "</pre>"; 237 $this->_render_output($renderer, $mode, $instr); 238 } 239 240 $renderer->section_close(); 241 } 242 return true; 243 } 244 245 // Save the current ID 246 $LNID = $SID; 247 248 // Add ID to flags['mergeDoc'] 249 if ($renderer->meta['sitetoc']['mergeDoc'] === true) { // || (count($renderer->meta['sitetoc']['siteexportTOC']) > 0 && $renderer->meta['sitetoc']['siteexportMergeDoc'] === true) ) { 250 $this->mergedPages[] = array($SID, $DEPTH); 251 resolve_pageid(getNS($ID), $SID, $exists); 252 } else { 253 // // print normal internal link (XHTML odt) 254 $renderer->internallink($LNID, $NAME, null); 255 256 // Display Description underneath 257 if ($renderer->meta['sitetoc']['showDescription'] === true) { 258 $renderer->cdata(p_get_metadata($SID, 'description abstract', true)); 259 } 260 } 261 262 // Render Metadata 263 } else if ($mode == 'metadata') { 264 if (!is_array($data) && $data == 'save__meta') { 265 $renderer->meta['sitetoc']['siteexportTOC'] = $this->savedToc; 266 267 foreach ($this->savedToc as $page) { 268 $renderer->meta['relation']['references'][$page['id']] = $page['exists']; 269 } 270 271 $this->savedToc = array(); 272 } else if (!isset($data['start']) && !isset($data['pos'])) { 273 $this->savedToc[] = $this->__addTocItem($SID, $NAME, $DEPTH, $renderer); 274 } 275 } else { 276 return false; 277 } 278 279 return true; 280 } 281 282 /* 283 * pull apart the ID and create an Entry for the TOC 284 */ 285 private function __addTocItem($id, $name, $depth, $renderer) { 286 global $conf; 287 global $ID; 288 289 // Render Title 290 $default = $renderer->_simpleTitle($id); 291 $exists = false; $isImage = false; $linktype = null; 292 resolve_pageid(getNS($ID), $id, $exists); 293 $name = $renderer->_getLinkTitle($name, $default, $isImage, $id, $linktype); 294 295 //keep hash anchor 296 list($id, $hash) = explode('#', $id, 2); 297 if (!empty($hash)) $hash = $renderer->_headerToLink($hash); 298 299 // Build Sitetoc Item 300 $item = array(); 301 $item['id'] = $id; 302 $item['name'] = $name; 303 $item['anchor'] = $hash; 304 $item['depth'] = $depth; 305 $item['exists'] = $exists; 306 if (!$conf['skipacl'] && auth_quickaclcheck($item['id']) < AUTH_READ) { 307 return false; 308 } 309 310 return $item; 311 } 312 313 /* 314 * Render the output of one page 315 */ 316 private function _render_output($renderer, $mode, $instr) { 317 global $ID; 318 319 // Section IDs 320 // $addID = sectionID($addID, $check); //not possible to use a:b:c for id 321 322 if ($mode == 'xhtml') { 323 324 //--------RENDER 325 //renderer information(TOC build / Cache used) 326 $info = array(); 327 $content = p_render($mode, $instr, $info); 328 329 //Remove TOC`s, section edit buttons and tags 330 $content = $this->_cleanXHTML($content); 331 332 // embed the included page 333 // $renderer->doc .= '<div class="include">'; 334 //add an anchor to find start of a inserted page 335 // $renderer->doc .= "<a name='$addID' id='$addID'>"; 336 $renderer->doc .= $content; 337 // $renderer->doc .= '</div>'; 338 } else if ($mode == 'odt') { 339 340 // Loop through the instructions 341 foreach ($instr as $instruction) { 342 // Execute the callback against the Renderer 343 call_user_func_array(array($renderer, $instruction[0]), $instruction[1]); 344 } 345 } 346 } 347 348 /* 349 * Corrects relative internal links and media and 350 * converts headers of included pages to subheaders of the current page 351 */ 352 private function _convertInstructions($instr, $id, &$renderer, $depth = 1) { 353 global $ID; 354 global $conf; 355 356 $n = count($instr); 357 358 for ($i = 0; $i < $n; $i++) { 359 //internal links(links inside this wiki) an relative links 360 if ((substr($instr[$i][0], 0, 12) == 'internallink')) { 361 $this->_convert_link($renderer, $instr[$i], $id); 362 } 363 else if ((substr($instr[$i][0], 0, 13) == 'internalmedia')) { 364 $this->_convert_media($renderer, $instr[$i], $id); 365 } 366 else if ((substr($instr[$i][0], 0, 6) == 'header')) { 367 $this->_convert_header($renderer, $instr[$i], $depth-1); // -1 because the depth starts at 1 368 } 369 else if ((substr($instr[$i][0], 0, 12) == 'section_open')) { 370 $this->_convert_section($renderer, $instr[$i], $depth-1); // -1 because the depth starts at 1 371 } 372 } 373 374 //if its the document start, cut off the first element(document information) 375 if ($instr[0][0] == 'document_start') 376 return array_slice($instr, 1, -1); 377 else 378 return $instr; 379 } 380 381 /* 382 * Convert link of given instruction 383 */ 384 private function _convert_link(&$renderer, &$instr, $id) { 385 global $ID; 386 387 $exists = false; 388 389 resolve_pageid(getNS($id), $instr[1][0], $exists); 390 list($pageID, $pageReference) = explode("#", $instr[1][0], 2); 391 392 if (in_array($pageID, $this->includedPages)) { 393 // Crate new internal Links 394 $check = null; 395 396 // Either get existing reference or create from first heading. If still not there take the alternate ID 397 $pageNameLink = empty($pageReference) ? sectionID($pageID, $check) : $pageReference; 398 399 $instr[1][0] = $ID . "#" . $pageNameLink; 400 401 } else { 402 // Convert external Links to plain Text 403 404 $instr = array( 405 "cdata", 406 array($instr[1][1]), 407 $instr[2] 408 ); 409 } 410 } 411 412 /* 413 * Convert internalmedia of given instruction 414 */ 415 private function _convert_media(&$renderer, &$instr, $id) { 416 global $ID; 417 418 // Resolvemedia returns the absolute path to media by reference 419 $exists = false; 420 resolve_mediaid(getNS($id), $instr[1][0], $exists); 421 } 422 423 /** 424 * @param integer $depth 425 */ 426 private function _convert_header(&$renderer, &$instr, $depth) { 427 // More Depth! 428 $instr[1][1] += $depth; 429 } 430 431 /** 432 * @param integer $depth 433 */ 434 private function _convert_section(&$renderer, &$instr, $depth) { 435 // More Depth! 436 $instr[1][0] += $depth; 437 } 438 439 private function _mergeWithHeaders($existing, $newInstructions, $level = 1, $mergeHint = array()) { 440 441 $returnInstructions = array(); 442 $preparedInstructions = array(); 443 $existingStart = $existingEnd = 0; 444 $firstRun = true; 445 446 while ($this->_findNextHeaderSection($existing, $level, $existingStart, $existingEnd)) { 447 448 if ($firstRun) { 449 $returnInstructions = array_merge($returnInstructions, array_slice($existing, 0, $existingStart)); 450 $firstRun = false; 451 } 452 453 $currentSlice = array_slice($existing, $existingStart, $existingEnd-$existingStart); 454 455 // Find matching part with headername 456 $newStart = $newEnd = 0; 457 if ($this->_findNextHeaderSection($newInstructions, $level, $newStart, $newEnd, $currentSlice[0][1][0])) { 458 459 $newSlice = array_slice($newInstructions, $newStart, $newEnd-$newStart); 460 if ($newSlice[0][0] == 'header') 461 array_shift($newSlice); // Remove Heading 462 463 // merge found parts on next level. 464 $returnedInstructions = $this->_mergeWithHeaders($currentSlice, $newSlice, $level+1, $mergeHint); 465 466 // Put them at the end! 467 $preparedInstructions = array_merge($preparedInstructions, $returnedInstructions); 468 469 // Remove from input 470 array_splice($newInstructions, $newStart, $newEnd-$newStart); 471 } else { 472 // Nothing else found 473 $preparedInstructions = array_merge($preparedInstructions, $currentSlice); 474 } 475 476 $existingStart = $existingEnd; 477 } 478 479 // Append the rest 480 $returnInstructions = array_merge($returnInstructions, array_slice($existing, $existingStart)); 481 482 // Check for section close inconsistencies and put one at the very end ... 483 $section_postpend = array(); 484 if ( 485 ( 486 ($tmp1 = array_slice($newInstructions, -1)) 487 && ($tmp1[0][0] == 'section_close') 488 ) 489 && 490 ( 491 ($tmp2 = array_slice($newInstructions, -2)) 492 && ($tmp2[0][0] == 'section_close') 493 ) 494 ) { 495 $section_postpend = array_splice($newInstructions, -1); 496 } 497 if ( 498 ( 499 ($tmp3 = array_slice($returnInstructions, -1)) 500 && ($tmp3[0][0] == 'section_close') 501 ) 502 && 503 ( 504 ($tmp4 = array_slice($returnInstructions, -2)) 505 && ($tmp4[0][0] == 'section_close') 506 ) 507 ) { 508 $section_postpend = array_merge($section_postpend, array_splice($returnInstructions, -1)); 509 } 510 511 // What if there are headings left inside the $newInstructions????? 512 // Find matching part with headername 513 $newStart = $newEnd = 0; 514 $section_prepend = array(); 515 if ($this->_findNextHeaderSection($newInstructions, $level, $newStart, $newEnd)) { 516 // If there are header in here, build a prepend and have the rest at the end 517 $section_prepend = array_splice($newInstructions, 0, $newStart); 518 } else { 519 // If not, prepend all of it. 520 $section_prepend = $newInstructions; 521 $newInstructions = array(); 522 } 523 524 $this->_insertMergeHint($section_prepend, $mergeHint); 525 526 $returnInstructions = array_merge($returnInstructions, $section_prepend, $preparedInstructions, $newInstructions, $section_postpend); 527 528 return $returnInstructions; 529 } 530 531 /** 532 * @param integer $level 533 */ 534 private function _findNextHeaderSection($section, $level, &$start, &$end, $headerName = null) { 535 536 $inCount = count($section); 537 $currentSlice = -1; 538 539 // Find Level 1 Header that matches. 540 for ($i = $start; $i < $inCount; $i++) { 541 542 $instruction = $section[$i]; 543 $end = $i; // Or it will be lost and a section close will be missing. 544 545 // First Level Header 546 if ($instruction[0] == 'header' && $instruction[1][1] == $level) { 547 548 if ($currentSlice > 0) { 549 return true; 550 } 551 552 if ($headerName == null || ($headerName == $instruction[1][0])) { 553 // Begin of new slice ... 554 $start = $currentSlice = $i; 555 } 556 } 557 } 558 559 // Nothing found 560 $end = $i; // Or it will be lost and a section close will be missing. 561 return $currentSlice > 0; 562 } 563 564 private function _cleanAllInstructions(&$instr, $advanced=false) { 565 $this->_cleanInstructions($instr, '/p_(close|open)/'); 566 $this->_cleanInstructions($instr, '/section_(close|open)/'); 567 $this->_cleanInstructions($instr, '/listu_(close|open)/'); 568 $this->_cleanInstructions($instr, '/listo_(close|open)/'); 569 570 if ( !$advanced ) { 571 return; 572 } 573 574 // if the last element is a pagebreak or toctools entry, remove it. 575 if ($instr[count($instr)-1][1][0] == 'siteexport_toctools' || $instr[count($instr)-1][1][1][0] == 'pagebreak' ) { 576 $instr = array_slice($instr, 0, -1); 577 } 578 579 // check top down: no toctools directly after header 580 581 $currentMergeHint = null; 582 $insideTocBlock = false; 583 for( $i=0; $i<count($instr); $i++ ) { 584 585 $hasMoreEntries = count($instr)-1 > $i; 586 587 if ( $instr[$i][0] == 'header' ) { 588 if ( $hasMoreEntries && $instr[$i+1][1][0] == 'siteexport_toctools' ) { 589 $currentMergeHint = $instr[$i+1]; 590 591 // THERE SHOULD BE NO MERGE_HINT DIRECTLY AFTER THE HEADER 592 // print "<p>Removing Mergehint after header</p>"; 593 array_splice($instr, $i+1, 1); 594 continue; 595 } 596 } 597 598 if ( $instr[$i][1][0] == 'siteexport_toctools' ) { 599 if ( $currentMergeHint != null && $instr[$i][1][1][2] == $currentMergeHint[1][1][2] ) { 600 // print "<p>Removing Mergehint in between </p>"; 601 array_splice($instr, $i--, 1); 602 } else { 603 // print "<p>Resetting Mergehint '" . $instr[$i][1][1][2] . "' == '" . $currentMergeHint[1][1][2] . "'</p>"; 604 $currentMergeHint = $instr[$i]; 605 } 606 } 607 } 608 } 609 610 /** 611 * @param string $tag 612 */ 613 private function _cleanInstructions(&$instructions, $tag) { 614 615 616/* 617 print "<pre>"; 618 print "$tag ->\n"; 619 print_r($instructions); 620 print "</pre>"; 621//*/ 622 $inCount = count($instructions); 623 for ($i = 0; $i < $inCount; $i++) { 624 625 // Last instruction 626 if ($i == $inCount-1) { 627 break; 628 } 629 630 if (preg_match($tag, $instructions[$i][0]) && preg_match($tag, $instructions[$i+1][0]) && $instructions[$i][0] != $instructions[$i+1][0]) { 631/* 632 print "<pre>"; 633 print "Removed ->\n"; 634 print_r($instructions[$i-1]); 635 print "---\n"; 636 print_r($instructions[$i]); 637 print_r($instructions[$i+1]); 638 print "---\n"; 639 print_r($instructions[$i+2]); 640 print "</pre>"; 641//*/ 642 643 // found different tags, but both match the expression and follow each other - so they can be elliminated 644 array_splice($instructions, $i, 2); 645 $inCount -= 2; 646 $i--; 647 } 648 } 649/* 650 print "<pre>"; 651 print "$tag ->\n"; 652 print_r($instructions); 653 print "</pre>"; 654//*/ 655 } 656 657 /** 658 * Strip everything except for the headers 659 */ 660 private function _initialHeaderStructure($instructions) { 661 $inCount = count($instructions); 662 for ($i = 0; $i < $inCount; $i++) { 663 664 // Last instruction 665 if ($i == $inCount-1) { 666 break; 667 } 668 669 if (!in_array($instructions[$i][0], array('header', 'section_open', 'section_close', 'p_open', 'p_close'))) { 670 // found non-matching 671 array_splice($instructions, $i, 1); 672 $inCount--; 673 $i--; 674 } 675 } 676 return $instructions; 677 } 678 679 private function _insertMergeHint(&$instructions, $mergeHint) { 680 681 // Surround new slice with a mergehint 682 if (empty($mergeHint)) { return; } 683 684 // No emtpy insruction sets. 685 $this->_cleanAllInstructions($instructions); 686 687 if (empty($instructions)) { return; } 688 689 $mergeHintPrepend = $this->_toctoolPrepends( $instructions ); 690 691 // only section content should be surrounded. 692 if ($instructions[0][0] != 'section_open') { return; } 693 694 // save for later use 695 $mergeHints = array(); 696 $mergeHintId = sectionid($mergeHint, $mergeHints); 697 $this->merghintIds[$mergeHintId] = $mergeHint; 698 699 // Insert section information 700 array_push( $mergeHintPrepend, array( 701 'plugin', 702 array( 703 'siteexport_toctools', 704 array( 705 'mergehint', 706 'start', 707 $mergeHint, 708 $mergeHintId 709 ) 710 ) 711 ) ); 712 713 $mergeHintPostpend = array(array( 714 'plugin', 715 array( 716 'siteexport_toctools', 717 array( 718 'mergehint', 719 'end', 720 $mergeHint 721 ) 722 ) 723 )); 724 725/* 726 print "<pre>"; print_r($instructions); print "</pre>"; 727//*/ 728 $instructions = array_merge($mergeHintPrepend, $instructions, $mergeHintPostpend); 729 } 730 731 private function _toctoolPrepends( &$instructions ) { 732 733 $mergeHintPrependPrepend = array(); 734 735 // 2021-01-14 This did no good - if a merged page had two mergehints, the first was stripped. 736/* 737 if ( $instructions[0][0] == 'plugin' && $instructions[0][1][0] == 'siteexport_toctools' && $instructions[0][1][1][1] == 'start' ) { 738 739 // This is already section merge hint ... but it will have a section at its end ... hopefully 740 do { 741 $_instructions = array_shift( $instructions ); 742 array_push( $mergeHintPrependPrepend, $_instructions); 743 } while( !($_instructions[0] == 'plugin' && $_instructions[1][0] == 'siteexport_toctools' && $_instructions[1][1][1] == 'end' ) ) ; 744 array_splice($mergeHintPrepend, 0, 0, $mergeHintPrependPrepend); 745 } 746//*/ 747/* 748 print "<pre>"; print_r($instructions); print "</pre>"; 749//*/ 750 return $mergeHintPrependPrepend; 751 } 752 753 /** 754 * Remove TOC, section edit buttons and tags 755 */ 756 private function _cleanXHTML($xhtml) { 757 $replace = array( 758 '!<div class="toc">.*?(</div>\n</div>)!s' => '', // remove TOCs 759 '#<!-- SECTION \[(\d*-\d*)\] -->#s' => '', // remove section edit buttons 760 '!<div id="tags">.*?(</div>)!s' => '' // remove category tags 761 ); 762 $xhtml = preg_replace(array_keys($replace), array_values($replace), $xhtml); 763 return $xhtml; 764 } 765 766 /** 767 * Allow the plugin to prevent DokuWiki creating a second instance of itself 768 * 769 * @return bool true if the plugin can not be instantiated more than once 770 */ 771 public function isSingleton() { 772 return true; 773 } 774} 775// vim:ts=4:sw=4:et:enc=utf-8: 776