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 */ 693f9e3215SAdrian Lang public function startSectionEdit($start, $type, $title = null) { 70b04a190dSMichael Hamann $this->sectionedits[] = array(++$this->lastsecid, $start, $type, $title); 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 */ 813f9e3215SAdrian Lang public function finishSectionEdit($end = null) { 8290df9a4dSAdrian Lang list($id, $start, $type, $title) = 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 } 90d9e36cbeSAdrian Lang $this->doc .= "[$start-".(is_null($end) ? '' : $end).'] -->'; 9190df9a4dSAdrian Lang } 9290df9a4dSAdrian Lang 933dd5c225SAndreas Gohr /** 943dd5c225SAndreas Gohr * Returns the format produced by this renderer. 953dd5c225SAndreas Gohr * 963dd5c225SAndreas Gohr * @return string always 'xhtml' 973dd5c225SAndreas Gohr */ 985f70445dSAndreas Gohr function getFormat() { 995f70445dSAndreas Gohr return 'xhtml'; 1005f70445dSAndreas Gohr } 1015f70445dSAndreas Gohr 1023dd5c225SAndreas Gohr /** 1033dd5c225SAndreas Gohr * Initialize the document 1043dd5c225SAndreas Gohr */ 1050cecf9d5Sandi function document_start() { 106c5a8fd96SAndreas Gohr //reset some internals 107c5a8fd96SAndreas Gohr $this->toc = array(); 108c5a8fd96SAndreas Gohr $this->headers = array(); 1090cecf9d5Sandi } 1100cecf9d5Sandi 1113dd5c225SAndreas Gohr /** 1123dd5c225SAndreas Gohr * Finalize the document 1133dd5c225SAndreas Gohr */ 1140cecf9d5Sandi function document_end() { 11590df9a4dSAdrian Lang // Finish open section edits. 11690df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 11790df9a4dSAdrian Lang if($this->sectionedits[count($this->sectionedits) - 1][1] <= 1) { 11890df9a4dSAdrian Lang // If there is only one section, do not write a section edit 11990df9a4dSAdrian Lang // marker. 12090df9a4dSAdrian Lang array_pop($this->sectionedits); 12190df9a4dSAdrian Lang } else { 122d9e36cbeSAdrian Lang $this->finishSectionEdit(); 12390df9a4dSAdrian Lang } 12490df9a4dSAdrian Lang } 12590df9a4dSAdrian Lang 1260cecf9d5Sandi if(count($this->footnotes) > 0) { 127a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 128d74aace9Schris 12916ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 130d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 131d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 132d74aace9Schris 133d74aace9Schris // open the footnote and set the anchor and backlink 134d74aace9Schris $this->doc .= '<div class="fn">'; 13516cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 13629bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 137d74aace9Schris 138d74aace9Schris // get any other footnotes that use the same markup 139d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 140d74aace9Schris 141d74aace9Schris if(count($alt)) { 142d74aace9Schris foreach($alt as $ref) { 143d74aace9Schris // set anchor and backlink for the other footnotes 14416ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 14516ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 146d74aace9Schris } 147d74aace9Schris } 148d74aace9Schris 149d74aace9Schris // add footnote markup and close this footnote 150a2d649c4Sandi $this->doc .= $footnote; 151d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 152d74aace9Schris } 1530cecf9d5Sandi } 154a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1550cecf9d5Sandi } 156c5a8fd96SAndreas Gohr 157b8595a66SAndreas Gohr // Prepare the TOC 158851f2e89SAnika Henke global $conf; 159851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 160b8595a66SAndreas Gohr global $TOC; 161b8595a66SAndreas Gohr $TOC = $this->toc; 1620cecf9d5Sandi } 1633e55d035SAndreas Gohr 1643e55d035SAndreas Gohr // make sure there are no empty paragraphs 16527918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 166e41c4da9SAndreas Gohr } 1670cecf9d5Sandi 1683dd5c225SAndreas Gohr /** 1693dd5c225SAndreas Gohr * Add an item to the TOC 1703dd5c225SAndreas Gohr * 1713dd5c225SAndreas Gohr * @param string $id the hash link 1723dd5c225SAndreas Gohr * @param string $text the text to display 1733dd5c225SAndreas Gohr * @param int $level the nesting level 1743dd5c225SAndreas Gohr */ 175e7856beaSchris function toc_additem($id, $text, $level) { 176af587fa8Sandi global $conf; 177af587fa8Sandi 178c5a8fd96SAndreas Gohr //handle TOC 179c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1807d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 181c5a8fd96SAndreas Gohr } 182e7856beaSchris } 183e7856beaSchris 1843dd5c225SAndreas Gohr /** 1853dd5c225SAndreas Gohr * Render a heading 1863dd5c225SAndreas Gohr * 1873dd5c225SAndreas Gohr * @param string $text the text to display 1883dd5c225SAndreas Gohr * @param int $level header level 1893dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1903dd5c225SAndreas Gohr */ 191e7856beaSchris function header($text, $level, $pos) { 19290df9a4dSAdrian Lang global $conf; 19390df9a4dSAdrian Lang 194bdd8111bSAndreas Gohr if(!$text) return; //skip empty headlines 195e7856beaSchris 196e7856beaSchris $hid = $this->_headerToLink($text, true); 197e7856beaSchris 198e7856beaSchris //only add items within configured levels 199e7856beaSchris $this->toc_additem($hid, $text, $level); 200c5a8fd96SAndreas Gohr 20191459163SAnika Henke // adjust $node to reflect hierarchy of levels 20291459163SAnika Henke $this->node[$level - 1]++; 20391459163SAnika Henke if($level < $this->lastlevel) { 20491459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 20591459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 20691459163SAnika Henke } 20791459163SAnika Henke } 20891459163SAnika Henke $this->lastlevel = $level; 20991459163SAnika Henke 21090df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 21190df9a4dSAdrian Lang count($this->sectionedits) > 0 && 2123dd5c225SAndreas Gohr $this->sectionedits[count($this->sectionedits) - 1][2] === 'section' 2133dd5c225SAndreas Gohr ) { 2146c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 21590df9a4dSAdrian Lang } 21690df9a4dSAdrian Lang 217c5a8fd96SAndreas Gohr // write the header 21890df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 21990df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 22090df9a4dSAdrian Lang $this->doc .= ' class="'.$this->startSectionEdit($pos, 'section', $text).'"'; 22190df9a4dSAdrian Lang } 22216cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 223a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 22416cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2250cecf9d5Sandi } 2260cecf9d5Sandi 2273dd5c225SAndreas Gohr /** 2283dd5c225SAndreas Gohr * Open a new section 2293dd5c225SAndreas Gohr * 2303dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2313dd5c225SAndreas Gohr */ 2320cecf9d5Sandi function section_open($level) { 2339864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2340cecf9d5Sandi } 2350cecf9d5Sandi 2363dd5c225SAndreas Gohr /** 2373dd5c225SAndreas Gohr * Close the current section 2383dd5c225SAndreas Gohr */ 2390cecf9d5Sandi function section_close() { 240a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2410cecf9d5Sandi } 2420cecf9d5Sandi 2433dd5c225SAndreas Gohr /** 2443dd5c225SAndreas Gohr * Render plain text data 2453dd5c225SAndreas Gohr * 2463dd5c225SAndreas Gohr * @param $text 2473dd5c225SAndreas Gohr */ 2480cecf9d5Sandi function cdata($text) { 249a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2500cecf9d5Sandi } 2510cecf9d5Sandi 2523dd5c225SAndreas Gohr /** 2533dd5c225SAndreas Gohr * Open a paragraph 2543dd5c225SAndreas Gohr */ 2550cecf9d5Sandi function p_open() { 25659869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2570cecf9d5Sandi } 2580cecf9d5Sandi 2593dd5c225SAndreas Gohr /** 2603dd5c225SAndreas Gohr * Close a paragraph 2613dd5c225SAndreas Gohr */ 2620cecf9d5Sandi function p_close() { 26359869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2640cecf9d5Sandi } 2650cecf9d5Sandi 2663dd5c225SAndreas Gohr /** 2673dd5c225SAndreas Gohr * Create a line break 2683dd5c225SAndreas Gohr */ 2690cecf9d5Sandi function linebreak() { 270a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2710cecf9d5Sandi } 2720cecf9d5Sandi 2733dd5c225SAndreas Gohr /** 2743dd5c225SAndreas Gohr * Create a horizontal line 2753dd5c225SAndreas Gohr */ 2760cecf9d5Sandi function hr() { 2774beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2780cecf9d5Sandi } 2790cecf9d5Sandi 2803dd5c225SAndreas Gohr /** 2813dd5c225SAndreas Gohr * Start strong (bold) formatting 2823dd5c225SAndreas Gohr */ 2830cecf9d5Sandi function strong_open() { 284a2d649c4Sandi $this->doc .= '<strong>'; 2850cecf9d5Sandi } 2860cecf9d5Sandi 2873dd5c225SAndreas Gohr /** 2883dd5c225SAndreas Gohr * Stop strong (bold) formatting 2893dd5c225SAndreas Gohr */ 2900cecf9d5Sandi function strong_close() { 291a2d649c4Sandi $this->doc .= '</strong>'; 2920cecf9d5Sandi } 2930cecf9d5Sandi 2943dd5c225SAndreas Gohr /** 2953dd5c225SAndreas Gohr * Start emphasis (italics) formatting 2963dd5c225SAndreas Gohr */ 2970cecf9d5Sandi function emphasis_open() { 298a2d649c4Sandi $this->doc .= '<em>'; 2990cecf9d5Sandi } 3000cecf9d5Sandi 3013dd5c225SAndreas Gohr /** 3023dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3033dd5c225SAndreas Gohr */ 3040cecf9d5Sandi function emphasis_close() { 305a2d649c4Sandi $this->doc .= '</em>'; 3060cecf9d5Sandi } 3070cecf9d5Sandi 3083dd5c225SAndreas Gohr /** 3093dd5c225SAndreas Gohr * Start underline formatting 3103dd5c225SAndreas Gohr */ 3110cecf9d5Sandi function underline_open() { 31202e51121SAnika Henke $this->doc .= '<em class="u">'; 3130cecf9d5Sandi } 3140cecf9d5Sandi 3153dd5c225SAndreas Gohr /** 3163dd5c225SAndreas Gohr * Stop underline formatting 3173dd5c225SAndreas Gohr */ 3180cecf9d5Sandi function underline_close() { 31902e51121SAnika Henke $this->doc .= '</em>'; 3200cecf9d5Sandi } 3210cecf9d5Sandi 3223dd5c225SAndreas Gohr /** 3233dd5c225SAndreas Gohr * Start monospace formatting 3243dd5c225SAndreas Gohr */ 3250cecf9d5Sandi function monospace_open() { 326a2d649c4Sandi $this->doc .= '<code>'; 3270cecf9d5Sandi } 3280cecf9d5Sandi 3293dd5c225SAndreas Gohr /** 3303dd5c225SAndreas Gohr * Stop monospace formatting 3313dd5c225SAndreas Gohr */ 3320cecf9d5Sandi function monospace_close() { 333a2d649c4Sandi $this->doc .= '</code>'; 3340cecf9d5Sandi } 3350cecf9d5Sandi 3363dd5c225SAndreas Gohr /** 3373dd5c225SAndreas Gohr * Start a subscript 3383dd5c225SAndreas Gohr */ 3390cecf9d5Sandi function subscript_open() { 340a2d649c4Sandi $this->doc .= '<sub>'; 3410cecf9d5Sandi } 3420cecf9d5Sandi 3433dd5c225SAndreas Gohr /** 3443dd5c225SAndreas Gohr * Stop a subscript 3453dd5c225SAndreas Gohr */ 3460cecf9d5Sandi function subscript_close() { 347a2d649c4Sandi $this->doc .= '</sub>'; 3480cecf9d5Sandi } 3490cecf9d5Sandi 3503dd5c225SAndreas Gohr /** 3513dd5c225SAndreas Gohr * Start a superscript 3523dd5c225SAndreas Gohr */ 3530cecf9d5Sandi function superscript_open() { 354a2d649c4Sandi $this->doc .= '<sup>'; 3550cecf9d5Sandi } 3560cecf9d5Sandi 3573dd5c225SAndreas Gohr /** 3583dd5c225SAndreas Gohr * Stop a superscript 3593dd5c225SAndreas Gohr */ 3600cecf9d5Sandi function superscript_close() { 361a2d649c4Sandi $this->doc .= '</sup>'; 3620cecf9d5Sandi } 3630cecf9d5Sandi 3643dd5c225SAndreas Gohr /** 3653dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3663dd5c225SAndreas Gohr */ 3670cecf9d5Sandi function deleted_open() { 368a2d649c4Sandi $this->doc .= '<del>'; 3690cecf9d5Sandi } 3700cecf9d5Sandi 3713dd5c225SAndreas Gohr /** 3723dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 3733dd5c225SAndreas Gohr */ 3740cecf9d5Sandi function deleted_close() { 375a2d649c4Sandi $this->doc .= '</del>'; 3760cecf9d5Sandi } 3770cecf9d5Sandi 3783fd0b676Sandi /** 3793fd0b676Sandi * Callback for footnote start syntax 3803fd0b676Sandi * 3813fd0b676Sandi * All following content will go to the footnote instead of 382d74aace9Schris * the document. To achieve this the previous rendered content 3833fd0b676Sandi * is moved to $store and $doc is cleared 3843fd0b676Sandi * 3853fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 3863fd0b676Sandi */ 3870cecf9d5Sandi function footnote_open() { 3887764a90aSandi 3897764a90aSandi // move current content to store and record footnote 3907764a90aSandi $this->store = $this->doc; 3917764a90aSandi $this->doc = ''; 3920cecf9d5Sandi } 3930cecf9d5Sandi 3943fd0b676Sandi /** 3953fd0b676Sandi * Callback for footnote end syntax 3963fd0b676Sandi * 3973fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 3983fd0b676Sandi * content is restored from $store again 3993fd0b676Sandi * 4003fd0b676Sandi * @author Andreas Gohr 4013fd0b676Sandi */ 4020cecf9d5Sandi function footnote_close() { 40316ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 40416ec3e37SAndreas Gohr static $fnid = 0; 40516ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 40616ec3e37SAndreas Gohr $fnid++; 4077764a90aSandi 408d74aace9Schris // recover footnote into the stack and restore old content 409d74aace9Schris $footnote = $this->doc; 4107764a90aSandi $this->doc = $this->store; 4117764a90aSandi $this->store = ''; 412d74aace9Schris 413d74aace9Schris // check to see if this footnote has been seen before 414d74aace9Schris $i = array_search($footnote, $this->footnotes); 415d74aace9Schris 416d74aace9Schris if($i === false) { 417d74aace9Schris // its a new footnote, add it to the $footnotes array 41816ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 419d74aace9Schris } else { 42016ec3e37SAndreas Gohr // seen this one before, save a placeholder 42116ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 422d74aace9Schris } 423d74aace9Schris 4246b379cbfSAndreas Gohr // output the footnote reference and link 42516ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4260cecf9d5Sandi } 4270cecf9d5Sandi 4283dd5c225SAndreas Gohr /** 4293dd5c225SAndreas Gohr * Open an unordered list 430*0c4c0281SGerrit Uitslag * 431*0c4c0281SGerrit Uitslag * @param string $classes css class 4323dd5c225SAndreas Gohr */ 433*0c4c0281SGerrit Uitslag function listu_open($classes = null) { 434*0c4c0281SGerrit Uitslag $class = ''; 435*0c4c0281SGerrit Uitslag if($classes !== null) { 436*0c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 437*0c4c0281SGerrit Uitslag } 438*0c4c0281SGerrit Uitslag $this->doc .= "<ul$class>".DOKU_LF; 4390cecf9d5Sandi } 4400cecf9d5Sandi 4413dd5c225SAndreas Gohr /** 4423dd5c225SAndreas Gohr * Close an unordered list 4433dd5c225SAndreas Gohr */ 4440cecf9d5Sandi function listu_close() { 445a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4460cecf9d5Sandi } 4470cecf9d5Sandi 4483dd5c225SAndreas Gohr /** 4493dd5c225SAndreas Gohr * Open an ordered list 450*0c4c0281SGerrit Uitslag * 451*0c4c0281SGerrit Uitslag * @param string $classes css class 4523dd5c225SAndreas Gohr */ 453*0c4c0281SGerrit Uitslag function listo_open($classes = null) { 454*0c4c0281SGerrit Uitslag $class = ''; 455*0c4c0281SGerrit Uitslag if($classes !== null) { 456*0c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 457*0c4c0281SGerrit Uitslag } 458*0c4c0281SGerrit Uitslag $this->doc .= "<ol$class>".DOKU_LF; 4590cecf9d5Sandi } 4600cecf9d5Sandi 4613dd5c225SAndreas Gohr /** 4623dd5c225SAndreas Gohr * Close an ordered list 4633dd5c225SAndreas Gohr */ 4640cecf9d5Sandi function listo_close() { 465a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4660cecf9d5Sandi } 4670cecf9d5Sandi 4683dd5c225SAndreas Gohr /** 4693dd5c225SAndreas Gohr * Open a list item 4703dd5c225SAndreas Gohr * 4713dd5c225SAndreas Gohr * @param int $level the nesting level 472e3a24861SChristopher Smith * @param bool $node true when a node; false when a leaf 4733dd5c225SAndreas Gohr */ 474e3a24861SChristopher Smith function listitem_open($level, $node=false) { 475e3a24861SChristopher Smith $branching = $node ? ' node' : ''; 476e3a24861SChristopher Smith $this->doc .= '<li class="level'.$level.$branching.'">'; 4770cecf9d5Sandi } 4780cecf9d5Sandi 4793dd5c225SAndreas Gohr /** 4803dd5c225SAndreas Gohr * Close a list item 4813dd5c225SAndreas Gohr */ 4820cecf9d5Sandi function listitem_close() { 483a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 4840cecf9d5Sandi } 4850cecf9d5Sandi 4863dd5c225SAndreas Gohr /** 4873dd5c225SAndreas Gohr * Start the content of a list item 4883dd5c225SAndreas Gohr */ 4890cecf9d5Sandi function listcontent_open() { 49090db23d7Schris $this->doc .= '<div class="li">'; 4910cecf9d5Sandi } 4920cecf9d5Sandi 4933dd5c225SAndreas Gohr /** 4943dd5c225SAndreas Gohr * Stop the content of a list item 4953dd5c225SAndreas Gohr */ 4960cecf9d5Sandi function listcontent_close() { 49759869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 4980cecf9d5Sandi } 4990cecf9d5Sandi 5003dd5c225SAndreas Gohr /** 5013dd5c225SAndreas Gohr * Output unformatted $text 5023dd5c225SAndreas Gohr * 5033dd5c225SAndreas Gohr * Defaults to $this->cdata() 5043dd5c225SAndreas Gohr * 5053dd5c225SAndreas Gohr * @param string $text 5063dd5c225SAndreas Gohr */ 5070cecf9d5Sandi function unformatted($text) { 508a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 5090cecf9d5Sandi } 5100cecf9d5Sandi 5110cecf9d5Sandi /** 5123fd0b676Sandi * Execute PHP code if allowed 5133fd0b676Sandi * 514d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5155d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5165d568b99SChris Smith * 5173fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5180cecf9d5Sandi */ 5195d568b99SChris Smith function php($text, $wrapper = 'code') { 52035a56260SChris Smith global $conf; 52135a56260SChris Smith 522d86d5af0SChris Smith if($conf['phpok']) { 523bad0b545Sandi ob_start(); 5244de671bcSandi eval($text); 5253fd0b676Sandi $this->doc .= ob_get_contents(); 526bad0b545Sandi ob_end_clean(); 527d86d5af0SChris Smith } else { 5285d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 529d86d5af0SChris Smith } 5300cecf9d5Sandi } 5310cecf9d5Sandi 5323dd5c225SAndreas Gohr /** 5333dd5c225SAndreas Gohr * Output block level PHP code 5343dd5c225SAndreas Gohr * 5353dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5363dd5c225SAndreas Gohr * to $doc 5373dd5c225SAndreas Gohr * 5383dd5c225SAndreas Gohr * @param string $text The PHP code 5393dd5c225SAndreas Gohr */ 54007f89c3cSAnika Henke function phpblock($text) { 5415d568b99SChris Smith $this->php($text, 'pre'); 54207f89c3cSAnika Henke } 54307f89c3cSAnika Henke 5440cecf9d5Sandi /** 5453fd0b676Sandi * Insert HTML if allowed 5463fd0b676Sandi * 547d9764001SMichael Hamann * @param string $text html text 5485d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5495d568b99SChris Smith * 5503fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5510cecf9d5Sandi */ 5525d568b99SChris Smith function html($text, $wrapper = 'code') { 55335a56260SChris Smith global $conf; 55435a56260SChris Smith 555d86d5af0SChris Smith if($conf['htmlok']) { 556a2d649c4Sandi $this->doc .= $text; 557d86d5af0SChris Smith } else { 5585d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 559d86d5af0SChris Smith } 5604de671bcSandi } 5610cecf9d5Sandi 5623dd5c225SAndreas Gohr /** 5633dd5c225SAndreas Gohr * Output raw block-level HTML 5643dd5c225SAndreas Gohr * 5653dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 5663dd5c225SAndreas Gohr * 5673dd5c225SAndreas Gohr * @param string $text The HTML 5683dd5c225SAndreas Gohr */ 56907f89c3cSAnika Henke function htmlblock($text) { 5705d568b99SChris Smith $this->html($text, 'pre'); 57107f89c3cSAnika Henke } 57207f89c3cSAnika Henke 5733dd5c225SAndreas Gohr /** 5743dd5c225SAndreas Gohr * Start a block quote 5753dd5c225SAndreas Gohr */ 5760cecf9d5Sandi function quote_open() { 57796331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 5780cecf9d5Sandi } 5790cecf9d5Sandi 5803dd5c225SAndreas Gohr /** 5813dd5c225SAndreas Gohr * Stop a block quote 5823dd5c225SAndreas Gohr */ 5830cecf9d5Sandi function quote_close() { 58496331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 5850cecf9d5Sandi } 5860cecf9d5Sandi 5873dd5c225SAndreas Gohr /** 5883dd5c225SAndreas Gohr * Output preformatted text 5893dd5c225SAndreas Gohr * 5903dd5c225SAndreas Gohr * @param string $text 5913dd5c225SAndreas Gohr */ 5923d491f75SAndreas Gohr function preformatted($text) { 593c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 5943d491f75SAndreas Gohr } 5953d491f75SAndreas Gohr 5963dd5c225SAndreas Gohr /** 5973dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 5983dd5c225SAndreas Gohr * 5993dd5c225SAndreas Gohr * @param string $text text to show 6003dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6013dd5c225SAndreas Gohr * @param string $filename file path label 6023dd5c225SAndreas Gohr */ 6033d491f75SAndreas Gohr function file($text, $language = null, $filename = null) { 6043d491f75SAndreas Gohr $this->_highlight('file', $text, $language, $filename); 6053d491f75SAndreas Gohr } 6063d491f75SAndreas Gohr 6073dd5c225SAndreas Gohr /** 6083dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 6093dd5c225SAndreas Gohr * 6103dd5c225SAndreas Gohr * @param string $text text to show 6113dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6123dd5c225SAndreas Gohr * @param string $filename file path label 6133dd5c225SAndreas Gohr */ 6143d491f75SAndreas Gohr function code($text, $language = null, $filename = null) { 6153d491f75SAndreas Gohr $this->_highlight('code', $text, $language, $filename); 6163d491f75SAndreas Gohr } 6173d491f75SAndreas Gohr 6180cecf9d5Sandi /** 6193d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6203fd0b676Sandi * 6213fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6223dd5c225SAndreas Gohr * @param string $type code|file 6233dd5c225SAndreas Gohr * @param string $text text to show 6243dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6253dd5c225SAndreas Gohr * @param string $filename file path label 6260cecf9d5Sandi */ 6273d491f75SAndreas Gohr function _highlight($type, $text, $language = null, $filename = null) { 6283d491f75SAndreas Gohr global $ID; 6293d491f75SAndreas Gohr global $lang; 6303d491f75SAndreas Gohr 6313d491f75SAndreas Gohr if($filename) { 632190c56e8SAndreas Gohr // add icon 63327bf7924STom N Harris list($ext) = mimetype($filename, false); 634190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 635190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 636190c56e8SAndreas Gohr 6373d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 638190c56e8SAndreas Gohr $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6393d491f75SAndreas Gohr $this->doc .= hsc($filename); 6403d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6413d491f75SAndreas Gohr } 6420cecf9d5Sandi 643d43aac1cSGina Haeussge if($text{0} == "\n") { 644d43aac1cSGina Haeussge $text = substr($text, 1); 645d43aac1cSGina Haeussge } 646d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 647d43aac1cSGina Haeussge $text = substr($text, 0, -1); 648d43aac1cSGina Haeussge } 649d43aac1cSGina Haeussge 6500cecf9d5Sandi if(is_null($language)) { 6513d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6520cecf9d5Sandi } else { 6533d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6543d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6553d491f75SAndreas Gohr 6563d491f75SAndreas Gohr $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '').'</pre>'.DOKU_LF; 6570cecf9d5Sandi } 6583d491f75SAndreas Gohr 6593d491f75SAndreas Gohr if($filename) { 6603d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 6613d491f75SAndreas Gohr } 6623d491f75SAndreas Gohr 6633d491f75SAndreas Gohr $this->_codeblock++; 6640cecf9d5Sandi } 6650cecf9d5Sandi 6663dd5c225SAndreas Gohr /** 6673dd5c225SAndreas Gohr * Format an acronym 6683dd5c225SAndreas Gohr * 6693dd5c225SAndreas Gohr * Uses $this->acronyms 6703dd5c225SAndreas Gohr * 6713dd5c225SAndreas Gohr * @param string $acronym 6723dd5c225SAndreas Gohr */ 6730cecf9d5Sandi function acronym($acronym) { 6740cecf9d5Sandi 6750cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 6760cecf9d5Sandi 677433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 6780cecf9d5Sandi 679940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 680940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 6810cecf9d5Sandi 6820cecf9d5Sandi } else { 683a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 6840cecf9d5Sandi } 6850cecf9d5Sandi } 6860cecf9d5Sandi 6873dd5c225SAndreas Gohr /** 6883dd5c225SAndreas Gohr * Format a smiley 6893dd5c225SAndreas Gohr * 6903dd5c225SAndreas Gohr * Uses $this->smiley 6913dd5c225SAndreas Gohr * 6923dd5c225SAndreas Gohr * @param string $smiley 6933dd5c225SAndreas Gohr */ 6940cecf9d5Sandi function smiley($smiley) { 6950cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 696f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 6978e38227fSAnika Henke '" class="icon" alt="'. 698433bef32Sandi $this->_xmlEntities($smiley).'" />'; 6990cecf9d5Sandi } else { 700a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 7010cecf9d5Sandi } 7020cecf9d5Sandi } 7030cecf9d5Sandi 7043dd5c225SAndreas Gohr /** 7053dd5c225SAndreas Gohr * Format an entity 7063dd5c225SAndreas Gohr * 7073dd5c225SAndreas Gohr * Entities are basically small text replacements 7083dd5c225SAndreas Gohr * 7093dd5c225SAndreas Gohr * Uses $this->entities 7103dd5c225SAndreas Gohr * 7113dd5c225SAndreas Gohr * @param string $entity 7124de671bcSandi */ 7130cecf9d5Sandi function entity($entity) { 7140cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 715a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7160cecf9d5Sandi } else { 717a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7180cecf9d5Sandi } 7190cecf9d5Sandi } 7200cecf9d5Sandi 7213dd5c225SAndreas Gohr /** 7223dd5c225SAndreas Gohr * Typographically format a multiply sign 7233dd5c225SAndreas Gohr * 7243dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7253dd5c225SAndreas Gohr * 7263dd5c225SAndreas Gohr * @param string|int $x first value 7273dd5c225SAndreas Gohr * @param string|int $y second value 7283dd5c225SAndreas Gohr */ 7290cecf9d5Sandi function multiplyentity($x, $y) { 730a2d649c4Sandi $this->doc .= "$x×$y"; 7310cecf9d5Sandi } 7320cecf9d5Sandi 7333dd5c225SAndreas Gohr /** 7343dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7353dd5c225SAndreas Gohr */ 7360cecf9d5Sandi function singlequoteopening() { 73771b40da2SAnika Henke global $lang; 73871b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7390cecf9d5Sandi } 7400cecf9d5Sandi 7413dd5c225SAndreas Gohr /** 7423dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7433dd5c225SAndreas Gohr */ 7440cecf9d5Sandi function singlequoteclosing() { 74571b40da2SAnika Henke global $lang; 74671b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7470cecf9d5Sandi } 7480cecf9d5Sandi 7493dd5c225SAndreas Gohr /** 7503dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7513dd5c225SAndreas Gohr */ 75257d757d1SAndreas Gohr function apostrophe() { 75357d757d1SAndreas Gohr global $lang; 754a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 75557d757d1SAndreas Gohr } 75657d757d1SAndreas Gohr 7573dd5c225SAndreas Gohr /** 7583dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 7593dd5c225SAndreas Gohr */ 7600cecf9d5Sandi function doublequoteopening() { 76171b40da2SAnika Henke global $lang; 76271b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 7630cecf9d5Sandi } 7640cecf9d5Sandi 7653dd5c225SAndreas Gohr /** 7663dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 7673dd5c225SAndreas Gohr */ 7680cecf9d5Sandi function doublequoteclosing() { 76971b40da2SAnika Henke global $lang; 77071b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 7710cecf9d5Sandi } 7720cecf9d5Sandi 7730cecf9d5Sandi /** 7743dd5c225SAndreas Gohr * Render a CamelCase link 7753dd5c225SAndreas Gohr * 7763dd5c225SAndreas Gohr * @param string $link The link name 777122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 778*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 779*0c4c0281SGerrit Uitslag * 7803dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 7810cecf9d5Sandi */ 782122f2d46SAndreas Böhler function camelcaselink($link, $returnonly = false) { 783122f2d46SAndreas Böhler if($returnonly) { 784122f2d46SAndreas Böhler return $this->internallink($link, $link, null, true); 785122f2d46SAndreas Böhler } else { 78611d0aa47Sandi $this->internallink($link, $link); 7870cecf9d5Sandi } 788122f2d46SAndreas Böhler } 7890cecf9d5Sandi 7903dd5c225SAndreas Gohr /** 7913dd5c225SAndreas Gohr * Render a page local link 7923dd5c225SAndreas Gohr * 7933dd5c225SAndreas Gohr * @param string $hash hash link identifier 7943dd5c225SAndreas Gohr * @param string $name name for the link 795122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 796*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 7973dd5c225SAndreas Gohr */ 798122f2d46SAndreas Böhler function locallink($hash, $name = null, $returnonly = false) { 7990b7c14c2Sandi global $ID; 8000b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 8010b7c14c2Sandi $hash = $this->_headerToLink($hash); 802e260f93bSAnika Henke $title = $ID.' ↵'; 803122f2d46SAndreas Böhler 804122f2d46SAndreas Böhler $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 805122f2d46SAndreas Böhler $doc .= $name; 806122f2d46SAndreas Böhler $doc .= '</a>'; 807122f2d46SAndreas Böhler 808122f2d46SAndreas Böhler if($returnonly) { 809122f2d46SAndreas Böhler return $doc; 810122f2d46SAndreas Böhler } else { 811122f2d46SAndreas Böhler $this->doc .= $doc; 812122f2d46SAndreas Böhler } 8130b7c14c2Sandi } 8140b7c14c2Sandi 815cffcc403Sandi /** 8163fd0b676Sandi * Render an internal Wiki Link 8173fd0b676Sandi * 818fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 819cffcc403Sandi * elsewhere - no need to implement them in other renderers 8203fd0b676Sandi * 8213dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 822f23eef27SGerrit Uitslag * @param string $id pageid 823f23eef27SGerrit Uitslag * @param string|null $name link name 824f23eef27SGerrit Uitslag * @param string|null $search adds search url param 825f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 826f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 827f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 828cffcc403Sandi */ 8290ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 830ba11bd29Sandi global $conf; 83137e34a5eSandi global $ID; 832c4dda6afSAnika Henke global $INFO; 83344653a53SAdrian Lang 8343d5e07d9SAdrian Lang $params = ''; 8353d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8363d5e07d9SAdrian Lang if(count($parts) === 2) { 8373d5e07d9SAdrian Lang $id = $parts[0]; 8383d5e07d9SAdrian Lang $params = $parts[1]; 83944653a53SAdrian Lang } 84044653a53SAdrian Lang 841fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 842fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 843fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 844fda14ffcSIzidor Matušov // (some things could be lost) 845fda14ffcSIzidor Matušov if($id === '') { 846fda14ffcSIzidor Matušov $id = $ID; 847fda14ffcSIzidor Matušov } 848fda14ffcSIzidor Matušov 8490339c872Sjan // default name is based on $id as given 8500339c872Sjan $default = $this->_simpleTitle($id); 851ad32e47eSAndreas Gohr 8520339c872Sjan // now first resolve and clean up the $id 85390bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 854fda14ffcSIzidor Matušov 85559bc3b48SGerrit Uitslag $link = array(); 856fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8570e1c636eSandi if(!$isImage) { 8580e1c636eSandi if($exists) { 859ba11bd29Sandi $class = 'wikilink1'; 8600cecf9d5Sandi } else { 861ba11bd29Sandi $class = 'wikilink2'; 86244a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 8630cecf9d5Sandi } 8640cecf9d5Sandi } else { 865ba11bd29Sandi $class = 'media'; 8660cecf9d5Sandi } 8670cecf9d5Sandi 868a1685bedSandi //keep hash anchor 8696d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 870943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 871a1685bedSandi 872ba11bd29Sandi //prepare for formating 873ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 874ba11bd29Sandi $link['style'] = ''; 875ba11bd29Sandi $link['pre'] = ''; 876ba11bd29Sandi $link['suf'] = ''; 87740eb54bbSjan // highlight link to current page 878c4dda6afSAnika Henke if($id == $INFO['id']) { 87992795d04Sandi $link['pre'] = '<span class="curid">'; 88092795d04Sandi $link['suf'] = '</span>'; 88140eb54bbSjan } 8825e163278SAndreas Gohr $link['more'] = ''; 883ba11bd29Sandi $link['class'] = $class; 8845c2eed9aSlisps if($this->date_at) { 8855c2eed9aSlisps $params['at'] = $this->date_at; 8865c2eed9aSlisps } 88744653a53SAdrian Lang $link['url'] = wl($id, $params); 888ba11bd29Sandi $link['name'] = $name; 889ba11bd29Sandi $link['title'] = $id; 890723d78dbSandi //add search string 891723d78dbSandi if($search) { 892546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 893546d3a99SAndreas Gohr if(is_array($search)) { 894546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 895546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 896546d3a99SAndreas Gohr } else { 897546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 898546d3a99SAndreas Gohr } 899723d78dbSandi } 900723d78dbSandi 901a1685bedSandi //keep hash 902a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 903a1685bedSandi 904ba11bd29Sandi //output formatted 905cffcc403Sandi if($returnonly) { 906cffcc403Sandi return $this->_formatLink($link); 907cffcc403Sandi } else { 908a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9090cecf9d5Sandi } 910cffcc403Sandi } 9110cecf9d5Sandi 9123dd5c225SAndreas Gohr /** 9133dd5c225SAndreas Gohr * Render an external link 9143dd5c225SAndreas Gohr * 9153dd5c225SAndreas Gohr * @param string $url full URL with scheme 9163dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 917122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 918*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9193dd5c225SAndreas Gohr */ 920122f2d46SAndreas Böhler function externallink($url, $name = null, $returnonly = false) { 921b625487dSandi global $conf; 9220cecf9d5Sandi 923433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 9246f0c5dbfSandi 925b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 926b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 927b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 928b52b1596SAndreas Gohr $scheme = strtolower($scheme); 929b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 930b52b1596SAndreas Gohr 931b52b1596SAndreas Gohr // is there still an URL? 932b52b1596SAndreas Gohr if(!$url) { 93349cef4fdSAndreas Böhler if($returnonly) { 93449cef4fdSAndreas Böhler return $name; 93549cef4fdSAndreas Böhler } else { 936b52b1596SAndreas Gohr $this->doc .= $name; 93749cef4fdSAndreas Böhler } 938b52b1596SAndreas Gohr return; 939b52b1596SAndreas Gohr } 940b52b1596SAndreas Gohr 941b52b1596SAndreas Gohr // set class 9420cecf9d5Sandi if(!$isImage) { 943b625487dSandi $class = 'urlextern'; 9440cecf9d5Sandi } else { 945b625487dSandi $class = 'media'; 9460cecf9d5Sandi } 9470cecf9d5Sandi 948b625487dSandi //prepare for formating 94959bc3b48SGerrit Uitslag $link = array(); 950b625487dSandi $link['target'] = $conf['target']['extern']; 951b625487dSandi $link['style'] = ''; 952b625487dSandi $link['pre'] = ''; 953b625487dSandi $link['suf'] = ''; 9545e163278SAndreas Gohr $link['more'] = ''; 955b625487dSandi $link['class'] = $class; 956b625487dSandi $link['url'] = $url; 957914045f3SAndreas Gohr $link['rel'] = ''; 958e1c10e4dSchris 959b625487dSandi $link['name'] = $name; 960433bef32Sandi $link['title'] = $this->_xmlEntities($url); 961914045f3SAndreas Gohr if($conf['relnofollow']) $link['rel'] .= ' nofollow'; 962914045f3SAndreas Gohr if($conf['target']['extern']) $link['rel'] .= ' noopener'; 9630cecf9d5Sandi 964b625487dSandi //output formatted 965122f2d46SAndreas Böhler if($returnonly) { 966122f2d46SAndreas Böhler return $this->_formatLink($link); 967122f2d46SAndreas Böhler } else { 968a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9690cecf9d5Sandi } 970122f2d46SAndreas Böhler } 9710cecf9d5Sandi 9720cecf9d5Sandi /** 9733dd5c225SAndreas Gohr * Render an interwiki link 9743dd5c225SAndreas Gohr * 9753dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 9763dd5c225SAndreas Gohr * 9773dd5c225SAndreas Gohr * @param string $match original link - probably not much use 9783dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 9793dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 9803dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 981122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 982*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9830cecf9d5Sandi */ 984122f2d46SAndreas Böhler function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) { 985b625487dSandi global $conf; 9860cecf9d5Sandi 98797a3e4e3Sandi $link = array(); 98897a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 98997a3e4e3Sandi $link['pre'] = ''; 99097a3e4e3Sandi $link['suf'] = ''; 9915e163278SAndreas Gohr $link['more'] = ''; 992433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 993914045f3SAndreas Gohr $link['rel'] = ''; 9940cecf9d5Sandi 99597a3e4e3Sandi //get interwiki URL 9966496c33fSGerrit Uitslag $exists = null; 9976496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 9980cecf9d5Sandi 99997a3e4e3Sandi if(!$isImage) { 10009d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 10019d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 10021c2d1019SAndreas Gohr } else { 10031c2d1019SAndreas Gohr $link['class'] = 'media'; 100497a3e4e3Sandi } 10050cecf9d5Sandi 100697a3e4e3Sandi //do we stay at the same server? Use local target 10072345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 100897a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 100997a3e4e3Sandi } 10106496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 10116496c33fSGerrit Uitslag if($exists) { 10126496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 10136496c33fSGerrit Uitslag } else { 10146496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 1015914045f3SAndreas Gohr $link['rel'] .= ' nofollow'; 10166496c33fSGerrit Uitslag } 10176496c33fSGerrit Uitslag } 1018914045f3SAndreas Gohr if($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 10190cecf9d5Sandi 102097a3e4e3Sandi $link['url'] = $url; 102197a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 102297a3e4e3Sandi 102397a3e4e3Sandi //output formatted 1024122f2d46SAndreas Böhler if($returnonly) { 1025122f2d46SAndreas Böhler return $this->_formatLink($link); 1026122f2d46SAndreas Böhler } else { 1027a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10280cecf9d5Sandi } 1029122f2d46SAndreas Böhler } 10300cecf9d5Sandi 10310cecf9d5Sandi /** 10323dd5c225SAndreas Gohr * Link to windows share 10333dd5c225SAndreas Gohr * 10343dd5c225SAndreas Gohr * @param string $url the link 10353dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1036122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 1037*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10380cecf9d5Sandi */ 1039122f2d46SAndreas Böhler function windowssharelink($url, $name = null, $returnonly = false) { 10401d47afe1Sandi global $conf; 10413dd5c225SAndreas Gohr 10421d47afe1Sandi //simple setup 104359bc3b48SGerrit Uitslag $link = array(); 10441d47afe1Sandi $link['target'] = $conf['target']['windows']; 10451d47afe1Sandi $link['pre'] = ''; 10461d47afe1Sandi $link['suf'] = ''; 10471d47afe1Sandi $link['style'] = ''; 10480cecf9d5Sandi 1049433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 10500cecf9d5Sandi if(!$isImage) { 10511d47afe1Sandi $link['class'] = 'windows'; 10520cecf9d5Sandi } else { 10531d47afe1Sandi $link['class'] = 'media'; 10540cecf9d5Sandi } 10550cecf9d5Sandi 1056433bef32Sandi $link['title'] = $this->_xmlEntities($url); 10571d47afe1Sandi $url = str_replace('\\', '/', $url); 10581d47afe1Sandi $url = 'file:///'.$url; 10591d47afe1Sandi $link['url'] = $url; 10600cecf9d5Sandi 10611d47afe1Sandi //output formatted 1062122f2d46SAndreas Böhler if($returnonly) { 1063122f2d46SAndreas Böhler return $this->_formatLink($link); 1064122f2d46SAndreas Böhler } else { 1065a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10660cecf9d5Sandi } 1067122f2d46SAndreas Böhler } 10680cecf9d5Sandi 10693dd5c225SAndreas Gohr /** 10703dd5c225SAndreas Gohr * Render a linked E-Mail Address 10713dd5c225SAndreas Gohr * 10723dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 10733dd5c225SAndreas Gohr * 10743dd5c225SAndreas Gohr * @param string $address Email-Address 10753dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1076122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 1077*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10783dd5c225SAndreas Gohr */ 1079122f2d46SAndreas Böhler function emaillink($address, $name = null, $returnonly = false) { 108071352defSandi global $conf; 108171352defSandi //simple setup 108271352defSandi $link = array(); 108371352defSandi $link['target'] = ''; 108471352defSandi $link['pre'] = ''; 108571352defSandi $link['suf'] = ''; 108671352defSandi $link['style'] = ''; 108771352defSandi $link['more'] = ''; 10880cecf9d5Sandi 1089c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 10900cecf9d5Sandi if(!$isImage) { 1091be96545cSAnika Henke $link['class'] = 'mail'; 10920cecf9d5Sandi } else { 1093be96545cSAnika Henke $link['class'] = 'media'; 10940cecf9d5Sandi } 10950cecf9d5Sandi 109607738714SAndreas Gohr $address = $this->_xmlEntities($address); 109700a7b5adSEsther Brunner $address = obfuscate($address); 109800a7b5adSEsther Brunner $title = $address; 10998c128049SAndreas Gohr 110071352defSandi if(empty($name)) { 110100a7b5adSEsther Brunner $name = $address; 110271352defSandi } 11030cecf9d5Sandi 1104776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1105776b36ecSAndreas Gohr 1106776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 110771352defSandi $link['name'] = $name; 110871352defSandi $link['title'] = $title; 11090cecf9d5Sandi 111071352defSandi //output formatted 1111122f2d46SAndreas Böhler if($returnonly) { 1112122f2d46SAndreas Böhler return $this->_formatLink($link); 1113122f2d46SAndreas Böhler } else { 1114a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11150cecf9d5Sandi } 1116122f2d46SAndreas Böhler } 11170cecf9d5Sandi 11183dd5c225SAndreas Gohr /** 11193dd5c225SAndreas Gohr * Render an internal media file 11203dd5c225SAndreas Gohr * 11213dd5c225SAndreas Gohr * @param string $src media ID 11223dd5c225SAndreas Gohr * @param string $title descriptive text 11233dd5c225SAndreas Gohr * @param string $align left|center|right 11243dd5c225SAndreas Gohr * @param int $width width of media in pixel 11253dd5c225SAndreas Gohr * @param int $height height of media in pixel 11263dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11273dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 11283dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 1129*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11303dd5c225SAndreas Gohr */ 11310ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 11323dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 113337e34a5eSandi global $ID; 113491df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1135cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 11360cecf9d5Sandi 1137d98d4540SBen Coburn $noLink = false; 11388acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1139b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 11403685f775Sandi 11413dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1142b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 114352dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct')); 1144f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11452a2a2ba2SAnika Henke // don't link movies 114644881bd0Shenning.noren $noLink = true; 114755efc227SAndreas Gohr } else { 11482ca14335SEsther Brunner // add file icons 11499d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 11509d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 115152dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true); 115291328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 115355efc227SAndreas Gohr } 11543685f775Sandi 115591df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 115691df343aSAndreas Gohr 11576fe20453SGina Haeussge //markup non existing files 11584a24b459SKate Arzamastseva if(!$exists) { 11596fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 11604a24b459SKate Arzamastseva } 11616fe20453SGina Haeussge 11623685f775Sandi //output formatted 1163f50634f0SAnika Henke if($return) { 1164f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1165f50634f0SAnika Henke else return $this->_formatLink($link); 1166f50634f0SAnika Henke } else { 1167dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 11682ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 11690cecf9d5Sandi } 1170f50634f0SAnika Henke } 11710cecf9d5Sandi 11723dd5c225SAndreas Gohr /** 11733dd5c225SAndreas Gohr * Render an external media file 11743dd5c225SAndreas Gohr * 11753dd5c225SAndreas Gohr * @param string $src full media URL 11763dd5c225SAndreas Gohr * @param string $title descriptive text 11773dd5c225SAndreas Gohr * @param string $align left|center|right 11783dd5c225SAndreas Gohr * @param int $width width of media in pixel 11793dd5c225SAndreas Gohr * @param int $height height of media in pixel 11803dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11813dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1182410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 1183*0c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11843dd5c225SAndreas Gohr */ 11850ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 1186410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 118791df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1188d98d4540SBen Coburn $noLink = false; 11898acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1190b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1191b739ff0fSPierre Spring 1192b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 11933685f775Sandi 11943dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1195b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 11962ca14335SEsther Brunner // link only jpeg images 119744881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1198f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11992a2a2ba2SAnika Henke // don't link movies 120044881bd0Shenning.noren $noLink = true; 12012ca14335SEsther Brunner } else { 12022ca14335SEsther Brunner // add file icons 120327bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 120427bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 12052ca14335SEsther Brunner } 12062ca14335SEsther Brunner 120791df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 120891df343aSAndreas Gohr 12093685f775Sandi //output formatted 1210410ee62aSAnika Henke if($return) { 1211410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1212410ee62aSAnika Henke else return $this->_formatLink($link); 1213410ee62aSAnika Henke } else { 1214dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12152ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12160cecf9d5Sandi } 1217410ee62aSAnika Henke } 12180cecf9d5Sandi 12194826ab45Sandi /** 12203db95becSAndreas Gohr * Renders an RSS feed 1221b625487dSandi * 1222*0c4c0281SGerrit Uitslag * @param string $url URL of the feed 1223*0c4c0281SGerrit Uitslag * @param array $params Finetuning of the output 1224*0c4c0281SGerrit Uitslag * 1225b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1226b625487dSandi */ 12273db95becSAndreas Gohr function rss($url, $params) { 1228b625487dSandi global $lang; 12293db95becSAndreas Gohr global $conf; 12303db95becSAndreas Gohr 12313db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 12323db95becSAndreas Gohr $feed = new FeedParser(); 123300077af8SAndreas Gohr $feed->set_feed_url($url); 1234b625487dSandi 1235b625487dSandi //disable warning while fetching 12363dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 12373dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 12383dd5c225SAndreas Gohr } 12393db95becSAndreas Gohr $rc = $feed->init(); 12403dd5c225SAndreas Gohr if(isset($elvl)) { 12413dd5c225SAndreas Gohr error_reporting($elvl); 12423dd5c225SAndreas Gohr } 1243b625487dSandi 124438c6f603SRobin H. Johnson if($params['nosort']) $feed->enable_order_by_date(false); 124538c6f603SRobin H. Johnson 12463db95becSAndreas Gohr //decide on start and end 12473db95becSAndreas Gohr if($params['reverse']) { 12483db95becSAndreas Gohr $mod = -1; 12493db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 12503db95becSAndreas Gohr $end = $start - ($params['max']); 1251b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 12523db95becSAndreas Gohr } else { 12533db95becSAndreas Gohr $mod = 1; 12543db95becSAndreas Gohr $start = 0; 12553db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1256d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 12573db95becSAndreas Gohr } 12583db95becSAndreas Gohr 1259a2d649c4Sandi $this->doc .= '<ul class="rss">'; 12603db95becSAndreas Gohr if($rc) { 12613db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 12621bde1582SAndreas Gohr $item = $feed->get_item($x); 12633db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1264d2ea3363SAndreas Gohr // support feeds without links 1265d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1266d2ea3363SAndreas Gohr if($lnkurl) { 1267793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1268793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 12693dd5c225SAndreas Gohr $this->externallink( 12703dd5c225SAndreas Gohr $item->get_permalink(), 12713dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 12723dd5c225SAndreas Gohr ); 1273d2ea3363SAndreas Gohr } else { 1274d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1275d2ea3363SAndreas Gohr } 12763db95becSAndreas Gohr if($params['author']) { 12771bde1582SAndreas Gohr $author = $item->get_author(0); 12781bde1582SAndreas Gohr if($author) { 12791bde1582SAndreas Gohr $name = $author->get_name(); 12801bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 12811bde1582SAndreas Gohr if($name) $this->doc .= ' '.$lang['by'].' '.$name; 12821bde1582SAndreas Gohr } 12833db95becSAndreas Gohr } 12843db95becSAndreas Gohr if($params['date']) { 12852e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 12863db95becSAndreas Gohr } 12871bde1582SAndreas Gohr if($params['details']) { 12883db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1289173dccb7STom N Harris if($conf['htmlok']) { 12901bde1582SAndreas Gohr $this->doc .= $item->get_description(); 12913db95becSAndreas Gohr } else { 12921bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 12933db95becSAndreas Gohr } 12943db95becSAndreas Gohr $this->doc .= '</div>'; 12953db95becSAndreas Gohr } 12963db95becSAndreas Gohr 12973db95becSAndreas Gohr $this->doc .= '</div></li>'; 1298b625487dSandi } 1299b625487dSandi } else { 13003db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1301a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1302b625487dSandi $this->externallink($url); 130345e147ccSAndreas Gohr if($conf['allowdebug']) { 130445e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 130545e147ccSAndreas Gohr } 13063db95becSAndreas Gohr $this->doc .= '</div></li>'; 1307b625487dSandi } 1308a2d649c4Sandi $this->doc .= '</ul>'; 1309b625487dSandi } 1310b625487dSandi 13113dd5c225SAndreas Gohr /** 13123dd5c225SAndreas Gohr * Start a table 13133dd5c225SAndreas Gohr * 13143dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 13153dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 13163dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1317*0c4c0281SGerrit Uitslag * @param string $classes css class 13183dd5c225SAndreas Gohr */ 1319*0c4c0281SGerrit Uitslag function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 1320b5742cedSPierre Spring // initialize the row counter used for classes 1321b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1322619736fdSAdrian Lang $class = 'table'; 1323*0c4c0281SGerrit Uitslag if($classes !== null) { 1324*0c4c0281SGerrit Uitslag $class .= ' ' . $classes; 1325*0c4c0281SGerrit Uitslag } 1326619736fdSAdrian Lang if($pos !== null) { 1327619736fdSAdrian Lang $class .= ' '.$this->startSectionEdit($pos, 'table'); 1328619736fdSAdrian Lang } 1329619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1330619736fdSAdrian Lang DOKU_LF; 13310cecf9d5Sandi } 13320cecf9d5Sandi 13333dd5c225SAndreas Gohr /** 13343dd5c225SAndreas Gohr * Close a table 13353dd5c225SAndreas Gohr * 13363dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13373dd5c225SAndreas Gohr */ 1338619736fdSAdrian Lang function table_close($pos = null) { 1339a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1340619736fdSAdrian Lang if($pos !== null) { 134190df9a4dSAdrian Lang $this->finishSectionEdit($pos); 13420cecf9d5Sandi } 1343619736fdSAdrian Lang } 13440cecf9d5Sandi 13453dd5c225SAndreas Gohr /** 13463dd5c225SAndreas Gohr * Open a table header 13473dd5c225SAndreas Gohr */ 1348f05a1cc5SGerrit Uitslag function tablethead_open() { 1349f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1350f05a1cc5SGerrit Uitslag } 1351f05a1cc5SGerrit Uitslag 13523dd5c225SAndreas Gohr /** 13533dd5c225SAndreas Gohr * Close a table header 13543dd5c225SAndreas Gohr */ 1355f05a1cc5SGerrit Uitslag function tablethead_close() { 1356f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1357f05a1cc5SGerrit Uitslag } 1358f05a1cc5SGerrit Uitslag 13593dd5c225SAndreas Gohr /** 13605a93f869SAnika Henke * Open a table body 13615a93f869SAnika Henke */ 13625a93f869SAnika Henke function tabletbody_open() { 13635a93f869SAnika Henke $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF; 13645a93f869SAnika Henke } 13655a93f869SAnika Henke 13665a93f869SAnika Henke /** 13675a93f869SAnika Henke * Close a table body 13685a93f869SAnika Henke */ 13695a93f869SAnika Henke function tabletbody_close() { 13705a93f869SAnika Henke $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF; 13715a93f869SAnika Henke } 13725a93f869SAnika Henke 13735a93f869SAnika Henke /** 13743dd5c225SAndreas Gohr * Open a table row 1375*0c4c0281SGerrit Uitslag * 1376*0c4c0281SGerrit Uitslag * @param string $classes css class 13773dd5c225SAndreas Gohr */ 1378*0c4c0281SGerrit Uitslag function tablerow_open($classes = null) { 1379b5742cedSPierre Spring // initialize the cell counter used for classes 1380b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1381b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 1382*0c4c0281SGerrit Uitslag if($classes !== null) { 1383*0c4c0281SGerrit Uitslag $class .= ' ' . $classes; 1384*0c4c0281SGerrit Uitslag } 1385b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 13860cecf9d5Sandi } 13870cecf9d5Sandi 13883dd5c225SAndreas Gohr /** 13893dd5c225SAndreas Gohr * Close a table row 13903dd5c225SAndreas Gohr */ 13910cecf9d5Sandi function tablerow_close() { 1392a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 13930cecf9d5Sandi } 13940cecf9d5Sandi 13953dd5c225SAndreas Gohr /** 13963dd5c225SAndreas Gohr * Open a table header cell 13973dd5c225SAndreas Gohr * 13983dd5c225SAndreas Gohr * @param int $colspan 13993dd5c225SAndreas Gohr * @param string $align left|center|right 14003dd5c225SAndreas Gohr * @param int $rowspan 1401*0c4c0281SGerrit Uitslag * @param string $classes css class 14023dd5c225SAndreas Gohr */ 1403*0c4c0281SGerrit Uitslag function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1404b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14050cecf9d5Sandi if(!is_null($align)) { 1406b5742cedSPierre Spring $class .= ' '.$align.'align'; 14070cecf9d5Sandi } 1408*0c4c0281SGerrit Uitslag if($classes !== null) { 1409*0c4c0281SGerrit Uitslag $class .= ' ' . $classes; 1410*0c4c0281SGerrit Uitslag } 1411b5742cedSPierre Spring $class .= '"'; 1412b5742cedSPierre Spring $this->doc .= '<th '.$class; 14130cecf9d5Sandi if($colspan > 1) { 1414a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1415a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14160cecf9d5Sandi } 141725b97867Shakan.sandell if($rowspan > 1) { 141825b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 141925b97867Shakan.sandell } 1420a2d649c4Sandi $this->doc .= '>'; 14210cecf9d5Sandi } 14220cecf9d5Sandi 14233dd5c225SAndreas Gohr /** 14243dd5c225SAndreas Gohr * Close a table header cell 14253dd5c225SAndreas Gohr */ 14260cecf9d5Sandi function tableheader_close() { 1427a2d649c4Sandi $this->doc .= '</th>'; 14280cecf9d5Sandi } 14290cecf9d5Sandi 14303dd5c225SAndreas Gohr /** 14313dd5c225SAndreas Gohr * Open a table cell 14323dd5c225SAndreas Gohr * 14333dd5c225SAndreas Gohr * @param int $colspan 14343dd5c225SAndreas Gohr * @param string $align left|center|right 14353dd5c225SAndreas Gohr * @param int $rowspan 1436*0c4c0281SGerrit Uitslag * @param string $classes css class 14373dd5c225SAndreas Gohr */ 1438*0c4c0281SGerrit Uitslag function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1439b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14400cecf9d5Sandi if(!is_null($align)) { 1441b5742cedSPierre Spring $class .= ' '.$align.'align'; 14420cecf9d5Sandi } 1443*0c4c0281SGerrit Uitslag if($classes !== null) { 1444*0c4c0281SGerrit Uitslag $class .= ' ' . $classes; 1445*0c4c0281SGerrit Uitslag } 1446b5742cedSPierre Spring $class .= '"'; 1447b5742cedSPierre Spring $this->doc .= '<td '.$class; 14480cecf9d5Sandi if($colspan > 1) { 1449a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1450a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14510cecf9d5Sandi } 145225b97867Shakan.sandell if($rowspan > 1) { 145325b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 145425b97867Shakan.sandell } 1455a2d649c4Sandi $this->doc .= '>'; 14560cecf9d5Sandi } 14570cecf9d5Sandi 14583dd5c225SAndreas Gohr /** 14593dd5c225SAndreas Gohr * Close a table cell 14603dd5c225SAndreas Gohr */ 14610cecf9d5Sandi function tablecell_close() { 1462a2d649c4Sandi $this->doc .= '</td>'; 14630cecf9d5Sandi } 14640cecf9d5Sandi 14653dd5c225SAndreas Gohr #region Utility functions 14660cecf9d5Sandi 1467ba11bd29Sandi /** 14683fd0b676Sandi * Build a link 14693fd0b676Sandi * 14703fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1471ba11bd29Sandi * 1472*0c4c0281SGerrit Uitslag * @param array $link attributes of a link 1473*0c4c0281SGerrit Uitslag * @return string 1474*0c4c0281SGerrit Uitslag * 1475ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1476ba11bd29Sandi */ 1477433bef32Sandi function _formatLink($link) { 1478ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1479ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1480ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1481ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1482ba11bd29Sandi } 1483ba11bd29Sandi //remove double encodings in titles 1484ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1485ba11bd29Sandi 1486453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1487453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1488453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1489453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1490453493f2SAndreas Gohr 1491ba11bd29Sandi $ret = ''; 1492ba11bd29Sandi $ret .= $link['pre']; 1493ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1494bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1495bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1496bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1497bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 1498914045f3SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"'; 1499bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1500ba11bd29Sandi $ret .= '>'; 1501ba11bd29Sandi $ret .= $link['name']; 1502ba11bd29Sandi $ret .= '</a>'; 1503ba11bd29Sandi $ret .= $link['suf']; 1504ba11bd29Sandi return $ret; 1505ba11bd29Sandi } 1506ba11bd29Sandi 1507ba11bd29Sandi /** 15083fd0b676Sandi * Renders internal and external media 15093fd0b676Sandi * 15103fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 15113dd5c225SAndreas Gohr * @param string $src media ID 15123dd5c225SAndreas Gohr * @param string $title descriptive text 15133dd5c225SAndreas Gohr * @param string $align left|center|right 15143dd5c225SAndreas Gohr * @param int $width width of media in pixel 15153dd5c225SAndreas Gohr * @param int $height height of media in pixel 15163dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 15173dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 15183dd5c225SAndreas Gohr * @return string 15193fd0b676Sandi */ 15200ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 15210ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 15223fd0b676Sandi 15233fd0b676Sandi $ret = ''; 15243fd0b676Sandi 15253dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 15263fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1527b739ff0fSPierre Spring // first get the $title 1528b739ff0fSPierre Spring if(!is_null($title)) { 1529b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1530b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1531b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1532b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 153367f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1534b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 15353dd5c225SAndreas Gohr if(!empty($cap)) { 1536b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1537b739ff0fSPierre Spring } 1538b739ff0fSPierre Spring } 1539b739ff0fSPierre Spring if(!$render) { 1540b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1541b739ff0fSPierre Spring // return the title of the picture 1542b739ff0fSPierre Spring if(!$title) { 1543b739ff0fSPierre Spring // just show the sourcename 15443009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1545b739ff0fSPierre Spring } 1546b739ff0fSPierre Spring return $title; 1547b739ff0fSPierre Spring } 15483fd0b676Sandi //add image tag 154952dc5eadSlisps $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"'; 15503fd0b676Sandi $ret .= ' class="media'.$align.'"'; 15513fd0b676Sandi 1552b739ff0fSPierre Spring if($title) { 1553b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1554b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 15553fd0b676Sandi } else { 15563fd0b676Sandi $ret .= ' alt=""'; 15573fd0b676Sandi } 15583fd0b676Sandi 15593fd0b676Sandi if(!is_null($width)) 15603fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 15613fd0b676Sandi 15623fd0b676Sandi if(!is_null($height)) 15633fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 15643fd0b676Sandi 15653fd0b676Sandi $ret .= ' />'; 15663fd0b676Sandi 156717954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 15682a2a2ba2SAnika Henke // first get the $title 156917954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 15702a2a2ba2SAnika Henke if(!$render) { 157117954bb5SAnika Henke // if the file is not supposed to be rendered 157217954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 157317954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 15742a2a2ba2SAnika Henke } 15752a2a2ba2SAnika Henke 15762a2a2ba2SAnika Henke $att = array(); 15772a2a2ba2SAnika Henke $att['class'] = "media$align"; 157817954bb5SAnika Henke if($title) { 157917954bb5SAnika Henke $att['title'] = $title; 158017954bb5SAnika Henke } 15812a2a2ba2SAnika Henke 158217954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 158317954bb5SAnika Henke //add video 158479e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1585b44a5dceSAnika Henke } 158617954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1587b44a5dceSAnika Henke //add audio 1588b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 158917954bb5SAnika Henke } 1590b44a5dceSAnika Henke 15913fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 15921c882ba8SAndreas Gohr if(!$render) { 15931c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 15941c882ba8SAndreas Gohr // return the title of the flash 15951c882ba8SAndreas Gohr if(!$title) { 15961c882ba8SAndreas Gohr // just show the sourcename 15973009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 15981c882ba8SAndreas Gohr } 159907bf32b2SAndreas Gohr return $this->_xmlEntities($title); 16001c882ba8SAndreas Gohr } 16011c882ba8SAndreas Gohr 160207bf32b2SAndreas Gohr $att = array(); 160307bf32b2SAndreas Gohr $att['class'] = "media$align"; 160407bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 160507bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 16063dd5c225SAndreas Gohr $ret .= html_flashobject( 16073dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 160807bf32b2SAndreas Gohr array('quality' => 'high'), 160907bf32b2SAndreas Gohr null, 161007bf32b2SAndreas Gohr $att, 16113dd5c225SAndreas Gohr $this->_xmlEntities($title) 16123dd5c225SAndreas Gohr ); 16130f428d7dSAndreas Gohr } elseif($title) { 16143fd0b676Sandi // well at least we have a title to display 16153fd0b676Sandi $ret .= $this->_xmlEntities($title); 16163fd0b676Sandi } else { 16175291ca3aSAndreas Gohr // just show the sourcename 16183009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 16193fd0b676Sandi } 16203fd0b676Sandi 16213fd0b676Sandi return $ret; 16223fd0b676Sandi } 16233fd0b676Sandi 16243dd5c225SAndreas Gohr /** 16253dd5c225SAndreas Gohr * Escape string for output 16263dd5c225SAndreas Gohr * 16273dd5c225SAndreas Gohr * @param $string 16283dd5c225SAndreas Gohr * @return string 16293dd5c225SAndreas Gohr */ 1630433bef32Sandi function _xmlEntities($string) { 1631de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 16320cecf9d5Sandi } 16330cecf9d5Sandi 16348a831f2bSAndreas Gohr /** 16358a831f2bSAndreas Gohr * Creates a linkid from a headline 1636c5a8fd96SAndreas Gohr * 16373dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1638c5a8fd96SAndreas Gohr * @param string $title The headline title 1639c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 16403dd5c225SAndreas Gohr * @return string 16418a831f2bSAndreas Gohr */ 1642c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1643c5a8fd96SAndreas Gohr if($create) { 16444ceab83fSAndreas Gohr return sectionID($title, $this->headers); 16454ceab83fSAndreas Gohr } else { 1646443d207bSAndreas Gohr $check = false; 1647443d207bSAndreas Gohr return sectionID($title, $check); 1648c5a8fd96SAndreas Gohr } 16490cecf9d5Sandi } 16500cecf9d5Sandi 1651af587fa8Sandi /** 16523fd0b676Sandi * Construct a title and handle images in titles 16533fd0b676Sandi * 16540b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 16553dd5c225SAndreas Gohr * @param string|array $title either string title or media array 16563dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 16573dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 16583dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 16593dd5c225SAndreas Gohr * @param string $linktype content|navigation 16603dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 16613fd0b676Sandi */ 16620ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 166344881bd0Shenning.noren $isImage = false; 166429657f9eSAndreas Gohr if(is_array($title)) { 166529657f9eSAndreas Gohr $isImage = true; 166629657f9eSAndreas Gohr return $this->_imageTitle($title); 166729657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1668fe9ec250SChris Smith if(useHeading($linktype) && $id) { 166967c15eceSMichael Hamann $heading = p_get_first_heading($id); 1670bb0a59d4Sjan if($heading) { 1671433bef32Sandi return $this->_xmlEntities($heading); 1672bb0a59d4Sjan } 1673bb0a59d4Sjan } 1674433bef32Sandi return $this->_xmlEntities($default); 167568c26e6dSMichael Klier } else { 167668c26e6dSMichael Klier return $this->_xmlEntities($title); 16770cecf9d5Sandi } 16780cecf9d5Sandi } 16790cecf9d5Sandi 16800cecf9d5Sandi /** 16813dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 16823fd0b676Sandi * 16833fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1684e0c26282SGerrit Uitslag * @param array $img 16853dd5c225SAndreas Gohr * @return string HTML img tag or similar 16860cecf9d5Sandi */ 1687433bef32Sandi function _imageTitle($img) { 1688d9baf1a7SKazutaka Miyasaka global $ID; 1689d9baf1a7SKazutaka Miyasaka 1690d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1691d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 16923dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1693d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1694cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1695d9baf1a7SKazutaka Miyasaka } 1696d9baf1a7SKazutaka Miyasaka 16973dd5c225SAndreas Gohr return $this->_media( 16983dd5c225SAndreas Gohr $img['src'], 16994826ab45Sandi $img['title'], 17004826ab45Sandi $img['align'], 17014826ab45Sandi $img['width'], 17024826ab45Sandi $img['height'], 17033dd5c225SAndreas Gohr $img['cache'] 17043dd5c225SAndreas Gohr ); 17050cecf9d5Sandi } 1706b739ff0fSPierre Spring 1707b739ff0fSPierre Spring /** 17083dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 17093dd5c225SAndreas Gohr * 17103dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1711b739ff0fSPierre Spring * 1712b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 17133dd5c225SAndreas Gohr * @param string $src media ID 17143dd5c225SAndreas Gohr * @param string $title descriptive text 17153dd5c225SAndreas Gohr * @param string $align left|center|right 17163dd5c225SAndreas Gohr * @param int $width width of media in pixel 17173dd5c225SAndreas Gohr * @param int $height height of media in pixel 17183dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 17193dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 17203dd5c225SAndreas Gohr * @return array associative array with link config 1721b739ff0fSPierre Spring */ 1722d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1723b739ff0fSPierre Spring global $conf; 1724b739ff0fSPierre Spring 1725b739ff0fSPierre Spring $link = array(); 1726b739ff0fSPierre Spring $link['class'] = 'media'; 1727b739ff0fSPierre Spring $link['style'] = ''; 1728b739ff0fSPierre Spring $link['pre'] = ''; 1729b739ff0fSPierre Spring $link['suf'] = ''; 1730b739ff0fSPierre Spring $link['more'] = ''; 1731b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1732bc3d2252SAndreas Gohr if($conf['target']['media']) $link['rel'] = 'noopener'; 1733b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1734b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1735b739ff0fSPierre Spring 1736b739ff0fSPierre Spring return $link; 1737b739ff0fSPierre Spring } 173891459163SAnika Henke 17392a2a2ba2SAnika Henke /** 17402a2a2ba2SAnika Henke * Embed video(s) in HTML 17412a2a2ba2SAnika Henke * 17422a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 17432a2a2ba2SAnika Henke * 17442a2a2ba2SAnika Henke * @param string $src - ID of video to embed 17452a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 17462a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 17472a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1748f50634f0SAnika Henke * @return string 17492a2a2ba2SAnika Henke */ 175079e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 17512a2a2ba2SAnika Henke // prepare width and height 17522a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 17532a2a2ba2SAnika Henke $atts['width'] = (int) $width; 17542a2a2ba2SAnika Henke $atts['height'] = (int) $height; 17552a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 17562a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 17572a2a2ba2SAnika Henke 1758410ee62aSAnika Henke $posterUrl = ''; 1759410ee62aSAnika Henke $files = array(); 1760410ee62aSAnika Henke $isExternal = media_isexternal($src); 1761410ee62aSAnika Henke 1762410ee62aSAnika Henke if ($isExternal) { 1763410ee62aSAnika Henke // take direct source for external files 1764702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1765410ee62aSAnika Henke $files[$srcMime] = $src; 1766410ee62aSAnika Henke } else { 17673d7a9e0aSAnika Henke // prepare alternative formats 17683d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1769410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 177059bc3b48SGerrit Uitslag $poster = media_alternativefiles($src, array('jpg', 'png')); 177199f943f6SAnika Henke if(!empty($poster)) { 17722d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 177399f943f6SAnika Henke } 1774410ee62aSAnika Henke } 17752a2a2ba2SAnika Henke 1776f50634f0SAnika Henke $out = ''; 177779e53fe5SAnika Henke // open video tag 1778f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 17793641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1780f50634f0SAnika Henke $out .= '>'.NL; 17813641199aSAnika Henke $fallback = ''; 178279e53fe5SAnika Henke 178379e53fe5SAnika Henke // output source for each alternative video format 1784410ee62aSAnika Henke foreach($files as $mime => $file) { 1785410ee62aSAnika Henke if ($isExternal) { 1786410ee62aSAnika Henke $url = $file; 1787410ee62aSAnika Henke $linkType = 'externalmedia'; 1788410ee62aSAnika Henke } else { 17892d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1790410ee62aSAnika Henke $linkType = 'internalmedia'; 1791410ee62aSAnika Henke } 179217954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 17933d7a9e0aSAnika Henke 1794f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 179579e53fe5SAnika Henke // alternative content (just a link to the file) 1796410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 17973d7a9e0aSAnika Henke } 17982a2a2ba2SAnika Henke 17992a2a2ba2SAnika Henke // finish 18003641199aSAnika Henke $out .= $fallback; 1801f50634f0SAnika Henke $out .= '</video>'.NL; 1802f50634f0SAnika Henke return $out; 18032a2a2ba2SAnika Henke } 18042a2a2ba2SAnika Henke 1805b44a5dceSAnika Henke /** 1806b44a5dceSAnika Henke * Embed audio in HTML 1807b44a5dceSAnika Henke * 1808b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1809b44a5dceSAnika Henke * 1810b44a5dceSAnika Henke * @param string $src - ID of audio to embed 18116d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1812f50634f0SAnika Henke * @return string 1813b44a5dceSAnika Henke */ 181459bc3b48SGerrit Uitslag function _audio($src, $atts = array()) { 1815702e97d3SAnika Henke $files = array(); 1816410ee62aSAnika Henke $isExternal = media_isexternal($src); 1817b44a5dceSAnika Henke 1818410ee62aSAnika Henke if ($isExternal) { 1819410ee62aSAnika Henke // take direct source for external files 1820702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1821410ee62aSAnika Henke $files[$srcMime] = $src; 1822410ee62aSAnika Henke } else { 1823b44a5dceSAnika Henke // prepare alternative formats 1824b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1825410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1826410ee62aSAnika Henke } 1827b44a5dceSAnika Henke 1828f50634f0SAnika Henke $out = ''; 1829b44a5dceSAnika Henke // open audio tag 1830f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 18313641199aSAnika Henke $fallback = ''; 1832b44a5dceSAnika Henke 1833b44a5dceSAnika Henke // output source for each alternative audio format 1834410ee62aSAnika Henke foreach($files as $mime => $file) { 1835410ee62aSAnika Henke if ($isExternal) { 1836410ee62aSAnika Henke $url = $file; 1837410ee62aSAnika Henke $linkType = 'externalmedia'; 1838410ee62aSAnika Henke } else { 18392d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1840410ee62aSAnika Henke $linkType = 'internalmedia'; 1841410ee62aSAnika Henke } 184217954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1843b44a5dceSAnika Henke 1844f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1845b44a5dceSAnika Henke // alternative content (just a link to the file) 1846410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1847b44a5dceSAnika Henke } 1848b44a5dceSAnika Henke 1849b44a5dceSAnika Henke // finish 18503641199aSAnika Henke $out .= $fallback; 1851f50634f0SAnika Henke $out .= '</audio>'.NL; 1852f50634f0SAnika Henke return $out; 1853b44a5dceSAnika Henke } 1854b44a5dceSAnika Henke 18555c2eed9aSlisps /** 185652dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 18575c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 18585c2eed9aSlisps * 18595c2eed9aSlisps * @author lisps 18605c2eed9aSlisps * @param string $media_id 18615c2eed9aSlisps * @access protected 18625c2eed9aSlisps * @return string revision ('' for current) 18635c2eed9aSlisps */ 186452dc5eadSlisps function _getLastMediaRevisionAt($media_id){ 186552dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 186678b874e6Slisps $pagelog = new MediaChangeLog($media_id); 186778b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 18685c2eed9aSlisps } 18695c2eed9aSlisps 18703dd5c225SAndreas Gohr #endregion 18710cecf9d5Sandi} 18720cecf9d5Sandi 1873e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1874