10cecf9d5Sandi<?php 2b625487dSandi/** 3b625487dSandi * Renderer for XHTML output 4b625487dSandi * 5b625487dSandi * @author Harry Fuecks <hfuecks@gmail.com> 6b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 7b625487dSandi */ 8fa8adffeSAndreas Gohrif(!defined('DOKU_INC')) die('meh.'); 90cecf9d5Sandi 100cecf9d5Sandiif(!defined('DOKU_LF')) { 110cecf9d5Sandi // Some whitespace to help View > Source 120cecf9d5Sandi define ('DOKU_LF', "\n"); 130cecf9d5Sandi} 140cecf9d5Sandi 150cecf9d5Sandiif(!defined('DOKU_TAB')) { 160cecf9d5Sandi // Some whitespace to help View > Source 170cecf9d5Sandi define ('DOKU_TAB', "\t"); 180cecf9d5Sandi} 190cecf9d5Sandi 200cecf9d5Sandi/** 213dd5c225SAndreas Gohr * The XHTML Renderer 223dd5c225SAndreas Gohr * 233dd5c225SAndreas Gohr * This is DokuWiki's main renderer used to display page content in the wiki 240cecf9d5Sandi */ 25ac83b9d8Sandiclass Doku_Renderer_xhtml extends Doku_Renderer { 263dd5c225SAndreas Gohr /** @var array store the table of contents */ 273dd5c225SAndreas Gohr public $toc = array(); 280cecf9d5Sandi 293dd5c225SAndreas Gohr /** @var array A stack of section edit data */ 303dd5c225SAndreas Gohr protected $sectionedits = array(); 314bde2196Slisps var $date_at = ''; // link pages and media against this revision 32c5a8fd96SAndreas Gohr 333dd5c225SAndreas Gohr /** @var int last section edit id, used by startSectionEdit */ 343dd5c225SAndreas Gohr protected $lastsecid = 0; 350cecf9d5Sandi 363dd5c225SAndreas Gohr /** @var array the list of headers used to create unique link ids */ 373dd5c225SAndreas Gohr protected $headers = array(); 383dd5c225SAndreas Gohr 3916ec3e37SAndreas Gohr /** @var array a list of footnotes, list starts at 1! */ 403dd5c225SAndreas Gohr protected $footnotes = array(); 417764a90aSandi 423dd5c225SAndreas Gohr /** @var int current section level */ 433dd5c225SAndreas Gohr protected $lastlevel = 0; 443dd5c225SAndreas Gohr /** @var array section node tracker */ 453dd5c225SAndreas Gohr protected $node = array(0, 0, 0, 0, 0); 463dd5c225SAndreas Gohr 473dd5c225SAndreas Gohr /** @var string temporary $doc store */ 483dd5c225SAndreas Gohr protected $store = ''; 493dd5c225SAndreas Gohr 503dd5c225SAndreas Gohr /** @var array global counter, for table classes etc. */ 513dd5c225SAndreas Gohr protected $_counter = array(); // 523dd5c225SAndreas Gohr 533dd5c225SAndreas Gohr /** @var int counts the code and file blocks, used to provide download links */ 543dd5c225SAndreas Gohr protected $_codeblock = 0; 553dd5c225SAndreas Gohr 563dd5c225SAndreas Gohr /** @var array list of allowed URL schemes */ 573dd5c225SAndreas Gohr protected $schemes = null; 58b5742cedSPierre Spring 5990df9a4dSAdrian Lang /** 6090df9a4dSAdrian Lang * Register a new edit section range 6190df9a4dSAdrian Lang * 6242ea7f44SGerrit Uitslag * @param string $type The section type identifier 6342ea7f44SGerrit Uitslag * @param string $title The section title 6442ea7f44SGerrit Uitslag * @param int $start The byte position for the edit start 6590df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 6642ea7f44SGerrit Uitslag * 6790df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 6890df9a4dSAdrian Lang */ 692571786cSLarsDW223 public function startSectionEdit($start, $type, $title = null, $hid = null) { 702571786cSLarsDW223 $this->sectionedits[] = array(++$this->lastsecid, $start, $type, $title, $hid); 71b04a190dSMichael Hamann return 'sectionedit'.$this->lastsecid; 7290df9a4dSAdrian Lang } 7390df9a4dSAdrian Lang 7490df9a4dSAdrian Lang /** 7590df9a4dSAdrian Lang * Finish an edit section range 7690df9a4dSAdrian Lang * 7742ea7f44SGerrit Uitslag * @param int $end The byte position for the edit end; null for the rest of the page 7842ea7f44SGerrit Uitslag * 7990df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 8090df9a4dSAdrian Lang */ 812571786cSLarsDW223 public function finishSectionEdit($end = null, $hid = null) { 822571786cSLarsDW223 list($id, $start, $type, $title, $hid) = array_pop($this->sectionedits); 83d9e36cbeSAdrian Lang if(!is_null($end) && $end <= $start) { 8400c13053SAdrian Lang return; 8500c13053SAdrian Lang } 8640868f2fSAdrian Lang $this->doc .= "<!-- EDIT$id ".strtoupper($type).' '; 8740868f2fSAdrian Lang if(!is_null($title)) { 8840868f2fSAdrian Lang $this->doc .= '"'.str_replace('"', '', $title).'" '; 8940868f2fSAdrian Lang } 902571786cSLarsDW223 if(!is_null($hid)) { 912571786cSLarsDW223 $this->doc .= '"'.$hid.'" '; 922571786cSLarsDW223 } 93d9e36cbeSAdrian Lang $this->doc .= "[$start-".(is_null($end) ? '' : $end).'] -->'; 9490df9a4dSAdrian Lang } 9590df9a4dSAdrian Lang 963dd5c225SAndreas Gohr /** 973dd5c225SAndreas Gohr * Returns the format produced by this renderer. 983dd5c225SAndreas Gohr * 993dd5c225SAndreas Gohr * @return string always 'xhtml' 1003dd5c225SAndreas Gohr */ 1015f70445dSAndreas Gohr function getFormat() { 1025f70445dSAndreas Gohr return 'xhtml'; 1035f70445dSAndreas Gohr } 1045f70445dSAndreas Gohr 1053dd5c225SAndreas Gohr /** 1063dd5c225SAndreas Gohr * Initialize the document 1073dd5c225SAndreas Gohr */ 1080cecf9d5Sandi function document_start() { 109c5a8fd96SAndreas Gohr //reset some internals 110c5a8fd96SAndreas Gohr $this->toc = array(); 111c5a8fd96SAndreas Gohr $this->headers = array(); 1120cecf9d5Sandi } 1130cecf9d5Sandi 1143dd5c225SAndreas Gohr /** 1153dd5c225SAndreas Gohr * Finalize the document 1163dd5c225SAndreas Gohr */ 1170cecf9d5Sandi function document_end() { 11890df9a4dSAdrian Lang // Finish open section edits. 11990df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 12090df9a4dSAdrian Lang if($this->sectionedits[count($this->sectionedits) - 1][1] <= 1) { 12190df9a4dSAdrian Lang // If there is only one section, do not write a section edit 12290df9a4dSAdrian Lang // marker. 12390df9a4dSAdrian Lang array_pop($this->sectionedits); 12490df9a4dSAdrian Lang } else { 125d9e36cbeSAdrian Lang $this->finishSectionEdit(); 12690df9a4dSAdrian Lang } 12790df9a4dSAdrian Lang } 12890df9a4dSAdrian Lang 1290cecf9d5Sandi if(count($this->footnotes) > 0) { 130a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 131d74aace9Schris 13216ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 133d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 134d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 135d74aace9Schris 136d74aace9Schris // open the footnote and set the anchor and backlink 137d74aace9Schris $this->doc .= '<div class="fn">'; 13816cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 13929bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 140d74aace9Schris 141d74aace9Schris // get any other footnotes that use the same markup 142d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 143d74aace9Schris 144d74aace9Schris if(count($alt)) { 145d74aace9Schris foreach($alt as $ref) { 146d74aace9Schris // set anchor and backlink for the other footnotes 14716ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 14816ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 149d74aace9Schris } 150d74aace9Schris } 151d74aace9Schris 152d74aace9Schris // add footnote markup and close this footnote 153694afa06SAnika Henke $this->doc .= '<div class="content">'.$footnote.'</div>'; 154d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 155d74aace9Schris } 1560cecf9d5Sandi } 157a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1580cecf9d5Sandi } 159c5a8fd96SAndreas Gohr 160b8595a66SAndreas Gohr // Prepare the TOC 161851f2e89SAnika Henke global $conf; 162851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 163b8595a66SAndreas Gohr global $TOC; 164b8595a66SAndreas Gohr $TOC = $this->toc; 1650cecf9d5Sandi } 1663e55d035SAndreas Gohr 1673e55d035SAndreas Gohr // make sure there are no empty paragraphs 16827918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 169e41c4da9SAndreas Gohr } 1700cecf9d5Sandi 1713dd5c225SAndreas Gohr /** 1723dd5c225SAndreas Gohr * Add an item to the TOC 1733dd5c225SAndreas Gohr * 1743dd5c225SAndreas Gohr * @param string $id the hash link 1753dd5c225SAndreas Gohr * @param string $text the text to display 1763dd5c225SAndreas Gohr * @param int $level the nesting level 1773dd5c225SAndreas Gohr */ 178e7856beaSchris function toc_additem($id, $text, $level) { 179af587fa8Sandi global $conf; 180af587fa8Sandi 181c5a8fd96SAndreas Gohr //handle TOC 182c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1837d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 184c5a8fd96SAndreas Gohr } 185e7856beaSchris } 186e7856beaSchris 1873dd5c225SAndreas Gohr /** 1883dd5c225SAndreas Gohr * Render a heading 1893dd5c225SAndreas Gohr * 1903dd5c225SAndreas Gohr * @param string $text the text to display 1913dd5c225SAndreas Gohr * @param int $level header level 1923dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1933dd5c225SAndreas Gohr */ 194e7856beaSchris function header($text, $level, $pos) { 19590df9a4dSAdrian Lang global $conf; 19690df9a4dSAdrian Lang 197f515db7fSAndreas Gohr if(blank($text)) return; //skip empty headlines 198e7856beaSchris 199e7856beaSchris $hid = $this->_headerToLink($text, true); 200e7856beaSchris 201e7856beaSchris //only add items within configured levels 202e7856beaSchris $this->toc_additem($hid, $text, $level); 203c5a8fd96SAndreas Gohr 20491459163SAnika Henke // adjust $node to reflect hierarchy of levels 20591459163SAnika Henke $this->node[$level - 1]++; 20691459163SAnika Henke if($level < $this->lastlevel) { 20791459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 20891459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 20991459163SAnika Henke } 21091459163SAnika Henke } 21191459163SAnika Henke $this->lastlevel = $level; 21291459163SAnika Henke 21390df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 21490df9a4dSAdrian Lang count($this->sectionedits) > 0 && 2153dd5c225SAndreas Gohr $this->sectionedits[count($this->sectionedits) - 1][2] === 'section' 2163dd5c225SAndreas Gohr ) { 2176c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 21890df9a4dSAdrian Lang } 21990df9a4dSAdrian Lang 220c5a8fd96SAndreas Gohr // write the header 22190df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 22290df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 2232571786cSLarsDW223 $this->doc .= ' class="'.$this->startSectionEdit($pos, 'section', $text, $hid).'"'; 22490df9a4dSAdrian Lang } 22516cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 226a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 22716cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2280cecf9d5Sandi } 2290cecf9d5Sandi 2303dd5c225SAndreas Gohr /** 2313dd5c225SAndreas Gohr * Open a new section 2323dd5c225SAndreas Gohr * 2333dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2343dd5c225SAndreas Gohr */ 2350cecf9d5Sandi function section_open($level) { 2369864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2370cecf9d5Sandi } 2380cecf9d5Sandi 2393dd5c225SAndreas Gohr /** 2403dd5c225SAndreas Gohr * Close the current section 2413dd5c225SAndreas Gohr */ 2420cecf9d5Sandi function section_close() { 243a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2440cecf9d5Sandi } 2450cecf9d5Sandi 2463dd5c225SAndreas Gohr /** 2473dd5c225SAndreas Gohr * Render plain text data 2483dd5c225SAndreas Gohr * 2493dd5c225SAndreas Gohr * @param $text 2503dd5c225SAndreas Gohr */ 2510cecf9d5Sandi function cdata($text) { 252a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2530cecf9d5Sandi } 2540cecf9d5Sandi 2553dd5c225SAndreas Gohr /** 2563dd5c225SAndreas Gohr * Open a paragraph 2573dd5c225SAndreas Gohr */ 2580cecf9d5Sandi function p_open() { 25959869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2600cecf9d5Sandi } 2610cecf9d5Sandi 2623dd5c225SAndreas Gohr /** 2633dd5c225SAndreas Gohr * Close a paragraph 2643dd5c225SAndreas Gohr */ 2650cecf9d5Sandi function p_close() { 26659869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2670cecf9d5Sandi } 2680cecf9d5Sandi 2693dd5c225SAndreas Gohr /** 2703dd5c225SAndreas Gohr * Create a line break 2713dd5c225SAndreas Gohr */ 2720cecf9d5Sandi function linebreak() { 273a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2740cecf9d5Sandi } 2750cecf9d5Sandi 2763dd5c225SAndreas Gohr /** 2773dd5c225SAndreas Gohr * Create a horizontal line 2783dd5c225SAndreas Gohr */ 2790cecf9d5Sandi function hr() { 2804beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2810cecf9d5Sandi } 2820cecf9d5Sandi 2833dd5c225SAndreas Gohr /** 2843dd5c225SAndreas Gohr * Start strong (bold) formatting 2853dd5c225SAndreas Gohr */ 2860cecf9d5Sandi function strong_open() { 287a2d649c4Sandi $this->doc .= '<strong>'; 2880cecf9d5Sandi } 2890cecf9d5Sandi 2903dd5c225SAndreas Gohr /** 2913dd5c225SAndreas Gohr * Stop strong (bold) formatting 2923dd5c225SAndreas Gohr */ 2930cecf9d5Sandi function strong_close() { 294a2d649c4Sandi $this->doc .= '</strong>'; 2950cecf9d5Sandi } 2960cecf9d5Sandi 2973dd5c225SAndreas Gohr /** 2983dd5c225SAndreas Gohr * Start emphasis (italics) formatting 2993dd5c225SAndreas Gohr */ 3000cecf9d5Sandi function emphasis_open() { 301a2d649c4Sandi $this->doc .= '<em>'; 3020cecf9d5Sandi } 3030cecf9d5Sandi 3043dd5c225SAndreas Gohr /** 3053dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3063dd5c225SAndreas Gohr */ 3070cecf9d5Sandi function emphasis_close() { 308a2d649c4Sandi $this->doc .= '</em>'; 3090cecf9d5Sandi } 3100cecf9d5Sandi 3113dd5c225SAndreas Gohr /** 3123dd5c225SAndreas Gohr * Start underline formatting 3133dd5c225SAndreas Gohr */ 3140cecf9d5Sandi function underline_open() { 31502e51121SAnika Henke $this->doc .= '<em class="u">'; 3160cecf9d5Sandi } 3170cecf9d5Sandi 3183dd5c225SAndreas Gohr /** 3193dd5c225SAndreas Gohr * Stop underline formatting 3203dd5c225SAndreas Gohr */ 3210cecf9d5Sandi function underline_close() { 32202e51121SAnika Henke $this->doc .= '</em>'; 3230cecf9d5Sandi } 3240cecf9d5Sandi 3253dd5c225SAndreas Gohr /** 3263dd5c225SAndreas Gohr * Start monospace formatting 3273dd5c225SAndreas Gohr */ 3280cecf9d5Sandi function monospace_open() { 329a2d649c4Sandi $this->doc .= '<code>'; 3300cecf9d5Sandi } 3310cecf9d5Sandi 3323dd5c225SAndreas Gohr /** 3333dd5c225SAndreas Gohr * Stop monospace formatting 3343dd5c225SAndreas Gohr */ 3350cecf9d5Sandi function monospace_close() { 336a2d649c4Sandi $this->doc .= '</code>'; 3370cecf9d5Sandi } 3380cecf9d5Sandi 3393dd5c225SAndreas Gohr /** 3403dd5c225SAndreas Gohr * Start a subscript 3413dd5c225SAndreas Gohr */ 3420cecf9d5Sandi function subscript_open() { 343a2d649c4Sandi $this->doc .= '<sub>'; 3440cecf9d5Sandi } 3450cecf9d5Sandi 3463dd5c225SAndreas Gohr /** 3473dd5c225SAndreas Gohr * Stop a subscript 3483dd5c225SAndreas Gohr */ 3490cecf9d5Sandi function subscript_close() { 350a2d649c4Sandi $this->doc .= '</sub>'; 3510cecf9d5Sandi } 3520cecf9d5Sandi 3533dd5c225SAndreas Gohr /** 3543dd5c225SAndreas Gohr * Start a superscript 3553dd5c225SAndreas Gohr */ 3560cecf9d5Sandi function superscript_open() { 357a2d649c4Sandi $this->doc .= '<sup>'; 3580cecf9d5Sandi } 3590cecf9d5Sandi 3603dd5c225SAndreas Gohr /** 3613dd5c225SAndreas Gohr * Stop a superscript 3623dd5c225SAndreas Gohr */ 3630cecf9d5Sandi function superscript_close() { 364a2d649c4Sandi $this->doc .= '</sup>'; 3650cecf9d5Sandi } 3660cecf9d5Sandi 3673dd5c225SAndreas Gohr /** 3683dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3693dd5c225SAndreas Gohr */ 3700cecf9d5Sandi function deleted_open() { 371a2d649c4Sandi $this->doc .= '<del>'; 3720cecf9d5Sandi } 3730cecf9d5Sandi 3743dd5c225SAndreas Gohr /** 3753dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 3763dd5c225SAndreas Gohr */ 3770cecf9d5Sandi function deleted_close() { 378a2d649c4Sandi $this->doc .= '</del>'; 3790cecf9d5Sandi } 3800cecf9d5Sandi 3813fd0b676Sandi /** 3823fd0b676Sandi * Callback for footnote start syntax 3833fd0b676Sandi * 3843fd0b676Sandi * All following content will go to the footnote instead of 385d74aace9Schris * the document. To achieve this the previous rendered content 3863fd0b676Sandi * is moved to $store and $doc is cleared 3873fd0b676Sandi * 3883fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 3893fd0b676Sandi */ 3900cecf9d5Sandi function footnote_open() { 3917764a90aSandi 3927764a90aSandi // move current content to store and record footnote 3937764a90aSandi $this->store = $this->doc; 3947764a90aSandi $this->doc = ''; 3950cecf9d5Sandi } 3960cecf9d5Sandi 3973fd0b676Sandi /** 3983fd0b676Sandi * Callback for footnote end syntax 3993fd0b676Sandi * 4003fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 4013fd0b676Sandi * content is restored from $store again 4023fd0b676Sandi * 4033fd0b676Sandi * @author Andreas Gohr 4043fd0b676Sandi */ 4050cecf9d5Sandi function footnote_close() { 40616ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 40716ec3e37SAndreas Gohr static $fnid = 0; 40816ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 40916ec3e37SAndreas Gohr $fnid++; 4107764a90aSandi 411d74aace9Schris // recover footnote into the stack and restore old content 412d74aace9Schris $footnote = $this->doc; 4137764a90aSandi $this->doc = $this->store; 4147764a90aSandi $this->store = ''; 415d74aace9Schris 416d74aace9Schris // check to see if this footnote has been seen before 417d74aace9Schris $i = array_search($footnote, $this->footnotes); 418d74aace9Schris 419d74aace9Schris if($i === false) { 420d74aace9Schris // its a new footnote, add it to the $footnotes array 42116ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 422d74aace9Schris } else { 42316ec3e37SAndreas Gohr // seen this one before, save a placeholder 42416ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 425d74aace9Schris } 426d74aace9Schris 4276b379cbfSAndreas Gohr // output the footnote reference and link 42816ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4290cecf9d5Sandi } 4300cecf9d5Sandi 4313dd5c225SAndreas Gohr /** 4323dd5c225SAndreas Gohr * Open an unordered list 4330c4c0281SGerrit Uitslag * 4347d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4353dd5c225SAndreas Gohr */ 4360c4c0281SGerrit Uitslag function listu_open($classes = null) { 4370c4c0281SGerrit Uitslag $class = ''; 4380c4c0281SGerrit Uitslag if($classes !== null) { 4392e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4400c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4410c4c0281SGerrit Uitslag } 4420c4c0281SGerrit Uitslag $this->doc .= "<ul$class>".DOKU_LF; 4430cecf9d5Sandi } 4440cecf9d5Sandi 4453dd5c225SAndreas Gohr /** 4463dd5c225SAndreas Gohr * Close an unordered list 4473dd5c225SAndreas Gohr */ 4480cecf9d5Sandi function listu_close() { 449a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4500cecf9d5Sandi } 4510cecf9d5Sandi 4523dd5c225SAndreas Gohr /** 4533dd5c225SAndreas Gohr * Open an ordered list 4540c4c0281SGerrit Uitslag * 4557d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4563dd5c225SAndreas Gohr */ 4570c4c0281SGerrit Uitslag function listo_open($classes = null) { 4580c4c0281SGerrit Uitslag $class = ''; 4590c4c0281SGerrit Uitslag if($classes !== null) { 4602e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4610c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4620c4c0281SGerrit Uitslag } 4630c4c0281SGerrit Uitslag $this->doc .= "<ol$class>".DOKU_LF; 4640cecf9d5Sandi } 4650cecf9d5Sandi 4663dd5c225SAndreas Gohr /** 4673dd5c225SAndreas Gohr * Close an ordered list 4683dd5c225SAndreas Gohr */ 4690cecf9d5Sandi function listo_close() { 470a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4710cecf9d5Sandi } 4720cecf9d5Sandi 4733dd5c225SAndreas Gohr /** 4743dd5c225SAndreas Gohr * Open a list item 4753dd5c225SAndreas Gohr * 4763dd5c225SAndreas Gohr * @param int $level the nesting level 477e3a24861SChristopher Smith * @param bool $node true when a node; false when a leaf 4783dd5c225SAndreas Gohr */ 479e3a24861SChristopher Smith function listitem_open($level, $node=false) { 480e3a24861SChristopher Smith $branching = $node ? ' node' : ''; 481e3a24861SChristopher Smith $this->doc .= '<li class="level'.$level.$branching.'">'; 4820cecf9d5Sandi } 4830cecf9d5Sandi 4843dd5c225SAndreas Gohr /** 4853dd5c225SAndreas Gohr * Close a list item 4863dd5c225SAndreas Gohr */ 4870cecf9d5Sandi function listitem_close() { 488a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 4890cecf9d5Sandi } 4900cecf9d5Sandi 4913dd5c225SAndreas Gohr /** 4923dd5c225SAndreas Gohr * Start the content of a list item 4933dd5c225SAndreas Gohr */ 4940cecf9d5Sandi function listcontent_open() { 49590db23d7Schris $this->doc .= '<div class="li">'; 4960cecf9d5Sandi } 4970cecf9d5Sandi 4983dd5c225SAndreas Gohr /** 4993dd5c225SAndreas Gohr * Stop the content of a list item 5003dd5c225SAndreas Gohr */ 5010cecf9d5Sandi function listcontent_close() { 50259869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 5030cecf9d5Sandi } 5040cecf9d5Sandi 5053dd5c225SAndreas Gohr /** 5063dd5c225SAndreas Gohr * Output unformatted $text 5073dd5c225SAndreas Gohr * 5083dd5c225SAndreas Gohr * Defaults to $this->cdata() 5093dd5c225SAndreas Gohr * 5103dd5c225SAndreas Gohr * @param string $text 5113dd5c225SAndreas Gohr */ 5120cecf9d5Sandi function unformatted($text) { 513a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 5140cecf9d5Sandi } 5150cecf9d5Sandi 5160cecf9d5Sandi /** 5173fd0b676Sandi * Execute PHP code if allowed 5183fd0b676Sandi * 519d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5205d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5215d568b99SChris Smith * 5223fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5230cecf9d5Sandi */ 5245d568b99SChris Smith function php($text, $wrapper = 'code') { 52535a56260SChris Smith global $conf; 52635a56260SChris Smith 527d86d5af0SChris Smith if($conf['phpok']) { 528bad0b545Sandi ob_start(); 5294de671bcSandi eval($text); 5303fd0b676Sandi $this->doc .= ob_get_contents(); 531bad0b545Sandi ob_end_clean(); 532d86d5af0SChris Smith } else { 5335d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 534d86d5af0SChris Smith } 5350cecf9d5Sandi } 5360cecf9d5Sandi 5373dd5c225SAndreas Gohr /** 5383dd5c225SAndreas Gohr * Output block level PHP code 5393dd5c225SAndreas Gohr * 5403dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5413dd5c225SAndreas Gohr * to $doc 5423dd5c225SAndreas Gohr * 5433dd5c225SAndreas Gohr * @param string $text The PHP code 5443dd5c225SAndreas Gohr */ 54507f89c3cSAnika Henke function phpblock($text) { 5465d568b99SChris Smith $this->php($text, 'pre'); 54707f89c3cSAnika Henke } 54807f89c3cSAnika Henke 5490cecf9d5Sandi /** 5503fd0b676Sandi * Insert HTML if allowed 5513fd0b676Sandi * 552d9764001SMichael Hamann * @param string $text html text 5535d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5545d568b99SChris Smith * 5553fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5560cecf9d5Sandi */ 5575d568b99SChris Smith function html($text, $wrapper = 'code') { 55835a56260SChris Smith global $conf; 55935a56260SChris Smith 560d86d5af0SChris Smith if($conf['htmlok']) { 561a2d649c4Sandi $this->doc .= $text; 562d86d5af0SChris Smith } else { 5635d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 564d86d5af0SChris Smith } 5654de671bcSandi } 5660cecf9d5Sandi 5673dd5c225SAndreas Gohr /** 5683dd5c225SAndreas Gohr * Output raw block-level HTML 5693dd5c225SAndreas Gohr * 5703dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 5713dd5c225SAndreas Gohr * 5723dd5c225SAndreas Gohr * @param string $text The HTML 5733dd5c225SAndreas Gohr */ 57407f89c3cSAnika Henke function htmlblock($text) { 5755d568b99SChris Smith $this->html($text, 'pre'); 57607f89c3cSAnika Henke } 57707f89c3cSAnika Henke 5783dd5c225SAndreas Gohr /** 5793dd5c225SAndreas Gohr * Start a block quote 5803dd5c225SAndreas Gohr */ 5810cecf9d5Sandi function quote_open() { 58296331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 5830cecf9d5Sandi } 5840cecf9d5Sandi 5853dd5c225SAndreas Gohr /** 5863dd5c225SAndreas Gohr * Stop a block quote 5873dd5c225SAndreas Gohr */ 5880cecf9d5Sandi function quote_close() { 58996331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 5900cecf9d5Sandi } 5910cecf9d5Sandi 5923dd5c225SAndreas Gohr /** 5933dd5c225SAndreas Gohr * Output preformatted text 5943dd5c225SAndreas Gohr * 5953dd5c225SAndreas Gohr * @param string $text 5963dd5c225SAndreas Gohr */ 5973d491f75SAndreas Gohr function preformatted($text) { 598c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 5993d491f75SAndreas Gohr } 6003d491f75SAndreas Gohr 6013dd5c225SAndreas Gohr /** 6023dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 6033dd5c225SAndreas Gohr * 6043dd5c225SAndreas Gohr * @param string $text text to show 6053dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6063dd5c225SAndreas Gohr * @param string $filename file path label 6073dd5c225SAndreas Gohr */ 6083d491f75SAndreas Gohr function file($text, $language = null, $filename = null) { 6093d491f75SAndreas Gohr $this->_highlight('file', $text, $language, $filename); 6103d491f75SAndreas Gohr } 6113d491f75SAndreas Gohr 6123dd5c225SAndreas Gohr /** 6133dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 6143dd5c225SAndreas Gohr * 6153dd5c225SAndreas Gohr * @param string $text text to show 6163dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6173dd5c225SAndreas Gohr * @param string $filename file path label 6183dd5c225SAndreas Gohr */ 6193d491f75SAndreas Gohr function code($text, $language = null, $filename = null) { 6203d491f75SAndreas Gohr $this->_highlight('code', $text, $language, $filename); 6213d491f75SAndreas Gohr } 6223d491f75SAndreas Gohr 6230cecf9d5Sandi /** 6243d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6253fd0b676Sandi * 6263fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6273dd5c225SAndreas Gohr * @param string $type code|file 6283dd5c225SAndreas Gohr * @param string $text text to show 6293dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6303dd5c225SAndreas Gohr * @param string $filename file path label 6310cecf9d5Sandi */ 6323d491f75SAndreas Gohr function _highlight($type, $text, $language = null, $filename = null) { 6333d491f75SAndreas Gohr global $ID; 6343d491f75SAndreas Gohr global $lang; 6353d491f75SAndreas Gohr 63656bd9509SPhy $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language); 63756bd9509SPhy 6383d491f75SAndreas Gohr if($filename) { 639190c56e8SAndreas Gohr // add icon 64027bf7924STom N Harris list($ext) = mimetype($filename, false); 641190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 642190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 643190c56e8SAndreas Gohr 6443d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 645190c56e8SAndreas Gohr $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6463d491f75SAndreas Gohr $this->doc .= hsc($filename); 6473d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6483d491f75SAndreas Gohr } 6490cecf9d5Sandi 650d43aac1cSGina Haeussge if($text{0} == "\n") { 651d43aac1cSGina Haeussge $text = substr($text, 1); 652d43aac1cSGina Haeussge } 653d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 654d43aac1cSGina Haeussge $text = substr($text, 0, -1); 655d43aac1cSGina Haeussge } 656d43aac1cSGina Haeussge 657a056e285SPhy if(empty($language)) { // empty is faster than is_null and can prevent '' string 6583d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6590cecf9d5Sandi } else { 6603d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6613d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6623d491f75SAndreas Gohr 6633d491f75SAndreas Gohr $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '').'</pre>'.DOKU_LF; 6640cecf9d5Sandi } 6653d491f75SAndreas Gohr 6663d491f75SAndreas Gohr if($filename) { 6673d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 6683d491f75SAndreas Gohr } 6693d491f75SAndreas Gohr 6703d491f75SAndreas Gohr $this->_codeblock++; 6710cecf9d5Sandi } 6720cecf9d5Sandi 6733dd5c225SAndreas Gohr /** 6743dd5c225SAndreas Gohr * Format an acronym 6753dd5c225SAndreas Gohr * 6763dd5c225SAndreas Gohr * Uses $this->acronyms 6773dd5c225SAndreas Gohr * 6783dd5c225SAndreas Gohr * @param string $acronym 6793dd5c225SAndreas Gohr */ 6800cecf9d5Sandi function acronym($acronym) { 6810cecf9d5Sandi 6820cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 6830cecf9d5Sandi 684433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 6850cecf9d5Sandi 686940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 687940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 6880cecf9d5Sandi 6890cecf9d5Sandi } else { 690a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 6910cecf9d5Sandi } 6920cecf9d5Sandi } 6930cecf9d5Sandi 6943dd5c225SAndreas Gohr /** 6953dd5c225SAndreas Gohr * Format a smiley 6963dd5c225SAndreas Gohr * 6973dd5c225SAndreas Gohr * Uses $this->smiley 6983dd5c225SAndreas Gohr * 6993dd5c225SAndreas Gohr * @param string $smiley 7003dd5c225SAndreas Gohr */ 7010cecf9d5Sandi function smiley($smiley) { 7020cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 703f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 7048e38227fSAnika Henke '" class="icon" alt="'. 705433bef32Sandi $this->_xmlEntities($smiley).'" />'; 7060cecf9d5Sandi } else { 707a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 7080cecf9d5Sandi } 7090cecf9d5Sandi } 7100cecf9d5Sandi 7113dd5c225SAndreas Gohr /** 7123dd5c225SAndreas Gohr * Format an entity 7133dd5c225SAndreas Gohr * 7143dd5c225SAndreas Gohr * Entities are basically small text replacements 7153dd5c225SAndreas Gohr * 7163dd5c225SAndreas Gohr * Uses $this->entities 7173dd5c225SAndreas Gohr * 7183dd5c225SAndreas Gohr * @param string $entity 7194de671bcSandi */ 7200cecf9d5Sandi function entity($entity) { 7210cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 722a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7230cecf9d5Sandi } else { 724a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7250cecf9d5Sandi } 7260cecf9d5Sandi } 7270cecf9d5Sandi 7283dd5c225SAndreas Gohr /** 7293dd5c225SAndreas Gohr * Typographically format a multiply sign 7303dd5c225SAndreas Gohr * 7313dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7323dd5c225SAndreas Gohr * 7333dd5c225SAndreas Gohr * @param string|int $x first value 7343dd5c225SAndreas Gohr * @param string|int $y second value 7353dd5c225SAndreas Gohr */ 7360cecf9d5Sandi function multiplyentity($x, $y) { 737a2d649c4Sandi $this->doc .= "$x×$y"; 7380cecf9d5Sandi } 7390cecf9d5Sandi 7403dd5c225SAndreas Gohr /** 7413dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7423dd5c225SAndreas Gohr */ 7430cecf9d5Sandi function singlequoteopening() { 74471b40da2SAnika Henke global $lang; 74571b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7460cecf9d5Sandi } 7470cecf9d5Sandi 7483dd5c225SAndreas Gohr /** 7493dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7503dd5c225SAndreas Gohr */ 7510cecf9d5Sandi function singlequoteclosing() { 75271b40da2SAnika Henke global $lang; 75371b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7540cecf9d5Sandi } 7550cecf9d5Sandi 7563dd5c225SAndreas Gohr /** 7573dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7583dd5c225SAndreas Gohr */ 75957d757d1SAndreas Gohr function apostrophe() { 76057d757d1SAndreas Gohr global $lang; 761a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 76257d757d1SAndreas Gohr } 76357d757d1SAndreas Gohr 7643dd5c225SAndreas Gohr /** 7653dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 7663dd5c225SAndreas Gohr */ 7670cecf9d5Sandi function doublequoteopening() { 76871b40da2SAnika Henke global $lang; 76971b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 7700cecf9d5Sandi } 7710cecf9d5Sandi 7723dd5c225SAndreas Gohr /** 7733dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 7743dd5c225SAndreas Gohr */ 7750cecf9d5Sandi function doublequoteclosing() { 77671b40da2SAnika Henke global $lang; 77771b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 7780cecf9d5Sandi } 7790cecf9d5Sandi 7800cecf9d5Sandi /** 7813dd5c225SAndreas Gohr * Render a CamelCase link 7823dd5c225SAndreas Gohr * 7833dd5c225SAndreas Gohr * @param string $link The link name 784122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 7850c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 7860c4c0281SGerrit Uitslag * 7873dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 7880cecf9d5Sandi */ 789122f2d46SAndreas Böhler function camelcaselink($link, $returnonly = false) { 790122f2d46SAndreas Böhler if($returnonly) { 791122f2d46SAndreas Böhler return $this->internallink($link, $link, null, true); 792122f2d46SAndreas Böhler } else { 79311d0aa47Sandi $this->internallink($link, $link); 7940cecf9d5Sandi } 795122f2d46SAndreas Böhler } 7960cecf9d5Sandi 7973dd5c225SAndreas Gohr /** 7983dd5c225SAndreas Gohr * Render a page local link 7993dd5c225SAndreas Gohr * 8003dd5c225SAndreas Gohr * @param string $hash hash link identifier 8013dd5c225SAndreas Gohr * @param string $name name for the link 802122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8030c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8043dd5c225SAndreas Gohr */ 805122f2d46SAndreas Böhler function locallink($hash, $name = null, $returnonly = false) { 8060b7c14c2Sandi global $ID; 8070b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 8080b7c14c2Sandi $hash = $this->_headerToLink($hash); 809e260f93bSAnika Henke $title = $ID.' ↵'; 810122f2d46SAndreas Böhler 811122f2d46SAndreas Böhler $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 812122f2d46SAndreas Böhler $doc .= $name; 813122f2d46SAndreas Böhler $doc .= '</a>'; 814122f2d46SAndreas Böhler 815122f2d46SAndreas Böhler if($returnonly) { 816122f2d46SAndreas Böhler return $doc; 817122f2d46SAndreas Böhler } else { 818122f2d46SAndreas Böhler $this->doc .= $doc; 819122f2d46SAndreas Böhler } 8200b7c14c2Sandi } 8210b7c14c2Sandi 822cffcc403Sandi /** 8233fd0b676Sandi * Render an internal Wiki Link 8243fd0b676Sandi * 825fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 826cffcc403Sandi * elsewhere - no need to implement them in other renderers 8273fd0b676Sandi * 8283dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 829f23eef27SGerrit Uitslag * @param string $id pageid 830f23eef27SGerrit Uitslag * @param string|null $name link name 831f23eef27SGerrit Uitslag * @param string|null $search adds search url param 832f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 833f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 834f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 835cffcc403Sandi */ 8360ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 837ba11bd29Sandi global $conf; 83837e34a5eSandi global $ID; 839c4dda6afSAnika Henke global $INFO; 84044653a53SAdrian Lang 8413d5e07d9SAdrian Lang $params = ''; 8423d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8433d5e07d9SAdrian Lang if(count($parts) === 2) { 8443d5e07d9SAdrian Lang $id = $parts[0]; 8453d5e07d9SAdrian Lang $params = $parts[1]; 84644653a53SAdrian Lang } 84744653a53SAdrian Lang 848fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 849fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 850fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 851fda14ffcSIzidor Matušov // (some things could be lost) 852fda14ffcSIzidor Matušov if($id === '') { 853fda14ffcSIzidor Matušov $id = $ID; 854fda14ffcSIzidor Matušov } 855fda14ffcSIzidor Matušov 8560339c872Sjan // default name is based on $id as given 8570339c872Sjan $default = $this->_simpleTitle($id); 858ad32e47eSAndreas Gohr 8590339c872Sjan // now first resolve and clean up the $id 86090bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 861fda14ffcSIzidor Matušov 86259bc3b48SGerrit Uitslag $link = array(); 863fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8640e1c636eSandi if(!$isImage) { 8650e1c636eSandi if($exists) { 866ba11bd29Sandi $class = 'wikilink1'; 8670cecf9d5Sandi } else { 868ba11bd29Sandi $class = 'wikilink2'; 86944a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 8700cecf9d5Sandi } 8710cecf9d5Sandi } else { 872ba11bd29Sandi $class = 'media'; 8730cecf9d5Sandi } 8740cecf9d5Sandi 875a1685bedSandi //keep hash anchor 8766d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 877943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 878a1685bedSandi 879ba11bd29Sandi //prepare for formating 880ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 881ba11bd29Sandi $link['style'] = ''; 882ba11bd29Sandi $link['pre'] = ''; 883ba11bd29Sandi $link['suf'] = ''; 88440eb54bbSjan // highlight link to current page 885c4dda6afSAnika Henke if($id == $INFO['id']) { 88692795d04Sandi $link['pre'] = '<span class="curid">'; 88792795d04Sandi $link['suf'] = '</span>'; 88840eb54bbSjan } 8895e163278SAndreas Gohr $link['more'] = ''; 890ba11bd29Sandi $link['class'] = $class; 8915c2eed9aSlisps if($this->date_at) { 8925c2eed9aSlisps $params['at'] = $this->date_at; 8935c2eed9aSlisps } 89444653a53SAdrian Lang $link['url'] = wl($id, $params); 895ba11bd29Sandi $link['name'] = $name; 896ba11bd29Sandi $link['title'] = $id; 897723d78dbSandi //add search string 898723d78dbSandi if($search) { 899546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 900546d3a99SAndreas Gohr if(is_array($search)) { 901546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 902546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 903546d3a99SAndreas Gohr } else { 904546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 905546d3a99SAndreas Gohr } 906723d78dbSandi } 907723d78dbSandi 908a1685bedSandi //keep hash 909a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 910a1685bedSandi 911ba11bd29Sandi //output formatted 912cffcc403Sandi if($returnonly) { 913cffcc403Sandi return $this->_formatLink($link); 914cffcc403Sandi } else { 915a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9160cecf9d5Sandi } 917cffcc403Sandi } 9180cecf9d5Sandi 9193dd5c225SAndreas Gohr /** 9203dd5c225SAndreas Gohr * Render an external link 9213dd5c225SAndreas Gohr * 9223dd5c225SAndreas Gohr * @param string $url full URL with scheme 9233dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 924122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 9250c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9263dd5c225SAndreas Gohr */ 927122f2d46SAndreas Böhler function externallink($url, $name = null, $returnonly = false) { 928b625487dSandi global $conf; 9290cecf9d5Sandi 930433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 9316f0c5dbfSandi 932b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 933b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 934b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 935b52b1596SAndreas Gohr $scheme = strtolower($scheme); 936b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 937b52b1596SAndreas Gohr 938b52b1596SAndreas Gohr // is there still an URL? 939b52b1596SAndreas Gohr if(!$url) { 94049cef4fdSAndreas Böhler if($returnonly) { 94149cef4fdSAndreas Böhler return $name; 94249cef4fdSAndreas Böhler } else { 943b52b1596SAndreas Gohr $this->doc .= $name; 94449cef4fdSAndreas Böhler } 945b52b1596SAndreas Gohr return; 946b52b1596SAndreas Gohr } 947b52b1596SAndreas Gohr 948b52b1596SAndreas Gohr // set class 9490cecf9d5Sandi if(!$isImage) { 950b625487dSandi $class = 'urlextern'; 9510cecf9d5Sandi } else { 952b625487dSandi $class = 'media'; 9530cecf9d5Sandi } 9540cecf9d5Sandi 955b625487dSandi //prepare for formating 95659bc3b48SGerrit Uitslag $link = array(); 957b625487dSandi $link['target'] = $conf['target']['extern']; 958b625487dSandi $link['style'] = ''; 959b625487dSandi $link['pre'] = ''; 960b625487dSandi $link['suf'] = ''; 9615e163278SAndreas Gohr $link['more'] = ''; 962b625487dSandi $link['class'] = $class; 963b625487dSandi $link['url'] = $url; 964914045f3SAndreas Gohr $link['rel'] = ''; 965e1c10e4dSchris 966b625487dSandi $link['name'] = $name; 967433bef32Sandi $link['title'] = $this->_xmlEntities($url); 968914045f3SAndreas Gohr if($conf['relnofollow']) $link['rel'] .= ' nofollow'; 969914045f3SAndreas Gohr if($conf['target']['extern']) $link['rel'] .= ' noopener'; 9700cecf9d5Sandi 971b625487dSandi //output formatted 972122f2d46SAndreas Böhler if($returnonly) { 973122f2d46SAndreas Böhler return $this->_formatLink($link); 974122f2d46SAndreas Böhler } else { 975a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9760cecf9d5Sandi } 977122f2d46SAndreas Böhler } 9780cecf9d5Sandi 9790cecf9d5Sandi /** 9803dd5c225SAndreas Gohr * Render an interwiki link 9813dd5c225SAndreas Gohr * 9823dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 9833dd5c225SAndreas Gohr * 9843dd5c225SAndreas Gohr * @param string $match original link - probably not much use 9853dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 9863dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 9873dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 988122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 9890c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9900cecf9d5Sandi */ 991122f2d46SAndreas Böhler function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) { 992b625487dSandi global $conf; 9930cecf9d5Sandi 99497a3e4e3Sandi $link = array(); 99597a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 99697a3e4e3Sandi $link['pre'] = ''; 99797a3e4e3Sandi $link['suf'] = ''; 9985e163278SAndreas Gohr $link['more'] = ''; 999433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 1000914045f3SAndreas Gohr $link['rel'] = ''; 10010cecf9d5Sandi 100297a3e4e3Sandi //get interwiki URL 10036496c33fSGerrit Uitslag $exists = null; 10046496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 10050cecf9d5Sandi 100697a3e4e3Sandi if(!$isImage) { 10079d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 10089d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 10091c2d1019SAndreas Gohr } else { 10101c2d1019SAndreas Gohr $link['class'] = 'media'; 101197a3e4e3Sandi } 10120cecf9d5Sandi 101397a3e4e3Sandi //do we stay at the same server? Use local target 10142345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 101597a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 101697a3e4e3Sandi } 10176496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 10186496c33fSGerrit Uitslag if($exists) { 10196496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 10206496c33fSGerrit Uitslag } else { 10216496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 1022914045f3SAndreas Gohr $link['rel'] .= ' nofollow'; 10236496c33fSGerrit Uitslag } 10246496c33fSGerrit Uitslag } 1025914045f3SAndreas Gohr if($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 10260cecf9d5Sandi 102797a3e4e3Sandi $link['url'] = $url; 102897a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 102997a3e4e3Sandi 103097a3e4e3Sandi //output formatted 1031122f2d46SAndreas Böhler if($returnonly) { 1032122f2d46SAndreas Böhler return $this->_formatLink($link); 1033122f2d46SAndreas Böhler } else { 1034a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10350cecf9d5Sandi } 1036122f2d46SAndreas Böhler } 10370cecf9d5Sandi 10380cecf9d5Sandi /** 10393dd5c225SAndreas Gohr * Link to windows share 10403dd5c225SAndreas Gohr * 10413dd5c225SAndreas Gohr * @param string $url the link 10423dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1043122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10440c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10450cecf9d5Sandi */ 1046122f2d46SAndreas Böhler function windowssharelink($url, $name = null, $returnonly = false) { 10471d47afe1Sandi global $conf; 10483dd5c225SAndreas Gohr 10491d47afe1Sandi //simple setup 105059bc3b48SGerrit Uitslag $link = array(); 10511d47afe1Sandi $link['target'] = $conf['target']['windows']; 10521d47afe1Sandi $link['pre'] = ''; 10531d47afe1Sandi $link['suf'] = ''; 10541d47afe1Sandi $link['style'] = ''; 10550cecf9d5Sandi 1056433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 10570cecf9d5Sandi if(!$isImage) { 10581d47afe1Sandi $link['class'] = 'windows'; 10590cecf9d5Sandi } else { 10601d47afe1Sandi $link['class'] = 'media'; 10610cecf9d5Sandi } 10620cecf9d5Sandi 1063433bef32Sandi $link['title'] = $this->_xmlEntities($url); 10641d47afe1Sandi $url = str_replace('\\', '/', $url); 10651d47afe1Sandi $url = 'file:///'.$url; 10661d47afe1Sandi $link['url'] = $url; 10670cecf9d5Sandi 10681d47afe1Sandi //output formatted 1069122f2d46SAndreas Böhler if($returnonly) { 1070122f2d46SAndreas Böhler return $this->_formatLink($link); 1071122f2d46SAndreas Böhler } else { 1072a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10730cecf9d5Sandi } 1074122f2d46SAndreas Böhler } 10750cecf9d5Sandi 10763dd5c225SAndreas Gohr /** 10773dd5c225SAndreas Gohr * Render a linked E-Mail Address 10783dd5c225SAndreas Gohr * 10793dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 10803dd5c225SAndreas Gohr * 10813dd5c225SAndreas Gohr * @param string $address Email-Address 10823dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1083122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10840c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10853dd5c225SAndreas Gohr */ 1086122f2d46SAndreas Böhler function emaillink($address, $name = null, $returnonly = false) { 108771352defSandi global $conf; 108871352defSandi //simple setup 108971352defSandi $link = array(); 109071352defSandi $link['target'] = ''; 109171352defSandi $link['pre'] = ''; 109271352defSandi $link['suf'] = ''; 109371352defSandi $link['style'] = ''; 109471352defSandi $link['more'] = ''; 10950cecf9d5Sandi 1096c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 10970cecf9d5Sandi if(!$isImage) { 1098be96545cSAnika Henke $link['class'] = 'mail'; 10990cecf9d5Sandi } else { 1100be96545cSAnika Henke $link['class'] = 'media'; 11010cecf9d5Sandi } 11020cecf9d5Sandi 110307738714SAndreas Gohr $address = $this->_xmlEntities($address); 110400a7b5adSEsther Brunner $address = obfuscate($address); 110500a7b5adSEsther Brunner $title = $address; 11068c128049SAndreas Gohr 110771352defSandi if(empty($name)) { 110800a7b5adSEsther Brunner $name = $address; 110971352defSandi } 11100cecf9d5Sandi 1111776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1112776b36ecSAndreas Gohr 1113776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 111471352defSandi $link['name'] = $name; 111571352defSandi $link['title'] = $title; 11160cecf9d5Sandi 111771352defSandi //output formatted 1118122f2d46SAndreas Böhler if($returnonly) { 1119122f2d46SAndreas Böhler return $this->_formatLink($link); 1120122f2d46SAndreas Böhler } else { 1121a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11220cecf9d5Sandi } 1123122f2d46SAndreas Böhler } 11240cecf9d5Sandi 11253dd5c225SAndreas Gohr /** 11263dd5c225SAndreas Gohr * Render an internal media file 11273dd5c225SAndreas Gohr * 11283dd5c225SAndreas Gohr * @param string $src media ID 11293dd5c225SAndreas Gohr * @param string $title descriptive text 11303dd5c225SAndreas Gohr * @param string $align left|center|right 11313dd5c225SAndreas Gohr * @param int $width width of media in pixel 11323dd5c225SAndreas Gohr * @param int $height height of media in pixel 11333dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11343dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 11353dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 11360c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11373dd5c225SAndreas Gohr */ 11380ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 11393dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 114037e34a5eSandi global $ID; 11418f34cf3dSMichael Große if (strpos($src, '#') !== false) { 114291df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 11438f34cf3dSMichael Große } 1144cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 11450cecf9d5Sandi 1146d98d4540SBen Coburn $noLink = false; 11478acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1148b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 11493685f775Sandi 11503dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1151b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 115252dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct')); 1153f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11542a2a2ba2SAnika Henke // don't link movies 115544881bd0Shenning.noren $noLink = true; 115655efc227SAndreas Gohr } else { 11572ca14335SEsther Brunner // add file icons 11589d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 11599d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 116052dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true); 116191328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 116255efc227SAndreas Gohr } 11633685f775Sandi 11648f34cf3dSMichael Große if (!empty($hash)) $link['url'] .= '#'.$hash; 116591df343aSAndreas Gohr 11666fe20453SGina Haeussge //markup non existing files 11674a24b459SKate Arzamastseva if(!$exists) { 11686fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 11694a24b459SKate Arzamastseva } 11706fe20453SGina Haeussge 11713685f775Sandi //output formatted 1172f50634f0SAnika Henke if($return) { 1173f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1174f50634f0SAnika Henke else return $this->_formatLink($link); 1175f50634f0SAnika Henke } else { 1176dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 11772ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 11780cecf9d5Sandi } 1179f50634f0SAnika Henke } 11800cecf9d5Sandi 11813dd5c225SAndreas Gohr /** 11823dd5c225SAndreas Gohr * Render an external media file 11833dd5c225SAndreas Gohr * 11843dd5c225SAndreas Gohr * @param string $src full media URL 11853dd5c225SAndreas Gohr * @param string $title descriptive text 11863dd5c225SAndreas Gohr * @param string $align left|center|right 11873dd5c225SAndreas Gohr * @param int $width width of media in pixel 11883dd5c225SAndreas Gohr * @param int $height height of media in pixel 11893dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11903dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1191410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 11920c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11933dd5c225SAndreas Gohr */ 11940ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 1195410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 11966efc45a2SDmitry Katsubo if(link_isinterwiki($src)){ 11976efc45a2SDmitry Katsubo list($shortcut, $reference) = explode('>', $src, 2); 11986efc45a2SDmitry Katsubo $exists = null; 11996efc45a2SDmitry Katsubo $src = $this->_resolveInterWiki($shortcut, $reference, $exists); 12006efc45a2SDmitry Katsubo } 120191df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1202d98d4540SBen Coburn $noLink = false; 12038acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1204b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1205b739ff0fSPierre Spring 1206b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 12073685f775Sandi 12083dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1209b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 12102ca14335SEsther Brunner // link only jpeg images 121144881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1212f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 12132a2a2ba2SAnika Henke // don't link movies 121444881bd0Shenning.noren $noLink = true; 12152ca14335SEsther Brunner } else { 12162ca14335SEsther Brunner // add file icons 121727bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 121827bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 12192ca14335SEsther Brunner } 12202ca14335SEsther Brunner 122191df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 122291df343aSAndreas Gohr 12233685f775Sandi //output formatted 1224410ee62aSAnika Henke if($return) { 1225410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1226410ee62aSAnika Henke else return $this->_formatLink($link); 1227410ee62aSAnika Henke } else { 1228dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12292ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12300cecf9d5Sandi } 1231410ee62aSAnika Henke } 12320cecf9d5Sandi 12334826ab45Sandi /** 12343db95becSAndreas Gohr * Renders an RSS feed 1235b625487dSandi * 12360c4c0281SGerrit Uitslag * @param string $url URL of the feed 12370c4c0281SGerrit Uitslag * @param array $params Finetuning of the output 12380c4c0281SGerrit Uitslag * 1239b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1240b625487dSandi */ 12413db95becSAndreas Gohr function rss($url, $params) { 1242b625487dSandi global $lang; 12433db95becSAndreas Gohr global $conf; 12443db95becSAndreas Gohr 12453db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 12463db95becSAndreas Gohr $feed = new FeedParser(); 124700077af8SAndreas Gohr $feed->set_feed_url($url); 1248b625487dSandi 1249b625487dSandi //disable warning while fetching 12503dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 12513dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 12523dd5c225SAndreas Gohr } 12533db95becSAndreas Gohr $rc = $feed->init(); 12543dd5c225SAndreas Gohr if(isset($elvl)) { 12553dd5c225SAndreas Gohr error_reporting($elvl); 12563dd5c225SAndreas Gohr } 1257b625487dSandi 125838c6f603SRobin H. Johnson if($params['nosort']) $feed->enable_order_by_date(false); 125938c6f603SRobin H. Johnson 12603db95becSAndreas Gohr //decide on start and end 12613db95becSAndreas Gohr if($params['reverse']) { 12623db95becSAndreas Gohr $mod = -1; 12633db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 12643db95becSAndreas Gohr $end = $start - ($params['max']); 1265b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 12663db95becSAndreas Gohr } else { 12673db95becSAndreas Gohr $mod = 1; 12683db95becSAndreas Gohr $start = 0; 12693db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1270d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 12713db95becSAndreas Gohr } 12723db95becSAndreas Gohr 1273a2d649c4Sandi $this->doc .= '<ul class="rss">'; 12743db95becSAndreas Gohr if($rc) { 12753db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 12761bde1582SAndreas Gohr $item = $feed->get_item($x); 12773db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1278d2ea3363SAndreas Gohr // support feeds without links 1279d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1280d2ea3363SAndreas Gohr if($lnkurl) { 1281793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1282793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 12833dd5c225SAndreas Gohr $this->externallink( 12843dd5c225SAndreas Gohr $item->get_permalink(), 12853dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 12863dd5c225SAndreas Gohr ); 1287d2ea3363SAndreas Gohr } else { 1288d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1289d2ea3363SAndreas Gohr } 12903db95becSAndreas Gohr if($params['author']) { 12911bde1582SAndreas Gohr $author = $item->get_author(0); 12921bde1582SAndreas Gohr if($author) { 12931bde1582SAndreas Gohr $name = $author->get_name(); 12941bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 1295163c2842SPhy if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name); 12961bde1582SAndreas Gohr } 12973db95becSAndreas Gohr } 12983db95becSAndreas Gohr if($params['date']) { 12992e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 13003db95becSAndreas Gohr } 13011bde1582SAndreas Gohr if($params['details']) { 13023db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1303173dccb7STom N Harris if($conf['htmlok']) { 13041bde1582SAndreas Gohr $this->doc .= $item->get_description(); 13053db95becSAndreas Gohr } else { 13061bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 13073db95becSAndreas Gohr } 13083db95becSAndreas Gohr $this->doc .= '</div>'; 13093db95becSAndreas Gohr } 13103db95becSAndreas Gohr 13113db95becSAndreas Gohr $this->doc .= '</div></li>'; 1312b625487dSandi } 1313b625487dSandi } else { 13143db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1315a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1316b625487dSandi $this->externallink($url); 131745e147ccSAndreas Gohr if($conf['allowdebug']) { 131845e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 131945e147ccSAndreas Gohr } 13203db95becSAndreas Gohr $this->doc .= '</div></li>'; 1321b625487dSandi } 1322a2d649c4Sandi $this->doc .= '</ul>'; 1323b625487dSandi } 1324b625487dSandi 13253dd5c225SAndreas Gohr /** 13263dd5c225SAndreas Gohr * Start a table 13273dd5c225SAndreas Gohr * 13283dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 13293dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 13303dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13317d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 13323dd5c225SAndreas Gohr */ 13330c4c0281SGerrit Uitslag function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 1334b5742cedSPierre Spring // initialize the row counter used for classes 1335b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1336619736fdSAdrian Lang $class = 'table'; 13370c4c0281SGerrit Uitslag if($classes !== null) { 13382e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 13390c4c0281SGerrit Uitslag $class .= ' ' . $classes; 13400c4c0281SGerrit Uitslag } 1341619736fdSAdrian Lang if($pos !== null) { 1342*06917fceSMichael Große $hid = $this->_headerToLink($class, true); 1343*06917fceSMichael Große $class .= ' '.$this->startSectionEdit($pos, 'table', '', $hid); 1344619736fdSAdrian Lang } 1345619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1346619736fdSAdrian Lang DOKU_LF; 13470cecf9d5Sandi } 13480cecf9d5Sandi 13493dd5c225SAndreas Gohr /** 13503dd5c225SAndreas Gohr * Close a table 13513dd5c225SAndreas Gohr * 13523dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13533dd5c225SAndreas Gohr */ 1354619736fdSAdrian Lang function table_close($pos = null) { 1355a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1356619736fdSAdrian Lang if($pos !== null) { 135790df9a4dSAdrian Lang $this->finishSectionEdit($pos); 13580cecf9d5Sandi } 1359619736fdSAdrian Lang } 13600cecf9d5Sandi 13613dd5c225SAndreas Gohr /** 13623dd5c225SAndreas Gohr * Open a table header 13633dd5c225SAndreas Gohr */ 1364f05a1cc5SGerrit Uitslag function tablethead_open() { 1365f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1366f05a1cc5SGerrit Uitslag } 1367f05a1cc5SGerrit Uitslag 13683dd5c225SAndreas Gohr /** 13693dd5c225SAndreas Gohr * Close a table header 13703dd5c225SAndreas Gohr */ 1371f05a1cc5SGerrit Uitslag function tablethead_close() { 1372f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1373f05a1cc5SGerrit Uitslag } 1374f05a1cc5SGerrit Uitslag 13753dd5c225SAndreas Gohr /** 13765a93f869SAnika Henke * Open a table body 13775a93f869SAnika Henke */ 13785a93f869SAnika Henke function tabletbody_open() { 13795a93f869SAnika Henke $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF; 13805a93f869SAnika Henke } 13815a93f869SAnika Henke 13825a93f869SAnika Henke /** 13835a93f869SAnika Henke * Close a table body 13845a93f869SAnika Henke */ 13855a93f869SAnika Henke function tabletbody_close() { 13865a93f869SAnika Henke $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF; 13875a93f869SAnika Henke } 13885a93f869SAnika Henke 13895a93f869SAnika Henke /** 1390d2a99739SAndreas Gohr * Open a table footer 1391d2a99739SAndreas Gohr */ 1392d2a99739SAndreas Gohr function tabletfoot_open() { 139344f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF; 1394d2a99739SAndreas Gohr } 1395d2a99739SAndreas Gohr 1396d2a99739SAndreas Gohr /** 1397d2a99739SAndreas Gohr * Close a table footer 1398d2a99739SAndreas Gohr */ 1399d2a99739SAndreas Gohr function tabletfoot_close() { 140044f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF; 1401d2a99739SAndreas Gohr } 1402d2a99739SAndreas Gohr 1403d2a99739SAndreas Gohr /** 14043dd5c225SAndreas Gohr * Open a table row 14050c4c0281SGerrit Uitslag * 14067d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14073dd5c225SAndreas Gohr */ 14080c4c0281SGerrit Uitslag function tablerow_open($classes = null) { 1409b5742cedSPierre Spring // initialize the cell counter used for classes 1410b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1411b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 14120c4c0281SGerrit Uitslag if($classes !== null) { 14132e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14140c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14150c4c0281SGerrit Uitslag } 1416b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 14170cecf9d5Sandi } 14180cecf9d5Sandi 14193dd5c225SAndreas Gohr /** 14203dd5c225SAndreas Gohr * Close a table row 14213dd5c225SAndreas Gohr */ 14220cecf9d5Sandi function tablerow_close() { 1423a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 14240cecf9d5Sandi } 14250cecf9d5Sandi 14263dd5c225SAndreas Gohr /** 14273dd5c225SAndreas Gohr * Open a table header cell 14283dd5c225SAndreas Gohr * 14293dd5c225SAndreas Gohr * @param int $colspan 14303dd5c225SAndreas Gohr * @param string $align left|center|right 14313dd5c225SAndreas Gohr * @param int $rowspan 14327d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14333dd5c225SAndreas Gohr */ 14340c4c0281SGerrit Uitslag function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1435b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14360cecf9d5Sandi if(!is_null($align)) { 1437b5742cedSPierre Spring $class .= ' '.$align.'align'; 14380cecf9d5Sandi } 14390c4c0281SGerrit Uitslag if($classes !== null) { 14402e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14410c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14420c4c0281SGerrit Uitslag } 1443b5742cedSPierre Spring $class .= '"'; 1444b5742cedSPierre Spring $this->doc .= '<th '.$class; 14450cecf9d5Sandi if($colspan > 1) { 1446a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1447a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14480cecf9d5Sandi } 144925b97867Shakan.sandell if($rowspan > 1) { 145025b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 145125b97867Shakan.sandell } 1452a2d649c4Sandi $this->doc .= '>'; 14530cecf9d5Sandi } 14540cecf9d5Sandi 14553dd5c225SAndreas Gohr /** 14563dd5c225SAndreas Gohr * Close a table header cell 14573dd5c225SAndreas Gohr */ 14580cecf9d5Sandi function tableheader_close() { 1459a2d649c4Sandi $this->doc .= '</th>'; 14600cecf9d5Sandi } 14610cecf9d5Sandi 14623dd5c225SAndreas Gohr /** 14633dd5c225SAndreas Gohr * Open a table cell 14643dd5c225SAndreas Gohr * 14653dd5c225SAndreas Gohr * @param int $colspan 14663dd5c225SAndreas Gohr * @param string $align left|center|right 14673dd5c225SAndreas Gohr * @param int $rowspan 14687d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14693dd5c225SAndreas Gohr */ 14700c4c0281SGerrit Uitslag function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1471b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14720cecf9d5Sandi if(!is_null($align)) { 1473b5742cedSPierre Spring $class .= ' '.$align.'align'; 14740cecf9d5Sandi } 14750c4c0281SGerrit Uitslag if($classes !== null) { 14762e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14770c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14780c4c0281SGerrit Uitslag } 1479b5742cedSPierre Spring $class .= '"'; 1480b5742cedSPierre Spring $this->doc .= '<td '.$class; 14810cecf9d5Sandi if($colspan > 1) { 1482a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1483a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14840cecf9d5Sandi } 148525b97867Shakan.sandell if($rowspan > 1) { 148625b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 148725b97867Shakan.sandell } 1488a2d649c4Sandi $this->doc .= '>'; 14890cecf9d5Sandi } 14900cecf9d5Sandi 14913dd5c225SAndreas Gohr /** 14923dd5c225SAndreas Gohr * Close a table cell 14933dd5c225SAndreas Gohr */ 14940cecf9d5Sandi function tablecell_close() { 1495a2d649c4Sandi $this->doc .= '</td>'; 14960cecf9d5Sandi } 14970cecf9d5Sandi 1498cea664bdSLarsDW223 /** 1499cea664bdSLarsDW223 * Returns the current header level. 1500cea664bdSLarsDW223 * (required e.g. by the filelist plugin) 1501cea664bdSLarsDW223 * 1502cea664bdSLarsDW223 * @return int The current header level 1503cea664bdSLarsDW223 */ 1504cea664bdSLarsDW223 function getLastlevel() { 1505cea664bdSLarsDW223 return $this->lastlevel; 1506cea664bdSLarsDW223 } 1507cea664bdSLarsDW223 15083dd5c225SAndreas Gohr #region Utility functions 15090cecf9d5Sandi 1510ba11bd29Sandi /** 15113fd0b676Sandi * Build a link 15123fd0b676Sandi * 15133fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1514ba11bd29Sandi * 15150c4c0281SGerrit Uitslag * @param array $link attributes of a link 15160c4c0281SGerrit Uitslag * @return string 15170c4c0281SGerrit Uitslag * 1518ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1519ba11bd29Sandi */ 1520433bef32Sandi function _formatLink($link) { 1521ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1522ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1523ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1524ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1525ba11bd29Sandi } 1526ba11bd29Sandi //remove double encodings in titles 1527ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1528ba11bd29Sandi 1529453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1530453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1531453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1532453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1533453493f2SAndreas Gohr 1534ba11bd29Sandi $ret = ''; 1535ba11bd29Sandi $ret .= $link['pre']; 1536ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1537bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1538bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1539bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1540bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 1541914045f3SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"'; 1542bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1543ba11bd29Sandi $ret .= '>'; 1544ba11bd29Sandi $ret .= $link['name']; 1545ba11bd29Sandi $ret .= '</a>'; 1546ba11bd29Sandi $ret .= $link['suf']; 1547ba11bd29Sandi return $ret; 1548ba11bd29Sandi } 1549ba11bd29Sandi 1550ba11bd29Sandi /** 15513fd0b676Sandi * Renders internal and external media 15523fd0b676Sandi * 15533fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 15543dd5c225SAndreas Gohr * @param string $src media ID 15553dd5c225SAndreas Gohr * @param string $title descriptive text 15563dd5c225SAndreas Gohr * @param string $align left|center|right 15573dd5c225SAndreas Gohr * @param int $width width of media in pixel 15583dd5c225SAndreas Gohr * @param int $height height of media in pixel 15593dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 15603dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 15613dd5c225SAndreas Gohr * @return string 15623fd0b676Sandi */ 15630ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 15640ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 15653fd0b676Sandi 15663fd0b676Sandi $ret = ''; 15673fd0b676Sandi 15683dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 15693fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1570b739ff0fSPierre Spring // first get the $title 1571b739ff0fSPierre Spring if(!is_null($title)) { 1572b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1573b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1574b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1575b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 157667f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1577b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 15783dd5c225SAndreas Gohr if(!empty($cap)) { 1579b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1580b739ff0fSPierre Spring } 1581b739ff0fSPierre Spring } 1582b739ff0fSPierre Spring if(!$render) { 1583b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1584b739ff0fSPierre Spring // return the title of the picture 1585b739ff0fSPierre Spring if(!$title) { 1586b739ff0fSPierre Spring // just show the sourcename 15873009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1588b739ff0fSPierre Spring } 1589b739ff0fSPierre Spring return $title; 1590b739ff0fSPierre Spring } 15913fd0b676Sandi //add image tag 159252dc5eadSlisps $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"'; 15933fd0b676Sandi $ret .= ' class="media'.$align.'"'; 15943fd0b676Sandi 1595b739ff0fSPierre Spring if($title) { 1596b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1597b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 15983fd0b676Sandi } else { 15993fd0b676Sandi $ret .= ' alt=""'; 16003fd0b676Sandi } 16013fd0b676Sandi 16023fd0b676Sandi if(!is_null($width)) 16033fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 16043fd0b676Sandi 16053fd0b676Sandi if(!is_null($height)) 16063fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 16073fd0b676Sandi 16083fd0b676Sandi $ret .= ' />'; 16093fd0b676Sandi 161017954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 16112a2a2ba2SAnika Henke // first get the $title 161217954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 16132a2a2ba2SAnika Henke if(!$render) { 161417954bb5SAnika Henke // if the file is not supposed to be rendered 161517954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 161617954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 16172a2a2ba2SAnika Henke } 16182a2a2ba2SAnika Henke 16192a2a2ba2SAnika Henke $att = array(); 16202a2a2ba2SAnika Henke $att['class'] = "media$align"; 162117954bb5SAnika Henke if($title) { 162217954bb5SAnika Henke $att['title'] = $title; 162317954bb5SAnika Henke } 16242a2a2ba2SAnika Henke 162517954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 162617954bb5SAnika Henke //add video 162779e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1628b44a5dceSAnika Henke } 162917954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1630b44a5dceSAnika Henke //add audio 1631b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 163217954bb5SAnika Henke } 1633b44a5dceSAnika Henke 16343fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 16351c882ba8SAndreas Gohr if(!$render) { 16361c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 16371c882ba8SAndreas Gohr // return the title of the flash 16381c882ba8SAndreas Gohr if(!$title) { 16391c882ba8SAndreas Gohr // just show the sourcename 16403009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 16411c882ba8SAndreas Gohr } 164207bf32b2SAndreas Gohr return $this->_xmlEntities($title); 16431c882ba8SAndreas Gohr } 16441c882ba8SAndreas Gohr 164507bf32b2SAndreas Gohr $att = array(); 164607bf32b2SAndreas Gohr $att['class'] = "media$align"; 164707bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 164807bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 16493dd5c225SAndreas Gohr $ret .= html_flashobject( 16503dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 165107bf32b2SAndreas Gohr array('quality' => 'high'), 165207bf32b2SAndreas Gohr null, 165307bf32b2SAndreas Gohr $att, 16543dd5c225SAndreas Gohr $this->_xmlEntities($title) 16553dd5c225SAndreas Gohr ); 16560f428d7dSAndreas Gohr } elseif($title) { 16573fd0b676Sandi // well at least we have a title to display 16583fd0b676Sandi $ret .= $this->_xmlEntities($title); 16593fd0b676Sandi } else { 16605291ca3aSAndreas Gohr // just show the sourcename 16613009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 16623fd0b676Sandi } 16633fd0b676Sandi 16643fd0b676Sandi return $ret; 16653fd0b676Sandi } 16663fd0b676Sandi 16673dd5c225SAndreas Gohr /** 16683dd5c225SAndreas Gohr * Escape string for output 16693dd5c225SAndreas Gohr * 16703dd5c225SAndreas Gohr * @param $string 16713dd5c225SAndreas Gohr * @return string 16723dd5c225SAndreas Gohr */ 1673433bef32Sandi function _xmlEntities($string) { 1674de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 16750cecf9d5Sandi } 16760cecf9d5Sandi 16778a831f2bSAndreas Gohr /** 16788a831f2bSAndreas Gohr * Creates a linkid from a headline 1679c5a8fd96SAndreas Gohr * 16803dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1681c5a8fd96SAndreas Gohr * @param string $title The headline title 1682c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 16833dd5c225SAndreas Gohr * @return string 16848a831f2bSAndreas Gohr */ 1685c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1686c5a8fd96SAndreas Gohr if($create) { 16874ceab83fSAndreas Gohr return sectionID($title, $this->headers); 16884ceab83fSAndreas Gohr } else { 1689443d207bSAndreas Gohr $check = false; 1690443d207bSAndreas Gohr return sectionID($title, $check); 1691c5a8fd96SAndreas Gohr } 16920cecf9d5Sandi } 16930cecf9d5Sandi 1694af587fa8Sandi /** 16953fd0b676Sandi * Construct a title and handle images in titles 16963fd0b676Sandi * 16970b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 16983dd5c225SAndreas Gohr * @param string|array $title either string title or media array 16993dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 17003dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 17013dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 17023dd5c225SAndreas Gohr * @param string $linktype content|navigation 17033dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 17043fd0b676Sandi */ 17050ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 170644881bd0Shenning.noren $isImage = false; 170729657f9eSAndreas Gohr if(is_array($title)) { 170829657f9eSAndreas Gohr $isImage = true; 170929657f9eSAndreas Gohr return $this->_imageTitle($title); 171029657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1711fe9ec250SChris Smith if(useHeading($linktype) && $id) { 171267c15eceSMichael Hamann $heading = p_get_first_heading($id); 1713f515db7fSAndreas Gohr if(!blank($heading)) { 1714433bef32Sandi return $this->_xmlEntities($heading); 1715bb0a59d4Sjan } 1716bb0a59d4Sjan } 1717433bef32Sandi return $this->_xmlEntities($default); 171868c26e6dSMichael Klier } else { 171968c26e6dSMichael Klier return $this->_xmlEntities($title); 17200cecf9d5Sandi } 17210cecf9d5Sandi } 17220cecf9d5Sandi 17230cecf9d5Sandi /** 17243dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 17253fd0b676Sandi * 17263fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1727e0c26282SGerrit Uitslag * @param array $img 17283dd5c225SAndreas Gohr * @return string HTML img tag or similar 17290cecf9d5Sandi */ 1730433bef32Sandi function _imageTitle($img) { 1731d9baf1a7SKazutaka Miyasaka global $ID; 1732d9baf1a7SKazutaka Miyasaka 1733d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1734d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 17353dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1736d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1737cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1738d9baf1a7SKazutaka Miyasaka } 1739d9baf1a7SKazutaka Miyasaka 17403dd5c225SAndreas Gohr return $this->_media( 17413dd5c225SAndreas Gohr $img['src'], 17424826ab45Sandi $img['title'], 17434826ab45Sandi $img['align'], 17444826ab45Sandi $img['width'], 17454826ab45Sandi $img['height'], 17463dd5c225SAndreas Gohr $img['cache'] 17473dd5c225SAndreas Gohr ); 17480cecf9d5Sandi } 1749b739ff0fSPierre Spring 1750b739ff0fSPierre Spring /** 17513dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 17523dd5c225SAndreas Gohr * 17533dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1754b739ff0fSPierre Spring * 1755b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 17563dd5c225SAndreas Gohr * @param string $src media ID 17573dd5c225SAndreas Gohr * @param string $title descriptive text 17583dd5c225SAndreas Gohr * @param string $align left|center|right 17593dd5c225SAndreas Gohr * @param int $width width of media in pixel 17603dd5c225SAndreas Gohr * @param int $height height of media in pixel 17613dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 17623dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 17633dd5c225SAndreas Gohr * @return array associative array with link config 1764b739ff0fSPierre Spring */ 1765d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1766b739ff0fSPierre Spring global $conf; 1767b739ff0fSPierre Spring 1768b739ff0fSPierre Spring $link = array(); 1769b739ff0fSPierre Spring $link['class'] = 'media'; 1770b739ff0fSPierre Spring $link['style'] = ''; 1771b739ff0fSPierre Spring $link['pre'] = ''; 1772b739ff0fSPierre Spring $link['suf'] = ''; 1773b739ff0fSPierre Spring $link['more'] = ''; 1774b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1775bc3d2252SAndreas Gohr if($conf['target']['media']) $link['rel'] = 'noopener'; 1776b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1777b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1778b739ff0fSPierre Spring 1779b739ff0fSPierre Spring return $link; 1780b739ff0fSPierre Spring } 178191459163SAnika Henke 17822a2a2ba2SAnika Henke /** 17832a2a2ba2SAnika Henke * Embed video(s) in HTML 17842a2a2ba2SAnika Henke * 17852a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 17862a2a2ba2SAnika Henke * 17872a2a2ba2SAnika Henke * @param string $src - ID of video to embed 17882a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 17892a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 17902a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1791f50634f0SAnika Henke * @return string 17922a2a2ba2SAnika Henke */ 179379e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 17942a2a2ba2SAnika Henke // prepare width and height 17952a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 17962a2a2ba2SAnika Henke $atts['width'] = (int) $width; 17972a2a2ba2SAnika Henke $atts['height'] = (int) $height; 17982a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 17992a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 18002a2a2ba2SAnika Henke 1801410ee62aSAnika Henke $posterUrl = ''; 1802410ee62aSAnika Henke $files = array(); 1803410ee62aSAnika Henke $isExternal = media_isexternal($src); 1804410ee62aSAnika Henke 1805410ee62aSAnika Henke if ($isExternal) { 1806410ee62aSAnika Henke // take direct source for external files 1807702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1808410ee62aSAnika Henke $files[$srcMime] = $src; 1809410ee62aSAnika Henke } else { 18103d7a9e0aSAnika Henke // prepare alternative formats 18113d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1812410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 181359bc3b48SGerrit Uitslag $poster = media_alternativefiles($src, array('jpg', 'png')); 181499f943f6SAnika Henke if(!empty($poster)) { 18152d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 181699f943f6SAnika Henke } 1817410ee62aSAnika Henke } 18182a2a2ba2SAnika Henke 1819f50634f0SAnika Henke $out = ''; 182079e53fe5SAnika Henke // open video tag 1821f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 18223641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1823f50634f0SAnika Henke $out .= '>'.NL; 18243641199aSAnika Henke $fallback = ''; 182579e53fe5SAnika Henke 182679e53fe5SAnika Henke // output source for each alternative video format 1827410ee62aSAnika Henke foreach($files as $mime => $file) { 1828410ee62aSAnika Henke if ($isExternal) { 1829410ee62aSAnika Henke $url = $file; 1830410ee62aSAnika Henke $linkType = 'externalmedia'; 1831410ee62aSAnika Henke } else { 18322d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1833410ee62aSAnika Henke $linkType = 'internalmedia'; 1834410ee62aSAnika Henke } 183517954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 18363d7a9e0aSAnika Henke 1837f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 183879e53fe5SAnika Henke // alternative content (just a link to the file) 1839410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 18403d7a9e0aSAnika Henke } 18412a2a2ba2SAnika Henke 18422a2a2ba2SAnika Henke // finish 18433641199aSAnika Henke $out .= $fallback; 1844f50634f0SAnika Henke $out .= '</video>'.NL; 1845f50634f0SAnika Henke return $out; 18462a2a2ba2SAnika Henke } 18472a2a2ba2SAnika Henke 1848b44a5dceSAnika Henke /** 1849b44a5dceSAnika Henke * Embed audio in HTML 1850b44a5dceSAnika Henke * 1851b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1852b44a5dceSAnika Henke * 1853b44a5dceSAnika Henke * @param string $src - ID of audio to embed 18546d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1855f50634f0SAnika Henke * @return string 1856b44a5dceSAnika Henke */ 185759bc3b48SGerrit Uitslag function _audio($src, $atts = array()) { 1858702e97d3SAnika Henke $files = array(); 1859410ee62aSAnika Henke $isExternal = media_isexternal($src); 1860b44a5dceSAnika Henke 1861410ee62aSAnika Henke if ($isExternal) { 1862410ee62aSAnika Henke // take direct source for external files 1863702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1864410ee62aSAnika Henke $files[$srcMime] = $src; 1865410ee62aSAnika Henke } else { 1866b44a5dceSAnika Henke // prepare alternative formats 1867b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1868410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1869410ee62aSAnika Henke } 1870b44a5dceSAnika Henke 1871f50634f0SAnika Henke $out = ''; 1872b44a5dceSAnika Henke // open audio tag 1873f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 18743641199aSAnika Henke $fallback = ''; 1875b44a5dceSAnika Henke 1876b44a5dceSAnika Henke // output source for each alternative audio format 1877410ee62aSAnika Henke foreach($files as $mime => $file) { 1878410ee62aSAnika Henke if ($isExternal) { 1879410ee62aSAnika Henke $url = $file; 1880410ee62aSAnika Henke $linkType = 'externalmedia'; 1881410ee62aSAnika Henke } else { 18822d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1883410ee62aSAnika Henke $linkType = 'internalmedia'; 1884410ee62aSAnika Henke } 188517954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1886b44a5dceSAnika Henke 1887f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1888b44a5dceSAnika Henke // alternative content (just a link to the file) 1889410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1890b44a5dceSAnika Henke } 1891b44a5dceSAnika Henke 1892b44a5dceSAnika Henke // finish 18933641199aSAnika Henke $out .= $fallback; 1894f50634f0SAnika Henke $out .= '</audio>'.NL; 1895f50634f0SAnika Henke return $out; 1896b44a5dceSAnika Henke } 1897b44a5dceSAnika Henke 18985c2eed9aSlisps /** 189952dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 19005c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 19015c2eed9aSlisps * 19025c2eed9aSlisps * @author lisps 19035c2eed9aSlisps * @param string $media_id 19045c2eed9aSlisps * @access protected 19055c2eed9aSlisps * @return string revision ('' for current) 19065c2eed9aSlisps */ 190752dc5eadSlisps function _getLastMediaRevisionAt($media_id){ 190852dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 190978b874e6Slisps $pagelog = new MediaChangeLog($media_id); 191078b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 19115c2eed9aSlisps } 19125c2eed9aSlisps 19133dd5c225SAndreas Gohr #endregion 19140cecf9d5Sandi} 19150cecf9d5Sandi 1916e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1917