1<?php 2/** 3 * Renderer for XHTML output 4 * 5 * @author Harry Fuecks <hfuecks@gmail.com> 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10 11if ( !defined('DOKU_LF') ) { 12 // Some whitespace to help View > Source 13 define ('DOKU_LF',"\n"); 14} 15 16if ( !defined('DOKU_TAB') ) { 17 // Some whitespace to help View > Source 18 define ('DOKU_TAB',"\t"); 19} 20 21require_once DOKU_INC . 'inc/parser/renderer.php'; 22require_once DOKU_INC . 'inc/pluginutils.php'; 23 24/** 25 * The Renderer 26 */ 27class Doku_Renderer_xhtml extends Doku_Renderer { 28 29 var $doc = ''; 30 31 var $headers = array(); 32 33 var $footnotes = array(); 34 35 var $acronyms = array(); 36 var $smileys = array(); 37 var $badwords = array(); 38 var $entities = array(); 39 var $interwiki = array(); 40 41 var $lastsec = 0; 42 43 var $store = ''; 44 45 function document_start() { 46 } 47 48 function document_end() { 49 // add button for last section if any and more than one 50 if($this->lastsec > 1) $this->_secedit($this->lastsec,''); 51 52 if ( count ($this->footnotes) > 0 ) { 53 $this->doc .= '<div class="footnotes">'.DOKU_LF; 54 55 $id = 0; 56 foreach ( $this->footnotes as $footnote ) { 57 $id++; // the number of the current footnote 58 59 // check its not a placeholder that indicates actual footnote text is elsewhere 60 if (substr($footnote, 0, 5) != "@@FNT") { 61 62 // open the footnote and set the anchor and backlink 63 $this->doc .= '<div class="fn">'; 64 $this->doc .= '<a href="#fnt'.$id.'" id="fn'.$id.'" name="fn'.$id.'" class="fn_bot">'; 65 $this->doc .= $id.')</a> '.DOKU_LF; 66 67 // get any other footnotes that use the same markup 68 $alt = array_keys($this->footnotes, "@@FNT$id"); 69 70 if (count($alt)) { 71 foreach ($alt as $ref) { 72 // set anchor and backlink for the other footnotes 73 $this->doc .= ', <a href="#fnt'.($ref+1).'" id="fn'.($ref+1).'" name="fn'.($ref+1).'" class="fn_bot">'; 74 $this->doc .= ($ref+1).')</a> '.DOKU_LF; 75 } 76 } 77 78 // add footnote markup and close this footnote 79 $this->doc .= $footnote; 80 $this->doc .= '</div>' . DOKU_LF; 81 } 82 } 83 $this->doc .= '</div>'.DOKU_LF; 84 } 85 } 86 87 //handles plugin rendering 88 function plugin($name,$data){ 89 $plugin = null; 90 if(plugin_load('syntax',$name,$plugin)){ 91 $plugin->render('xhtml',$this,$data); 92 } 93 } 94 95 function toc_open() { 96 global $lang; 97 $this->doc .= '<div class="toc">'.DOKU_LF; 98 $this->doc .= '<div class="tocheader">'; 99 $this->doc .= ' <script type="text/javascript">showTocToggle("+","-")</script>'; 100 $this->doc .= $lang['toc']; 101 $this->doc .= '</div>'.DOKU_LF; 102 $this->doc .= '<div id="tocinside">'.DOKU_LF; 103 } 104 105 function tocbranch_open($level) { 106 $this->doc .= '<ul class="toc">'.DOKU_LF; 107 } 108 109 function tocitem_open($level, $empty = FALSE) { 110 if ( !$empty ) { 111 $this->doc .= '<li class="level'.$level.'">'; 112 } else { 113 $this->doc .= '<li class="clear">'; 114 } 115 } 116 117 function tocelement($level, $title) { 118 $this->doc .= '<span class="li"><a href="#'.$this->_headerToLink($title).'" class="toc">'; 119 $this->doc .= $this->_xmlEntities($title); 120 $this->doc .= '</a></span>'; 121 } 122 123 function tocitem_close($level) { 124 $this->doc .= '</li>'.DOKU_LF; 125 } 126 127 function tocbranch_close($level) { 128 $this->doc .= '</ul>'.DOKU_LF; 129 } 130 131 function toc_close() { 132 $this->doc .= '</div>'.DOKU_LF.'</div>'.DOKU_LF; 133 } 134 135 function header($text, $level, $pos) { 136 global $conf; 137 //handle section editing 138 if($level <= $conf['maxseclevel']){ 139 // add button for last section if any 140 if($this->lastsec) $this->_secedit($this->lastsec,$pos-1); 141 // remember current position 142 $this->lastsec = $pos; 143 } 144 145 $this->doc .= DOKU_LF.'<a name="'.$this->_headerToLink($text).'"></a><h'.$level.'>'; 146 $this->doc .= $this->_xmlEntities($text); 147 $this->doc .= "</h$level>".DOKU_LF; 148 } 149 150 function section_open($level) { 151 $this->doc .= "<div class=\"level$level\">".DOKU_LF; 152 } 153 154 function section_close() { 155 $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 156 } 157 158 function cdata($text) { 159 $this->doc .= $this->_xmlEntities($text); 160 } 161 162 function p_open() { 163 $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 164 } 165 166 function p_close() { 167 $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 168 } 169 170 function linebreak() { 171 $this->doc .= '<br/>'.DOKU_LF; 172 } 173 174 function hr() { 175 $this->doc .= '<hr noshade="noshade" size="1" />'.DOKU_LF; 176 } 177 178 function strong_open() { 179 $this->doc .= '<strong>'; 180 } 181 182 function strong_close() { 183 $this->doc .= '</strong>'; 184 } 185 186 function emphasis_open() { 187 $this->doc .= '<em>'; 188 } 189 190 function emphasis_close() { 191 $this->doc .= '</em>'; 192 } 193 194 function underline_open() { 195 $this->doc .= '<u>'; 196 } 197 198 function underline_close() { 199 $this->doc .= '</u>'; 200 } 201 202 function monospace_open() { 203 $this->doc .= '<code>'; 204 } 205 206 function monospace_close() { 207 $this->doc .= '</code>'; 208 } 209 210 function subscript_open() { 211 $this->doc .= '<sub>'; 212 } 213 214 function subscript_close() { 215 $this->doc .= '</sub>'; 216 } 217 218 function superscript_open() { 219 $this->doc .= '<sup>'; 220 } 221 222 function superscript_close() { 223 $this->doc .= '</sup>'; 224 } 225 226 function deleted_open() { 227 $this->doc .= '<del>'; 228 } 229 230 function deleted_close() { 231 $this->doc .= '</del>'; 232 } 233 234 /** 235 * Callback for footnote start syntax 236 * 237 * All following content will go to the footnote instead of 238 * the document. To achieve this the previous rendered content 239 * is moved to $store and $doc is cleared 240 * 241 * @author Andreas Gohr <andi@splitbrain.org> 242 */ 243 function footnote_open() { 244 245 // move current content to store and record footnote 246 $this->store = $this->doc; 247 $this->doc = ''; 248 } 249 250 /** 251 * Callback for footnote end syntax 252 * 253 * All rendered content is moved to the $footnotes array and the old 254 * content is restored from $store again 255 * 256 * @author Andreas Gohr 257 */ 258 function footnote_close() { 259 260 // recover footnote into the stack and restore old content 261 $footnote = $this->doc; 262 $this->doc = $this->store; 263 $this->store = ''; 264 265 // check to see if this footnote has been seen before 266 $i = array_search($footnote, $this->footnotes); 267 268 if ($i === false) { 269 // its a new footnote, add it to the $footnotes array 270 $id = count($this->footnotes)+1; 271 $this->footnotes[count($this->footnotes)] = $footnote; 272 } else { 273 // seen this one before, translate the index to an id and save a placeholder 274 $i++; 275 $id = count($this->footnotes)+1; 276 $this->footnotes[count($this->footnotes)] = "@@FNT".($i); 277 } 278 279 // output the footnote reference and link, incl. onmouseover for insitu footnote popup 280 $this->doc .= '<a href="#fn'.$id.'" name="fnt'.$id.'" class="fn_top" onmouseover="fnt(\''.$id.'\', this, event);">'.$id.')</a>'; 281 } 282 283 function listu_open() { 284 $this->doc .= '<ul>'.DOKU_LF; 285 } 286 287 function listu_close() { 288 $this->doc .= '</ul>'.DOKU_LF; 289 } 290 291 function listo_open() { 292 $this->doc .= '<ol>'.DOKU_LF; 293 } 294 295 function listo_close() { 296 $this->doc .= '</ol>'.DOKU_LF; 297 } 298 299 function listitem_open($level) { 300 $this->doc .= '<li class="level'.$level.'">'; 301 } 302 303 function listitem_close() { 304 $this->doc .= '</li>'.DOKU_LF; 305 } 306 307 function listcontent_open() { 308 $this->doc .= '<div class="li">'; 309 } 310 311 function listcontent_close() { 312 $this->doc .= '</div>'.DOKU_LF; 313 } 314 315 function unformatted($text) { 316 $this->doc .= $this->_xmlEntities($text); 317 } 318 319 /** 320 * Execute PHP code if allowed 321 * 322 * @author Andreas Gohr <andi@splitbrain.org> 323 */ 324 function php($text) { 325 global $conf; 326 if($conf['phpok']){ 327 ob_start(); 328 eval($text); 329 $this->doc .= ob_get_contents(); 330 ob_end_clean(); 331 }else{ 332 $this->file($text); 333 } 334 } 335 336 /** 337 * Insert HTML if allowed 338 * 339 * @author Andreas Gohr <andi@splitbrain.org> 340 */ 341 function html($text) { 342 global $conf; 343 if($conf['htmlok']){ 344 $this->doc .= $text; 345 }else{ 346 $this->file($text); 347 } 348 } 349 350 function preformatted($text) { 351 $this->doc .= '<pre class="code">' . $this->_xmlEntities($text) . '</pre>'. DOKU_LF; 352 } 353 354 function file($text) { 355 $this->doc .= '<pre class="file">' . $this->_xmlEntities($text). '</pre>'. DOKU_LF; 356 } 357 358 function quote_open() { 359 $this->doc .= '<blockquote>'.DOKU_LF; 360 } 361 362 function quote_close() { 363 $this->doc .= '</blockquote>'.DOKU_LF; 364 } 365 366 /** 367 * Callback for code text 368 * 369 * Uses GeSHi to highlight language syntax 370 * 371 * @author Andreas Gohr <andi@splitbrain.org> 372 */ 373 function code($text, $language = NULL) { 374 global $conf; 375 376 if ( is_null($language) ) { 377 $this->preformatted($text); 378 } else { 379 //strip leading blank line 380 $text = preg_replace('/^\s*?\n/','',$text); 381 // Handle with Geshi here 382 require_once(DOKU_INC . 'inc/geshi.php'); 383 $geshi = new GeSHi($text, strtolower($language), DOKU_INC . 'inc/geshi'); 384 $geshi->set_encoding('utf-8'); 385 $geshi->enable_classes(); 386 $geshi->set_header_type(GESHI_HEADER_PRE); 387 $geshi->set_overall_class("code $language"); 388 $geshi->set_link_target($conf['target']['extern']); 389 390 $text = $geshi->parse_code(); 391 $this->doc .= $text; 392 } 393 } 394 395 function acronym($acronym) { 396 397 if ( array_key_exists($acronym, $this->acronyms) ) { 398 399 $title = $this->_xmlEntities($this->acronyms[$acronym]); 400 401 $this->doc .= '<acronym title="'.$title 402 .'">'.$this->_xmlEntities($acronym).'</acronym>'; 403 404 } else { 405 $this->doc .= $this->_xmlEntities($acronym); 406 } 407 } 408 409 function smiley($smiley) { 410 if ( array_key_exists($smiley, $this->smileys) ) { 411 $title = $this->_xmlEntities($this->smileys[$smiley]); 412 $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 413 '" align="middle" alt="'. 414 $this->_xmlEntities($smiley).'" />'; 415 } else { 416 $this->doc .= $this->_xmlEntities($smiley); 417 } 418 } 419 420 /* 421 * not used 422 function wordblock($word) { 423 if ( array_key_exists($word, $this->badwords) ) { 424 $this->doc .= '** BLEEP **'; 425 } else { 426 $this->doc .= $this->_xmlEntities($word); 427 } 428 } 429 */ 430 431 function entity($entity) { 432 if ( array_key_exists($entity, $this->entities) ) { 433 $this->doc .= $this->entities[$entity]; 434 } else { 435 $this->doc .= $this->_xmlEntities($entity); 436 } 437 } 438 439 function multiplyentity($x, $y) { 440 $this->doc .= "$x×$y"; 441 } 442 443 function singlequoteopening() { 444 $this->doc .= "‘"; 445 } 446 447 function singlequoteclosing() { 448 $this->doc .= "’"; 449 } 450 451 function doublequoteopening() { 452 $this->doc .= "“"; 453 } 454 455 function doublequoteclosing() { 456 $this->doc .= "”"; 457 } 458 459 /** 460 */ 461 function camelcaselink($link) { 462 $this->internallink($link,$link); 463 } 464 465 466 function locallink($hash, $name = NULL){ 467 global $ID; 468 $name = $this->_getLinkTitle($name, $hash, $isImage); 469 $hash = $this->_headerToLink($hash); 470 $title = $ID.' ↵'; 471 $this->doc .= '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 472 $this->doc .= $name; 473 $this->doc .= '</a>'; 474 } 475 476 /** 477 * Render an internal Wiki Link 478 * 479 * $search and $returnonly are not for the renderer but are used 480 * elsewhere - no need to implement them in other renderers 481 * 482 * @author Andreas Gohr <andi@splitbrain.org> 483 */ 484 function internallink($id, $name = NULL, $search=NULL,$returnonly=false) { 485 global $conf; 486 global $ID; 487 // default name is based on $id as given 488 $default = $this->_simpleTitle($id); 489 // now first resolve and clean up the $id 490 resolve_pageid(getNS($ID),$id,$exists); 491 $name = $this->_getLinkTitle($name, $default, $isImage, $id); 492 if ( !$isImage ) { 493 if ( $exists ) { 494 $class='wikilink1'; 495 } else { 496 $class='wikilink2'; 497 } 498 } else { 499 $class='media'; 500 } 501 502 //keep hash anchor 503 list($id,$hash) = split('#',$id,2); 504 505 //prepare for formating 506 $link['target'] = $conf['target']['wiki']; 507 $link['style'] = ''; 508 $link['pre'] = ''; 509 $link['suf'] = ''; 510 // highlight link to current page 511 if ($id == $ID) { 512 $link['pre'] = '<span class="curid">'; 513 $link['suf'] = '</span>'; 514 } 515 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 516 $link['class'] = $class; 517 $link['url'] = wl($id); 518 $link['name'] = $name; 519 $link['title'] = $id; 520 //add search string 521 if($search){ 522 ($conf['userewrite']) ? $link['url'].='?s=' : $link['url'].='&s='; 523 $link['url'] .= urlencode($search); 524 } 525 526 //keep hash 527 if($hash) $link['url'].='#'.$hash; 528 529 //output formatted 530 if($returnonly){ 531 return $this->_formatLink($link); 532 }else{ 533 $this->doc .= $this->_formatLink($link); 534 } 535 } 536 537 function externallink($url, $name = NULL) { 538 global $conf; 539 540 $name = $this->_getLinkTitle($name, $url, $isImage); 541 542 // add protocol on simple short URLs 543 if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')) $url = 'ftp://'.$url; 544 if(substr($url,0,3) == 'www') $url = 'http://'.$url; 545 546 if ( !$isImage ) { 547 $class='urlextern'; 548 } else { 549 $class='media'; 550 } 551 552 //prepare for formating 553 $link['target'] = $conf['target']['extern']; 554 $link['style'] = ''; 555 $link['pre'] = ''; 556 $link['suf'] = ''; 557 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 558 $link['class'] = $class; 559 $link['url'] = $url; 560 $link['name'] = $name; 561 $link['title'] = $this->_xmlEntities($url); 562 if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 563 564 //output formatted 565 $this->doc .= $this->_formatLink($link); 566 } 567 568 /** 569 */ 570 function interwikilink($match, $name = NULL, $wikiName, $wikiUri) { 571 global $conf; 572 573 $link = array(); 574 $link['target'] = $conf['target']['interwiki']; 575 $link['pre'] = ''; 576 $link['suf'] = ''; 577 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 578 $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 579 580 if ( !$isImage ) { 581 $link['class'] = 'interwiki'; 582 } else { 583 $link['class'] = 'media'; 584 } 585 586 //get interwiki URL 587 if ( isset($this->interwiki[$wikiName]) ) { 588 $url = $this->interwiki[$wikiName]; 589 } else { 590 // Default to Google I'm feeling lucky 591 $url = 'http://www.google.com/search?q={URL}&btnI=lucky'; 592 $wikiName = 'go'; 593 } 594 595 if(!$isImage){ 596 //if ico exists set additional style 597 if(@file_exists(DOKU_INC.'lib/images/interwiki/'.$wikiName.'.png')){ 598 $link['style']='background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$wikiName.'.png)'; 599 }elseif(@file_exists(DOKU_INC.'lib/images/interwiki/'.$wikiName.'.gif')){ 600 $link['style']='background-image: url('.DOKU_BASE.'lib/images/interwiki/'.$wikiName.'.gif)'; 601 } 602 } 603 604 //do we stay at the same server? Use local target 605 if( strpos($url,DOKU_URL) === 0 ){ 606 $link['target'] = $conf['target']['wiki']; 607 } 608 609 //replace placeholder 610 if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#',$url)){ 611 //use placeholders 612 $url = str_replace('{URL}',urlencode($wikiUri),$url); 613 $url = str_replace('{NAME}',$wikiUri,$url); 614 $parsed = parse_url($wikiUri); 615 if(!$parsed['port']) $parsed['port'] = 80; 616 $url = str_replace('{SCHEME}',$parsed['scheme'],$url); 617 $url = str_replace('{HOST}',$parsed['host'],$url); 618 $url = str_replace('{PORT}',$parsed['port'],$url); 619 $url = str_replace('{PATH}',$parsed['path'],$url); 620 $url = str_replace('{QUERY}',$parsed['query'],$url); 621 $link['url'] = $url; 622 }else{ 623 //default 624 $link['url'] = $url.urlencode($wikiUri); 625 } 626 627 $link['title'] = htmlspecialchars($link['url']); 628 629 //output formatted 630 $this->doc .= $this->_formatLink($link); 631 } 632 633 /** 634 */ 635 function windowssharelink($url, $name = NULL) { 636 global $conf; 637 global $lang; 638 //simple setup 639 $link['target'] = $conf['target']['windows']; 640 $link['pre'] = ''; 641 $link['suf'] = ''; 642 $link['style'] = ''; 643 //Display error on browsers other than IE 644 $link['more'] = 'onclick="if(document.all == null){alert(\''. 645 $this->_xmlEntities($lang['nosmblinks'],ENT_QUOTES). 646 '\');}" onkeypress="if(document.all == null){alert(\''. 647 $this->_xmlEntities($lang['nosmblinks'],ENT_QUOTES).'\');}"'; 648 649 $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 650 if ( !$isImage ) { 651 $link['class'] = 'windows'; 652 } else { 653 $link['class'] = 'media'; 654 } 655 656 657 $link['title'] = $this->_xmlEntities($url); 658 $url = str_replace('\\','/',$url); 659 $url = 'file:///'.$url; 660 $link['url'] = $url; 661 662 //output formatted 663 $this->doc .= $this->_formatLink($link); 664 } 665 666 function emaillink($address, $name = NULL) { 667 global $conf; 668 //simple setup 669 $link = array(); 670 $link['target'] = ''; 671 $link['pre'] = ''; 672 $link['suf'] = ''; 673 $link['style'] = ''; 674 $link['more'] = ''; 675 676 //we just test for image here - we need to encode the title our self 677 $this->_getLinkTitle($name, $address, $isImage); 678 if ( !$isImage ) { 679 $link['class']='mail'; 680 } else { 681 $link['class']='media'; 682 } 683 684 //shields up 685 if($conf['mailguard']=='visible'){ 686 //the mail name gets some visible encoding 687 $address = str_replace('@',' [at] ',$address); 688 $address = str_replace('.',' [dot] ',$address); 689 $address = str_replace('-',' [dash] ',$address); 690 691 $title = $this->_xmlEntities($address); 692 if(empty($name)){ 693 $name = $this->_xmlEntities($address); 694 }else{ 695 $name = $this->_xmlEntities($name); 696 } 697 }elseif($conf['mailguard']=='hex'){ 698 //encode every char to a hex entity 699 for ($x=0; $x < strlen($address); $x++) { 700 $encode .= '&#x' . bin2hex($address[$x]).';'; 701 } 702 $address = $encode; 703 $title = $encode; 704 if(empty($name)){ 705 $name = $encode; 706 }else{ 707 $name = $this->_xmlEntities($name); 708 } 709 }else{ 710 //keep address as is 711 $title = $this->_xmlEntities($address); 712 if(empty($name)){ 713 $name = $this->_xmlEntities($address); 714 }else{ 715 $name = $this->_xmlEntities($name); 716 } 717 } 718 719 $link['url'] = 'mailto:'.rawurlencode($address); 720 $link['name'] = $name; 721 $link['title'] = $title; 722 723 //output formatted 724 $this->doc .= $this->_formatLink($link); 725 } 726 727 /** 728 * @todo don't add link for flash 729 */ 730 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 731 $height=NULL, $cache=NULL) { 732 global $conf; 733 global $ID; 734 resolve_mediaid(getNS($ID),$src, $exists); 735 736 $link = array(); 737 $link['class'] = 'media'; 738 $link['style'] = ''; 739 $link['pre'] = ''; 740 $link['suf'] = ''; 741 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 742 $link['target'] = $conf['target']['media']; 743 744 $link['title'] = $this->_xmlEntities($src); 745 list($ext,$mime) = mimetype($src); 746 if(substr($mime,0,5) == 'image' && !preg_match('#^(https?|ftp)://#i',$src)){ 747 $link['url']= DOKU_BASE.'lib/exe/detail.php?id='.$ID.'&cache='.$cache.'&media='.urlencode($src); 748 }else{ 749 $link['url']= DOKU_BASE.'lib/exe/fetch.php?cache='.$cache.'&media='.urlencode($src); 750 } 751 $link['name'] = $this->_media ($src, $title, $align, $width, $height, $cache); 752 753 754 //output formatted 755 $this->doc .= $this->_formatLink($link); 756 } 757 758 /** 759 * @todo don't add link for flash 760 */ 761 function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL, 762 $height=NULL, $cache=NULL) { 763 global $conf; 764 765 $link = array(); 766 $link['class'] = 'media'; 767 $link['style'] = ''; 768 $link['pre'] = ''; 769 $link['suf'] = ''; 770 $link['more'] = 'onclick="return svchk()" onkeypress="return svchk()"'; 771 $link['target'] = $conf['target']['media']; 772 773 $link['title'] = $this->_xmlEntities($src); 774 $link['url'] = DOKU_BASE.'lib/exe/fetch.php?cache='.$cache.'&media='.urlencode($src); 775 $link['name'] = $this->_media ($src, $title, $align, $width, $height, $cache); 776 777 778 //output formatted 779 $this->doc .= $this->_formatLink($link); 780 } 781 782 /** 783 * Renders an RSS feed using Magpie 784 * 785 * @author Andreas Gohr <andi@splitbrain.org> 786 */ 787 function rss ($url){ 788 global $lang; 789 define('MAGPIE_CACHE_ON', false); //we do our own caching 790 define('MAGPIE_DIR', DOKU_INC.'inc/magpie/'); 791 define('MAGPIE_OUTPUT_ENCODING','UTF-8'); //return all feeds as UTF-8 792 require_once(MAGPIE_DIR.'/rss_fetch.inc'); 793 794 //disable warning while fetching 795 $elvl = error_reporting(E_ERROR); 796 $rss = fetch_rss($url); 797 error_reporting($elvl); 798 799 $this->doc .= '<ul class="rss">'; 800 if($rss){ 801 foreach ($rss->items as $item ) { 802 $this->doc .= '<li>'; 803 $this->externallink($item['link'],$item['title']); 804 $this->doc .= '</li>'; 805 } 806 }else{ 807 $this->doc .= '<li>'; 808 $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 809 $this->externallink($url); 810 $this->doc .= '</li>'; 811 } 812 $this->doc .= '</ul>'; 813 } 814 815 // $numrows not yet implemented 816 function table_open($maxcols = NULL, $numrows = NULL){ 817 $this->doc .= '<table class="inline">'.DOKU_LF; 818 } 819 820 function table_close(){ 821 $this->doc .= '</table>'.DOKU_LF.'<br />'.DOKU_LF; 822 } 823 824 function tablerow_open(){ 825 $this->doc .= DOKU_TAB . '<tr>' . DOKU_LF . DOKU_TAB . DOKU_TAB; 826 } 827 828 function tablerow_close(){ 829 $this->doc .= DOKU_LF . DOKU_TAB . '</tr>' . DOKU_LF; 830 } 831 832 function tableheader_open($colspan = 1, $align = NULL){ 833 $this->doc .= '<th'; 834 if ( !is_null($align) ) { 835 $this->doc .= ' class="'.$align.'align"'; 836 } 837 if ( $colspan > 1 ) { 838 $this->doc .= ' colspan="'.$colspan.'"'; 839 } 840 $this->doc .= '>'; 841 } 842 843 function tableheader_close(){ 844 $this->doc .= '</th>'; 845 } 846 847 function tablecell_open($colspan = 1, $align = NULL){ 848 $this->doc .= '<td'; 849 if ( !is_null($align) ) { 850 $this->doc .= ' class="'.$align.'align"'; 851 } 852 if ( $colspan > 1 ) { 853 $this->doc .= ' colspan="'.$colspan.'"'; 854 } 855 $this->doc .= '>'; 856 } 857 858 function tablecell_close(){ 859 $this->doc .= '</td>'; 860 } 861 862 //---------------------------------------------------------- 863 // Utils 864 865 /** 866 * Build a link 867 * 868 * Assembles all parts defined in $link returns HTML for the link 869 * 870 * @author Andreas Gohr <andi@splitbrain.org> 871 */ 872 function _formatLink($link){ 873 //make sure the url is XHTML compliant (skip mailto) 874 if(substr($link['url'],0,7) != 'mailto:'){ 875 $link['url'] = str_replace('&','&',$link['url']); 876 $link['url'] = str_replace('&amp;','&',$link['url']); 877 } 878 //remove double encodings in titles 879 $link['title'] = str_replace('&amp;','&',$link['title']); 880 881 $ret = ''; 882 $ret .= $link['pre']; 883 $ret .= '<a href="'.$link['url'].'"'; 884 if($link['class']) $ret .= ' class="'.$link['class'].'"'; 885 if($link['target']) $ret .= ' target="'.$link['target'].'"'; 886 if($link['title']) $ret .= ' title="'.$link['title'].'"'; 887 if($link['style']) $ret .= ' style="'.$link['style'].'"'; 888 if($link['more']) $ret .= ' '.$link['more']; 889 $ret .= '>'; 890 $ret .= $link['name']; 891 $ret .= '</a>'; 892 $ret .= $link['suf']; 893 return $ret; 894 } 895 896 /** 897 * Removes any Namespace from the given name but keeps 898 * casing and special chars 899 * 900 * @author Andreas Gohr <andi@splitbrain.org> 901 */ 902 function _simpleTitle($name){ 903 global $conf; 904 905 if($conf['useslash']){ 906 $nssep = '[:;/]'; 907 }else{ 908 $nssep = '[:;]'; 909 } 910 $name = preg_replace('!.*'.$nssep.'!','',$name); 911 //if there is a hash we use the ancor name only 912 $name = preg_replace('!.*#!','',$name); 913 return $name; 914 } 915 916 /** 917 * Renders internal and external media 918 * 919 * @author Andreas Gohr <andi@splitbrain.org> 920 */ 921 function _media ($src, $title=NULL, $align=NULL, $width=NULL, 922 $height=NULL, $cache=NULL) { 923 924 $ret = ''; 925 926 list($ext,$mime) = mimetype($src); 927 if(substr($mime,0,5) == 'image'){ 928 //add image tag 929 $ret .= '<img src="'.DOKU_BASE.'lib/exe/fetch.php?w='.$width.'&h='.$height. 930 '&cache='.$cache.'&media='.urlencode($src).'"'; 931 932 $ret .= ' class="media'.$align.'"'; 933 934 if (!is_null($title)) { 935 $ret .= ' title="'.$this->_xmlEntities($title).'"'; 936 $ret .= ' alt="'.$this->_xmlEntities($title).'"'; 937 }elseif($ext == 'jpg' || $ext == 'jpeg'){ 938 //try to use the caption from IPTC/EXIF 939 require_once(DOKU_INC.'inc/JpegMeta.php'); 940 $jpeg =& new JpegMeta(mediaFN($src)); 941 if($jpeg !== false) $cap = $jpeg->getTitle(); 942 if($cap){ 943 $ret .= ' title="'.$this->_xmlEntities($cap).'"'; 944 $ret .= ' alt="'.$this->_xmlEntities($cap).'"'; 945 } 946 }else{ 947 $ret .= ' alt=""'; 948 } 949 950 if ( !is_null($width) ) 951 $ret .= ' width="'.$this->_xmlEntities($width).'"'; 952 953 if ( !is_null($height) ) 954 $ret .= ' height="'.$this->_xmlEntities($height).'"'; 955 956 $ret .= ' />'; 957 958 }elseif($mime == 'application/x-shockwave-flash'){ 959 $ret .= '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'. 960 ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"'; 961 if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"'; 962 if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"'; 963 $ret .= '>'.DOKU_LF; 964 $ret .= '<param name="movie" value="'.DOKU_BASE.'lib/exe/fetch.php?media='.urlencode($src).'" />'.DOKU_LF; 965 $ret .= '<param name="quality" value="high" />'.DOKU_LF; 966 $ret .= '<embed src="'.DOKU_BASE.'lib/exe/fetch.php?media='.urlencode($src).'"'. 967 ' quality="high"'; 968 if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"'; 969 if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"'; 970 $ret .= ' type="application/x-shockwave-flash"'. 971 ' pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>'.DOKU_LF; 972 $ret .= '</object>'.DOKU_LF; 973 974 }elseif(!is_null($title)){ 975 // well at least we have a title to display 976 $ret .= $this->_xmlEntities($title); 977 }else{ 978 // just show the source 979 $ret .= $this->_xmlEntities($src); 980 } 981 982 return $ret; 983 } 984 985 function _xmlEntities($string) { 986 return htmlspecialchars($string); 987 } 988 989 function _headerToLink($title) { 990 return str_replace(':','',cleanID($title)); 991 } 992 993 /** 994 * Adds code for section editing button 995 * 996 * This is just aplaceholder and gets replace by the button if 997 * section editing is allowed 998 * 999 * @author Andreas Gohr <andi@splitbrain.org> 1000 */ 1001 function _secedit($f, $t){ 1002 $this->doc .= '<!-- SECTION ['.$f.'-'.$t.'] -->'; 1003 } 1004 1005 /** 1006 * Construct a title and handle images in titles 1007 * 1008 * @author Harry Fuecks <hfuecks@gmail.com> 1009 */ 1010 function _getLinkTitle($title, $default, & $isImage, $id=NULL) { 1011 global $conf; 1012 1013 $isImage = FALSE; 1014 if ( is_null($title) ) { 1015 if ($conf['useheading'] && $id) { 1016 $heading = p_get_first_heading($id); 1017 if ($heading) { 1018 return $this->_xmlEntities($heading); 1019 } 1020 } 1021 return $this->_xmlEntities($default); 1022 } else if ( is_string($title) ) { 1023 return $this->_xmlEntities($title); 1024 } else if ( is_array($title) ) { 1025 $isImage = TRUE; 1026 return $this->_imageTitle($title); 1027 } 1028 } 1029 1030 /** 1031 * Returns an HTML code for images used in link titles 1032 * 1033 * @todo Resolve namespace on internal images 1034 * @author Andreas Gohr <andi@splitbrain.org> 1035 */ 1036 function _imageTitle($img) { 1037 return $this->_media($img['src'], 1038 $img['title'], 1039 $img['align'], 1040 $img['width'], 1041 $img['height'], 1042 $img['cache']); 1043 } 1044} 1045 1046//Setup VIM: ex: et ts=4 enc=utf-8 : 1047