1<?php 2/** 3 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 4 * @author Esther Brunner <wikidesign@gmail.com> 5 * @author Christopher Smith <chris@jalakai.co.uk> 6 * @author Gina Häußge, Michael Klier <dokuwiki@chimeric.de> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) die(); 11 12if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 15 16class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin 17 18 var $pages = array(); // filechain of included pages 19 var $page = array(); // associative array with data about the page to include 20 var $ins = array(); // instructions array 21 var $doc = ''; // the final output XHTML string 22 var $mode = 'section'; // inclusion mode: 'page' or 'section' 23 var $clevel = 1; // current section level 24 var $firstsec = 0; // show first section only 25 var $editbtn = 1; // show edit button 26 var $footer = 1; // show metaline below page 27 var $noheader = 0; // omit header 28 var $permalink = 0; // make first headline permalink to included page 29 var $redirect = 1; // redirect back to original page after an edit 30 var $header = array(); // included page / section header 31 var $renderer = NULL; // DokuWiki renderer object 32 var $toplevel_id = NULL; 33 var $toc = array(); 34 35 var $INCLUDE_LIMIT = 12; 36 37 // private variables 38 var $_offset = NULL; 39 40 /** 41 * Constructor loads some config settings 42 */ 43 function helper_plugin_include() { 44 $this->firstsec = $this->getConf('firstseconly'); 45 $this->editbtn = $this->getConf('showeditbtn'); 46 $this->footer = $this->getConf('showfooter'); 47 $this->redirect = $this->getConf('doredirect'); 48 $this->noheader = 0; 49 $this->permalink = 0; 50 $this->header = array(); 51 } 52 53 function getInfo() { 54 return array( 55 'author' => 'Gina Häußge, Michael Klier, Esther Brunner', 56 'email' => 'dokuwiki@chimeric.de', 57 'date' => @file_get_contents(DOKU_PLUGIN . 'blog/VERSION'), 58 'name' => 'Include Plugin (helper class)', 59 'desc' => 'Functions to include another page in a wiki page', 60 'url' => 'http://wiki.splitbrain.org/plugin:include', 61 ); 62 } 63 64 function getMethods() { 65 $result = array(); 66 $result[] = array( 67 'name' => 'setPage', 68 'desc' => 'sets the page to include', 69 'params' => array("page attributes, 'id' required, 'section' for filtering" => 'array'), 70 'return' => array('success' => 'boolean'), 71 ); 72 $result[] = array( 73 'name' => 'setMode', 74 'desc' => 'sets inclusion mode: should indention be merged?', 75 'params' => array("'page' (original) or 'section' (merged indention)" => 'string'), 76 ); 77 $result[] = array( 78 'name' => 'setLevel', 79 'desc' => 'sets the indention for the current section level', 80 'params' => array('level: 0 to 5' => 'integer'), 81 'return' => array('success' => 'boolean'), 82 ); 83 $result[] = array( 84 'name' => 'setFlags', 85 'desc' => 'overrides standard values for showfooter and firstseconly settings', 86 'params' => array('flags' => 'array'), 87 ); 88 $result[] = array( 89 'name' => 'renderXHTML', 90 'desc' => 'renders the XHTML output of the included page', 91 'params' => array('DokuWiki renderer' => 'object'), 92 'return' => array('XHTML' => 'string'), 93 ); 94 return $result; 95 } 96 97 /** 98 * Sets the page to include if it is not already included (prevent recursion) 99 * and the current user is allowed to read it 100 */ 101 function setPage($page) { 102 global $ID; 103 104 $id = $page['id']; 105 $fullid = $id.'#'.$page['section']; 106 107 if (!$id) return false; // no page id given 108 if ($id == $ID) return false; // page can't include itself 109 110 // prevent include recursion 111 if ($this->_in_filechain($id,$page['section']) || (count($this->pages) >= $this->INCLUDE_LIMIT)) return false; 112 113 // we need to make sure 'perm', 'file' and 'exists' are set 114 if (!isset($page['perm'])) $page['perm'] = auth_quickaclcheck($page['id']); 115 if (!isset($page['file'])) $page['file'] = wikiFN($page['id']); 116 if (!isset($page['exists'])) $page['exists'] = @file_exists($page['file']); 117 118 // check permission 119 if ($page['perm'] < AUTH_READ) return false; 120 121 // add the page to the filechain 122 $this->page = $page; 123 return true; 124 } 125 126 function _push_page($id,$section='') { 127 global $ID; 128 if (empty($this->pages)) array_push($this->pages, $ID.'#'); 129 array_push($this->pages, $id.'#'.$section); 130 } 131 132 function _pop_page() { 133 $page = array_pop($this->pages); 134 if (count($this->pages=1)) $this->pages = array(); 135 136 return $page; 137 } 138 139 function _in_filechain($id,$section) { 140 $pattern = ($section) ? "/^($id#$section|$id#)$/" : "/^$id#/"; 141 $match = preg_grep($pattern, $this->pages); 142 143 return (!empty($match)); 144 } 145 146 /** 147 * Sets the inclusion mode: 'page' or 'section' 148 */ 149 function setMode($mode) { 150 $this->mode = $mode; 151 } 152 153 /** 154 * Sets the right indention for a given section level 155 */ 156 function setLevel($level) { 157 if ((is_numeric($level)) && ($level >= 0) && ($level <= 5)) { 158 $this->clevel = $level; 159 return true; 160 } 161 return false; 162 } 163 164 /** 165 * Overrides standard values for showfooter and firstseconly settings 166 */ 167 function setFlags($flags) { 168 foreach ($flags as $flag) { 169 switch ($flag) { 170 case 'footer': 171 $this->footer = 1; 172 break; 173 case 'nofooter': 174 $this->footer = 0; 175 break; 176 case 'firstseconly': 177 case 'firstsectiononly': 178 $this->firstsec = 1; 179 break; 180 case 'fullpage': 181 $this->firstsec = 0; 182 break; 183 case 'noheader': 184 $this->noheader = 1; 185 break; 186 case 'editbtn': 187 case 'editbutton': 188 $this->editbtn = 1; 189 break; 190 case 'noeditbtn': 191 case 'noeditbutton': 192 $this->editbtn = 0; 193 break; 194 case 'permalink': 195 $this->permalink = 1; 196 break; 197 case 'nopermalink': 198 $this->permalink = 0; 199 break; 200 case 'redirect': 201 $this->redirect = 1; 202 break; 203 case 'noredirect': 204 $this->redirect = 0; 205 break; 206 } 207 } 208 } 209 210 /** 211 * Builds the XHTML to embed the page to include 212 */ 213 function renderXHTML(&$renderer, &$info) { 214 global $ID; 215 216 if (!$this->page['id']) return ''; // page must be set first 217 if (!$this->page['exists'] && ($this->page['perm'] < AUTH_CREATE)) return ''; 218 219 $this->_push_page($this->page['id'],$this->page['section']); 220 221 // prepare variables 222 $rdoc = $renderer->doc; 223 $doc = ''; 224 $this->renderer =& $renderer; 225 226 $page = $this->page; 227 $clevel = $this->clevel; 228 $mode = $this->mode; 229 230 // exchange page ID for included one 231 $backupID = $ID; // store the current ID 232 $ID = $this->page['id']; // change ID to the included page 233 234 // get instructions and render them on the fly 235 $this->ins = p_cached_instructions($this->page['file']); 236 237 // show only a given section? 238 if ($this->page['section'] && $this->page['exists']) $this->_getSection(); 239 240 // convert relative links 241 $this->_convertInstructions(); 242 243 $xhtml = p_render('xhtml', $this->ins, $info); 244 $ID = $backupID; // restore ID 245 246 $this->mode = $mode; 247 $this->clevel = $clevel; 248 $this->page = $page; 249 250 $xhtml = $this->_cleanXHTML($xhtml); 251 $xhtml = $this->_convertFootnotes($xhtml, $this->page['id']); 252 253 // render the included page 254 $content = '<div class="entry-content">' . DOKU_LF 255 . $xhtml . DOKU_LF 256 . '</div><!-- .entry-content -->' . DOKU_LF; 257 258 // restore ID 259 $ID = $backupID; 260 261 // embed the included page 262 $class = ($this->page['draft'] ? 'include draft' : 'include'); 263 264 $doc .= DOKU_LF.'<!-- including '.str_replace('--', '-', $this->page['id']).' // '.$this->page['file'].' -->'.DOKU_LF; 265 $doc .= '<div class="'.$class.' hentry"'.$this->_showTagLogos().'>'.DOKU_LF; 266 if (!$this->header && $this->clevel && ($this->mode == 'section')) 267 $doc .= '<div class="level'.$this->clevel.'">'.DOKU_LF; 268 269 if ((@file_exists(DOKU_PLUGIN.'editsections/action.php')) 270 && (!plugin_isdisabled('editsections'))) { // for Edit Section Reorganizer Plugin 271 $doc .= $this->_editButton().$content; 272 } else { 273 $doc .= $content.$this->_editButton(); 274 } 275 276 // output meta line (if wanted) and remove page from filechain 277 $doc .= $this->_footer($this->page); 278 279 if (!$this->header && $this->clevel && ($this->mode == 'section')) 280 $doc .= '</div>'.DOKU_LF; // class="level?" 281 $doc .= '</div>'.DOKU_LF; // class="include hentry" 282 $doc .= DOKU_LF.'<!-- /including '.$this->page['id'].' -->'.DOKU_LF; 283 284 // reset defaults 285 $this->helper_plugin_include(); 286 $this->_pop_page(); 287 288 // return XHTML 289 $renderer->doc = $rdoc.$doc; 290 return $doc; 291 } 292 293 /* ---------- Private Methods ---------- */ 294 295 /** 296 * Get a section including its subsections 297 */ 298 function _getSection() { 299 foreach ($this->ins as $ins) { 300 if ($ins[0] == 'header') { 301 302 // found the right header 303 if (cleanID($ins[1][0]) == $this->page['section']) { 304 $level = $ins[1][1]; 305 $i[] = $ins; 306 307 // next header of the same or higher level -> exit 308 } elseif ($ins[1][1] <= $level) { 309 $this->ins = $i; 310 return true; 311 } elseif (isset($level)) { 312 $i[] = $ins; 313 } 314 315 // add instructions from our section 316 } elseif (isset($level)) { 317 $i[] = $ins; 318 } 319 } 320 $this->ins = $i; 321 return true; 322 } 323 324 /** 325 * Corrects relative internal links and media and 326 * converts headers of included pages to subheaders of the current page 327 */ 328 function _convertInstructions() { 329 global $ID; 330 331 if (!$this->page['exists']) return false; 332 333 // check if included page is in same namespace 334 $ns = getNS($this->page['id']); 335 $convert = (getNS($ID) == $ns ? false : true); 336 337 $n = count($this->ins); 338 for ($i = 0; $i < $n; $i++) { 339 $current = $this->ins[$i][0]; 340 341 // convert internal links and media from relative to absolute 342 if ($convert && (substr($current, 0, 8) == 'internal')) { 343 $this->ins[$i][1][0] = $this->_convertInternalLink($this->ins[$i][1][0], $ns); 344 345 // set header level to current section level + header level 346 } elseif ($current == 'header') { 347 $this->_convertHeader($i); 348 349 // the same for sections 350 } elseif (($current == 'section_open') && ($this->mode == 'section')) { 351 $this->ins[$i][1][0] = $this->_convertSectionLevel($this->ins[$i][1][0]); 352 353 // show only the first section? 354 } elseif ($this->firstsec && ($current == 'section_close') 355 && ($this->ins[$i-1][0] != 'section_open')) { 356 $this->_readMore($i); 357 return true; 358 } 359 } 360 $this->_finishConvert(); 361 return true; 362 } 363 364 /** 365 * Convert relative internal links and media 366 * 367 * @param integer $i: counter for current instruction 368 * @param string $ns: namespace of included page 369 * @return string $link: converted, now absolute link 370 */ 371 function _convertInternalLink($link, $ns) { 372 373 // relative subnamespace 374 if ($link{0} == '.') { 375 if ($link{1} == '.') return getNS($ns).':'.substr($link, 2); // parent namespace 376 else return $ns.':'.substr($link, 1); // current namespace 377 378 // relative link 379 } elseif (strpos($link, ':') === false) { 380 return $ns.':'.$link; 381 382 // absolute link - don't change 383 } else { 384 return $link; 385 } 386 } 387 388 /** 389 * Convert header level and add header to TOC 390 * 391 * @param integer $i: counter for current instruction 392 * @return boolean true 393 */ 394 function _convertHeader($i) { 395 global $conf; 396 397 $text = $this->ins[$i][1][0]; 398 $hid = $this->renderer->_headerToLink($text, 'true'); 399 if (empty($this->header)) { 400 $this->_offset = $this->clevel - $this->ins[$i][1][1] + 1; 401 $level = $this->_convertSectionLevel(1); 402 $this->header = array('hid' => $hid, 'title' => hsc($text), 'level' => $level); 403 if ($this->noheader) { 404 unset($this->ins[$i]); 405 return true; 406 } else if ($this->permalink){ 407 $this->ins[$i] = $this->_permalinkHeader($text, $level, $this->ins[$i][1][2]); 408 } 409 } else { 410 $level = $this->_convertSectionLevel($this->ins[$i][1][1]); 411 } 412 if ($this->mode == 'section') { 413 if (is_array($this->ins[$i][1][1])) { // permalink header 414 $this->ins[$i][1][1][1] = $level; 415 } else { // normal header 416 $this->ins[$i][1][1] = $level; 417 } 418 } 419 420 421 // add TOC item 422 if (($level >= $conf['toptoclevel']) && ($level <= $conf['maxtoclevel'])) { 423 $item = array( 424 'hid' => $hid, 425 'title' => $text, 426 'type' => 'ul', 427 'level' => $level - $conf['toptoclevel'] + 1 428 ); 429 $this->toc[] = $item; 430 } 431 return true; 432 } 433 434 /** 435 * Create instruction item for a permalink header 436 * 437 * @param string $text: Headline text 438 * @param integer $level: Headline level 439 * @param integer $pos: I wish I knew what this is for... 440 * 441 * @author Gina Haeussge <osd@foosel.net> 442 */ 443 function _permalinkHeader($text, $level, $pos) { 444 $newIns = array( 445 'plugin', 446 array( 447 'include_header', 448 array( 449 $text, 450 $level 451 ), 452 ), 453 $pos 454 ); 455 456 return $newIns; 457 } 458 459 /** 460 * Convert the level of headers and sections 461 * 462 * @param integer $in: current level 463 * @return integer $out: converted level 464 */ 465 function _convertSectionLevel($in) { 466 $out = $in + $this->_offset; 467 if ($out >= 5) return 5; 468 if ($out <= $this->clevel + 1) return $this->clevel + 1; 469 return $out; 470 } 471 472 /** 473 * Adds a read more... link at the bottom of the first section 474 * 475 * @param integer $i: counter for current instruction 476 * @return boolean true 477 */ 478 function _readMore($i) { 479 $more = ((is_array($this->ins[$i+1])) && ($this->ins[$i+1][0] != 'document_end')); 480 481 if ($this->ins[0][0] == 'document_start') $this->ins = array_slice($this->ins, 1, $i); 482 else $this->ins = array_slice($this->ins, 0, $i); 483 484 if ($more) { 485 array_unshift($this->ins, array('document_start', array(), 0)); 486 $last = array_pop($this->ins); 487 $this->ins[] = array('p_open', array(), $last[2]); 488 $this->ins[] = array('internallink',array($this->page['id'], $this->getLang('readmore')),$last[2]); 489 $this->ins[] = array('p_close', array(), $last[2]); 490 $this->ins[] = $last; 491 $this->ins[] = array('document_end', array(), $last[2]); 492 } else { 493 $this->_finishConvert(); 494 } 495 return true; 496 } 497 498 /** 499 * Adds 'document_start' and 'document_end' instructions if not already there 500 */ 501 function _finishConvert() { 502 if ($this->ins[0][0] != 'document_start') 503 @array_unshift($this->ins, array('document_start', array(), 0)); 504 $c = count($this->ins) - 1; 505 if ($this->ins[$c][0] != 'document_end') 506 $this->ins[] = array('document_end', array(), 0); 507 } 508 509 /** 510 * Remove TOC, section edit buttons and tags 511 */ 512 function _cleanXHTML($xhtml) { 513 $replace = array( 514 '!<div class="toc">.*?(</div>\n</div>)!s' => '', // remove toc 515 '#<!-- SECTION "(.*?)" \[(\d+-\d*)\] -->#e' => '', // remove section edit buttons 516 '!<div class="tags">.*?(</div>)!s' => '', // remove category tags 517 ); 518 if ($this->clevel) 519 $replace['#<div class="footnotes">#s'] = '<div class="footnotes level'.$this->clevel.'">'; 520 $xhtml = preg_replace(array_keys($replace), array_values($replace), $xhtml); 521 return $xhtml; 522 } 523 524 /** 525 * Convert footnotes to include page id to make them unique if more than 526 * one page or section are included in one wiki node. (FS#93) 527 * 528 * Gotta admit, this fix is kind of ugly, but since we have no chance to 529 * fix the generated footnote ids on instruction level, this has to be 530 * done on the generated XHTML. 531 * 532 * @param $xhtml XHTML code of the page 533 * @param $id included page's id 534 * @return XHTML code with converted footnote anchors and ids 535 * 536 * @author Gina Haeussge <osd@foosel.net> 537 */ 538 function _convertFootnotes($xhtml, $id) { 539 $id = str_replace(':', '_', $id); 540 $replace = array( 541 '!<a href="#fn__(\d+)" name="fnt__(\d+)" id="fnt__(\d+)" class="fn_top">!' => 542 '<a href="#fn__'.$id.'__\1" name="fnt__'.$id.'__\2" id="fnt__'.$id.'__\3" class="fn_top">', 543 '!<a href="#fnt__(\d+)" id="fn__(\d+)" name="fn__(\d+)" class="fn_bot">!' => 544 '<a href="#fnt__'.$id.'__\1" name="fn__'.$id.'__\2" id="fn__'.$id.'__\3" class="fn_bot">', 545 ); 546 $xhtml = preg_replace(array_keys($replace), array_values($replace), $xhtml); 547 return $xhtml; 548 } 549 550 /** 551 * Optionally display logo for the first tag found in the included page 552 */ 553 function _showTagLogos() { 554 if ((!$this->getConf('showtaglogos')) 555 || (plugin_isdisabled('tag')) 556 || (!$taghelper =& plugin_load('helper', 'tag'))) 557 return ''; 558 559 $subject = p_get_metadata($this->page['id'], 'subject'); 560 if (is_array($subject)) $tag = $subject[0]; 561 else list($tag, $rest) = explode(' ', $subject, 2); 562 $title = str_replace('_', ' ', noNS($tag)); 563 resolve_pageid($taghelper->namespace, $tag, $exists); // resolve shortcuts 564 565 $logosrc = mediaFN($logoID); 566 $types = array('.png', '.jpg', '.gif'); // auto-detect filetype 567 foreach ($types as $type) { 568 if (!@file_exists($logosrc.$type)) continue; 569 $logoID = $tag.$type; 570 $logosrc .= $type; 571 list($w, $h, $t, $a) = getimagesize($logosrc); 572 return ' style="min-height: '.$h.'px">'. 573 '<img class="mediaright" src="'.ml($logoID).'" alt="'.$title.'"/'; 574 } 575 return ''; 576 } 577 578 /** 579 * Display an edit button for the included page 580 */ 581 function _editButton() { 582 global $ID; 583 if ($this->page['exists']) { 584 if (($this->page['perm'] >= AUTH_EDIT) && (is_writable($this->page['file']))) 585 $action = 'edit'; 586 else return ''; 587 } elseif ($this->page['perm'] >= AUTH_CREATE) { 588 $action = 'create'; 589 } 590 if ($this->editbtn) { 591 $params = array('do' => 'edit'); 592 if ($this->redirect) 593 $params['redirect_id'] = $ID; 594 return '<div class="secedit">'.DOKU_LF.DOKU_TAB. 595 html_btn($action, $this->page['id'], '', $params, 'post').DOKU_LF. 596 '</div>'.DOKU_LF; 597 } else { 598 return ''; 599 } 600 } 601 602 /** 603 * Returns the meta line below the included page 604 */ 605 function _footer($page) { 606 global $conf, $ID; 607 608 if (!$this->footer) return ''; // '<div class="inclmeta"> </div>'.DOKU_LF; 609 610 $id = $page['id']; 611 $meta = p_get_metadata($id); 612 $ret = array(); 613 614 // permalink 615 if ($this->getConf('showlink')) { 616 $title = ($page['title'] ? $page['title'] : $meta['title']); 617 if (!$title) $title = str_replace('_', ' ', noNS($id)); 618 $class = ($page['exists'] ? 'wikilink1' : 'wikilink2'); 619 $link = array( 620 'url' => wl($id), 621 'title' => $id, 622 'name' => hsc($title), 623 'target' => $conf['target']['wiki'], 624 'class' => $class.' permalink', 625 'more' => 'rel="bookmark"', 626 ); 627 $ret[] = $this->renderer->_formatLink($link); 628 } 629 630 // date 631 if ($this->getConf('showdate')) { 632 $date = ($page['date'] ? $page['date'] : $meta['date']['created']); 633 if ($date) 634 $ret[] = '<abbr class="published" title="'.strftime('%Y-%m-%dT%H:%M:%SZ', $date).'">'. 635 strftime($conf['dformat'], $date). 636 '</abbr>'; 637 } 638 639 // author 640 if ($this->getConf('showuser')) { 641 $author = ($page['user'] ? $page['user'] : $meta['creator']); 642 if ($author) { 643 $userpage = cleanID($this->getConf('usernamespace').':'.$author); 644 resolve_pageid(getNS($ID), $userpage, $exists); 645 $class = ($exists ? 'wikilink1' : 'wikilink2'); 646 $link = array( 647 'url' => wl($userpage), 648 'title' => $userpage, 649 'name' => hsc($author), 650 'target' => $conf['target']['wiki'], 651 'class' => $class.' url fn', 652 'pre' => '<span class="vcard author">', 653 'suf' => '</span>', 654 ); 655 $ret[] = $this->renderer->_formatLink($link); 656 } 657 } 658 659 // comments - let Discussion Plugin do the work for us 660 if (!$page['section'] && $this->getConf('showcomments') 661 && (!plugin_isdisabled('discussion')) 662 && ($discussion =& plugin_load('helper', 'discussion'))) { 663 $disc = $discussion->td($id); 664 if ($disc) $ret[] = '<span class="comment">'.$disc.'</span>'; 665 } 666 667 // linkbacks - let Linkback Plugin do the work for us 668 if (!$page['section'] && $this->getConf('showlinkbacks') 669 && (!plugin_isdisabled('linkback')) 670 && ($linkback =& plugin_load('helper', 'linkback'))) { 671 $link = $linkback->td($id); 672 if ($link) $ret[] = '<span class="linkback">'.$link.'</span>'; 673 } 674 675 $ret = implode(DOKU_LF.DOKU_TAB.'· ', $ret); 676 677 // tags - let Tag Plugin do the work for us 678 if (!$page['section'] && $this->getConf('showtags') 679 && (!plugin_isdisabled('tag')) 680 && ($tag =& plugin_load('helper', 'tag'))) { 681 $page['tags'] = '<div class="tags"><span>'.DOKU_LF. 682 DOKU_TAB.$tag->td($id).DOKU_LF. 683 DOKU_TAB.'</span></div>'.DOKU_LF; 684 $ret = $page['tags'].DOKU_TAB.$ret; 685 } 686 687 if (!$ret) $ret = ' '; 688 $class = 'inclmeta'; 689 if ($this->header && $this->clevel && ($this->mode == 'section')) { 690 $class .= ' level'.$this->clevel; 691 } 692 return '<div class="'.$class.'">'.DOKU_LF.DOKU_TAB.$ret.DOKU_LF.'</div>'.DOKU_LF; 693 } 694 695 /** 696 * Builds the ODT to embed the page to include 697 */ 698 function renderODT(&$renderer) { 699 global $ID; 700 701 if (!$this->page['id']) return ''; // page must be set first 702 if (!$this->page['exists'] && ($this->page['perm'] < AUTH_CREATE)) return ''; 703 704 // prepare variable 705 $this->renderer =& $renderer; 706 707 // get instructions and render them on the fly 708 $this->ins = p_cached_instructions($this->page['file']); 709 710 // show only a given section? 711 if ($this->page['section'] && $this->page['exists']) $this->_getSection(); 712 713 // convert relative links 714 $this->_convertInstructions(); 715 716 // render the included page 717 $backupID = $ID; // store the current ID 718 $ID = $this->page['id']; // change ID to the included page 719 // remove document_start and document_end to avoid zipping 720 $this->ins = array_slice($this->ins, 1, -1); 721 p_render('odt', $this->ins, $info); 722 $ID = $backupID; // restore ID 723 // reset defaults 724 $this->helper_plugin_include(); 725 } 726 727 /** 728 * Makes user or date dependent includes possible 729 */ 730 function _applyMacro($id) { 731 global $INFO, $auth; 732 733 // if we don't have an auth object, do nothing 734 if (!$auth) 735 return $id; 736 737 $user = $_SERVER['REMOTE_USER']; 738 $userdata = $auth->getUserData($user); 739 $group = $userdata['grps'][0]; 740 741 $replace = array( 742 '@USER@' => cleanID($user), 743 '@NAME@' => cleanID($INFO['userinfo']['name']), 744 '@GROUP@' => cleanID($group), 745 '@YEAR@' => date('Y'), 746 '@MONTH@' => date('m'), 747 '@DAY@' => date('d'), 748 ); 749 return str_replace(array_keys($replace), array_values($replace), $id); 750 } 751} 752//vim:ts=4:sw=4:et:enc=utf-8: 753