1<?php 2/** 3 * Renderer for text output 4 * 5 * @author Michael Hamann <michael@content-space.de> 6 * @author Todd Augsburger <todd@rollerorgans.com> 7 */ 8 9if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../../').'/'); 10 11if ( !defined('DOKU_LF') ) { 12 define ('DOKU_LF',"\n"); 13} 14 15require_once DOKU_INC . 'inc/parser/renderer.php'; 16require_once DOKU_INC . 'inc/parser/xhtml.php'; 17require_once DOKU_INC . 'inc/html.php'; 18 19class renderer_plugin_text extends Doku_Renderer_xhtml { 20 21 // @access public 22 var $nSpan = 0; 23 var $separator = ''; 24 25 function getFormat(){ 26 return 'text'; 27 } 28 29 /* Compatibility functions for the xhtml mode */ 30 public function startSectionEdit($start, $type, $title = NULL) { 31 } 32 public function finishSectionEdit($end = null, $hid = null) { 33 } 34 35 //handle plugin rendering 36 function plugin($name, $data, $state = '', $match = '') { 37 /** @var DokuWiki_Syntax_Plugin $plugin */ 38 $plugin = plugin_load('syntax', $name); 39 if ($plugin != NULL) { 40 if (!$plugin->render($this->getFormat(),$this,$data)) { 41 42 // probably doesn't support text, so use stripped-down xhtml 43 $tmpData = $this->doc; 44 $this->doc = ''; 45 if ($plugin->render('xhtml',$this,$data) && ($this->doc != '')) { 46 $search = array('@<script[^>]*?>.*?</script>@si', // javascript 47 '@<style[^>]*?>.*?</style>@si', // style tags 48 '@<[\/\!]*?[^<>]*?>@si', // HTML tags 49 '@<![\s\S]*?--[ \t\n\r]*>@', // multi-line comments 50 '@\s+@', // extra whitespace 51 ); 52 $this->doc = $tmpData . DOKU_LF . 53 trim(html_entity_decode(preg_replace($search,' ',$this->doc),ENT_QUOTES)) . 54 DOKU_LF; 55 } else $this->doc = $tmpData; 56 } 57 } 58 } 59 60 function document_start() { 61 global $ID; 62 63 $this->doc = ''; 64 $this->toc = array(); 65 $this->footnotes = array(); 66 $this->store = ''; 67 $this->nSpan = 0; 68 $this->separator = ''; 69 70 $metaheader = array(); 71 $metaheader['Content-Type'] = 'text/plain; charset=utf-8'; 72 //$metaheader['Content-Disposition'] = 'attachment; filename="noname.txt"'; 73 $meta = array(); 74 $meta['format']['text'] = $metaheader; 75 p_set_metadata($ID,$meta); 76 } 77 78 function document_end() { 79 if ( count($this->footnotes) > 0 ) { 80 $this->doc .= DOKU_LF; 81 82 $id = 0; 83 foreach ( $this->footnotes as $footnote ) { 84 $id++; // the number of the current footnote 85 86 // check its not a placeholder that indicates actual footnote text is elsewhere 87 if (substr($footnote, 0, 5) != "@@FNT") { 88 $this->doc .= $id.') '; 89 // get any other footnotes that use the same markup 90 $alt = array_keys($this->footnotes, "@@FNT$id"); 91 if (count($alt)) { 92 foreach ($alt as $ref) { 93 $this->doc .= ($ref+1).') '; 94 } 95 } 96 $this->doc .= $footnote . DOKU_LF; 97 } 98 } 99 } 100 101 // Prepare the TOC 102 global $conf; 103 if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 104 global $TOC; 105 $TOC = $this->toc; 106 } 107 108 // make sure there are no empty paragraphs 109 $this->doc = preg_replace('#'.DOKU_LF.'\s*'.DOKU_LF.'\s*'.DOKU_LF.'#',DOKU_LF.DOKU_LF,$this->doc); 110 } 111 112 function header($text, $level, $pos, $returnonly = false) { 113 $this->doc .= DOKU_LF . $text . DOKU_LF; 114 } 115 116 function section_open($level) { 117 } 118 119 function section_close() { 120 $this->doc .= DOKU_LF; 121 } 122 123 function cdata($text) { 124 $this->doc .= $text; 125 } 126 127 function p_open() { 128 } 129 130 function p_close() { 131 $this->doc .= DOKU_LF; 132 } 133 134 function linebreak() { 135 $this->doc .= DOKU_LF; 136 } 137 138 function hr() { 139 $this->doc .= '--------'.DOKU_LF; 140 } 141 142 /** 143 * Start strong (bold) formatting 144 */ 145 function strong_open() { 146 } 147 148 /** 149 * Stop strong (bold) formatting 150 */ 151 function strong_close() { 152 } 153 154 /** 155 * Start emphasis (italics) formatting 156 */ 157 function emphasis_open() { 158 } 159 160 /** 161 * Stop emphasis (italics) formatting 162 */ 163 function emphasis_close() { 164 } 165 166 /** 167 * Start underline formatting 168 */ 169 function underline_open() { 170 } 171 172 /** 173 * Stop underline formatting 174 */ 175 function underline_close() { 176 } 177 178 /** 179 * Start monospace formatting 180 */ 181 function monospace_open() { 182 } 183 184 /** 185 * Stop monospace formatting 186 */ 187 function monospace_close() { 188 } 189 190 /** 191 * Start a subscript 192 */ 193 function subscript_open() { 194 } 195 196 /** 197 * Stop a subscript 198 */ 199 function subscript_close() { 200 } 201 202 /** 203 * Start a superscript 204 */ 205 function superscript_open() { 206 } 207 208 /** 209 * Stop a superscript 210 */ 211 function superscript_close() { 212 } 213 214 /** 215 * Start deleted (strike-through) formatting 216 */ 217 function deleted_open() { 218 } 219 220 /** 221 * Stop deleted (strike-through) formatting 222 */ 223 function deleted_close() { 224 } 225 226 /** 227 * Callback for footnote start syntax 228 * 229 * All following content will go to the footnote instead of 230 * the document. To achieve this the previous rendered content 231 * is moved to $store and $doc is cleared 232 * 233 * @author Andreas Gohr <andi@splitbrain.org> 234 */ 235 function footnote_open() { 236 237 // move current content to store and record footnote 238 $this->store = $this->doc; 239 $this->doc = ''; 240 } 241 242 /** 243 * Callback for footnote end syntax 244 * 245 * All rendered content is moved to the $footnotes array and the old 246 * content is restored from $store again 247 * 248 * @author Andreas Gohr 249 */ 250 function footnote_close() { 251 252 // recover footnote into the stack and restore old content 253 $footnote = $this->doc; 254 $this->doc = $this->store; 255 $this->store = ''; 256 257 // check to see if this footnote has been seen before 258 $i = array_search($footnote, $this->footnotes); 259 260 if ($i === false) { 261 // its a new footnote, add it to the $footnotes array 262 $id = count($this->footnotes)+1; 263 $this->footnotes[count($this->footnotes)] = $footnote; 264 } else { 265 // seen this one before, translate the index to an id and save a placeholder 266 $i++; 267 $id = count($this->footnotes)+1; 268 $this->footnotes[count($this->footnotes)] = "@@FNT".($i); 269 } 270 271 // output the footnote reference and link 272 $this->doc .= ' '.$id.')'; 273 } 274 275 /** 276 * Open an unordered list 277 */ 278 function listu_open($classes = NULL) { 279 } 280 281 function listu_close() { 282 $this->doc .= DOKU_LF; 283 } 284 285 /** 286 * Open an ordered list 287 */ 288 function listo_open($classes = NULL) { 289 } 290 291 /** 292 * Close an ordered list 293 */ 294 function listo_close() { 295 $this->doc .= DOKU_LF; 296 } 297 298 /** 299 * Open a list item 300 * 301 * @param int $level the nesting level 302 * @param bool $node true when a node; false when a leaf 303 */ 304 function listitem_open($level, $node=false) { 305 } 306 307 /** 308 * Close a list item 309 */ 310 function listitem_close() { 311 } 312 313 /** 314 * Start the content of a list item 315 */ 316 function listcontent_open() { 317 } 318 319 function listcontent_close() { 320 $this->doc .= DOKU_LF; 321 } 322 323 function unformatted($text) { 324 $this->doc .= $text; 325 } 326 327 function php($text, $wrapper='code') { 328 global $conf; 329 330 if ($conf['phpok']) { 331 ob_start(); 332 eval($text); 333 $this->html(ob_get_contents()); 334 ob_end_clean(); 335 } else { 336 $this->cdata($text); 337 } 338 } 339 340 function phpblock($text) { 341 $this->doc .= $text; 342 } 343 344 function html($text, $wrapper='code') { 345 $this->doc .= strip_tags($text); 346 } 347 348 function htmlblock($text) { 349 $this->html($text); 350 } 351 352 /** 353 * Start a block quote 354 */ 355 function quote_open() { 356 } 357 358 /** 359 * Stop a block quote 360 */ 361 function quote_close() { 362 $this->doc .= DOKU_LF; 363 } 364 365 function preformatted($text) { 366 $this->doc .= $text.DOKU_LF; 367 } 368 369 function file($text, $language = null, $filename = null, $options = null) { 370 $this->doc .= $text.DOKU_LF; 371 } 372 373 function code($text, $language = null, $filename = null, $options = null) { 374 $this->preformatted($text); 375 } 376 377 function acronym($acronym) { 378 if ( array_key_exists($acronym, $this->acronyms) ) { 379 $title = $this->acronyms[$acronym]; 380 $this->doc .= $acronym.' ('.$title.')'; 381 } else { 382 $this->doc .= $acronym; 383 } 384 } 385 386 function smiley($smiley) { 387 $this->doc .= $smiley; 388 } 389 390 function entity($entity) { 391 if ( array_key_exists($entity, $this->entities) ) { 392 $this->doc .= $this->entities[$entity]; 393 } else { 394 $this->doc .= $entity; 395 } 396 } 397 398 function multiplyentity($x, $y) { 399 $this->doc .= $x.'x'.$y; 400 } 401 402 function singlequoteopening() { 403 global $lang; 404 $this->doc .= $lang['singlequoteopening']; 405 } 406 407 function singlequoteclosing() { 408 global $lang; 409 $this->doc .= $lang['singlequoteclosing']; 410 } 411 412 function apostrophe() { 413 global $lang; 414 $this->doc .= $lang['apostrophe']; 415 } 416 417 function doublequoteopening() { 418 global $lang; 419 $this->doc .= $lang['doublequoteopening']; 420 } 421 422 function doublequoteclosing() { 423 global $lang; 424 $this->doc .= $lang['doublequoteclosing']; 425 } 426 427 function camelcaselink($link, $returnonly=false) { 428 $this->internallink($link,$link); 429 } 430 431 function locallink($hash, $name = NULL, $returnonly=false){ 432 $name = $this->_getLinkTitle($name, $hash, $isImage); 433 $this->doc .= $name;; 434 } 435 436 function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 437 global $ID; 438 // default name is based on $id as given 439 $default = $this->_simpleTitle($id); 440 resolve_pageid(getNS($ID),$id,$exists); 441 $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 442 if ($returnonly) { 443 return $name; 444 } else { 445 $this->doc .= $name; 446 } 447 } 448 449 function externallink($url, $name = NULL, $returnonly=false) { 450 $this->doc .= $this->_getLinkTitle($name, $url, $isImage); 451 } 452 453 function interwikilink($match, $name = NULL, $wikiName, $wikiUri, $returnonly=false) { 454 $this->doc .= $this->_getLinkTitle($name, $wikiUri, $isImage); 455 } 456 457 function windowssharelink($url, $name = NULL, $returnonly=false) { 458 $this->doc .= $this->_getLinkTitle($name, $url, $isImage); 459 } 460 461 function emaillink($address, $name = NULL, $returnonly=false) { 462 $name = $this->_getLinkTitle($name, '', $isImage); 463 $address = html_entity_decode(obfuscate($address),ENT_QUOTES,'UTF-8'); 464 if (empty($name)) { 465 $name = $address; 466 } 467 $this->doc .= $name; 468 } 469 470 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 471 $height=NULL, $cache=NULL, $linking=NULL, $return=false) { 472 $this->doc .= $title; 473 } 474 475 function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 476 $height=NULL, $cache=NULL, $linking=NULL, $return=false) { 477 $this->doc .= $title; 478 } 479 480 function rss($url, $params) { 481 } 482 483 /** 484 * Start a table 485 * 486 * @param int $maxcols maximum number of columns 487 * @param int $numrows NOT IMPLEMENTED 488 * @param int $pos byte position in the original source 489 */ 490 function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 491 } 492 493 function table_close($pos = NULL) { 494 $this->doc .= DOKU_LF; 495 } 496 497 /** 498 * Open a table header 499 */ 500 function tablethead_open() { 501 } 502 503 /** 504 * Close a table header 505 */ 506 function tablethead_close() { 507 } 508 509 /** 510 * Open a table body 511 */ 512 function tabletbody_open() { 513 } 514 515 /** 516 * Close a table body 517 */ 518 function tabletbody_close() { 519 } 520 521 function tablerow_open($classes=NULL) { 522 $this->separator = ''; 523 } 524 525 function tablerow_close() { 526 $this->doc .= DOKU_LF; 527 } 528 529 function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1, $classes = null) { 530 $this->tablecell_open(); 531 } 532 533 function tableheader_close() { 534 $this->tablecell_close(); 535 } 536 537 function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1, $classes = null) { 538 $this->nSpan = $colspan; 539 $this->doc .= $this->separator; 540 $this->separator = ', '; 541 } 542 543 function tablecell_close() { 544 if ($this->nSpan > 0) { 545 $this->doc .= str_repeat(',', $this->nSpan - 1); 546 } 547 $this->nSpan = 0; 548 } 549 550 function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 551 $isImage = false; 552 if(is_array($title)) { 553 $isImage = true; 554 if (!is_null($default) && ($default != $title['title'])) 555 return $default." ".$title['title']; 556 else 557 return $title['title']; 558 } elseif(is_null($title) || trim($title) == '') { 559 if(useHeading($linktype) && $id) { 560 $heading = p_get_first_heading($id); 561 if($heading) { 562 return $this->_xmlEntities($heading); 563 } 564 } 565 return $this->_xmlEntities($default); 566 } else { 567 return $this->_xmlEntities($title); 568 } 569 } 570 571 function _xmlEntities($string) { 572 return $string; // nothing to do for text 573 } 574 575 function _formatLink($link) { 576 if (!empty($link['name'])) 577 return $link['name']; 578 elseif (!empty($link['title'])) 579 return $link['title']; 580 return $link['url']; 581 } 582} 583