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