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 * 6290df9a4dSAdrian Lang * @param $type string The section type identifier 6390df9a4dSAdrian Lang * @param $title string The section title 6490df9a4dSAdrian Lang * @param $start int The byte position for the edit start 6590df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 6690df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 6790df9a4dSAdrian Lang */ 683f9e3215SAdrian Lang public function startSectionEdit($start, $type, $title = null) { 69b04a190dSMichael Hamann $this->sectionedits[] = array(++$this->lastsecid, $start, $type, $title); 70b04a190dSMichael Hamann return 'sectionedit'.$this->lastsecid; 7190df9a4dSAdrian Lang } 7290df9a4dSAdrian Lang 7390df9a4dSAdrian Lang /** 7490df9a4dSAdrian Lang * Finish an edit section range 7590df9a4dSAdrian Lang * 76d9e36cbeSAdrian Lang * @param $end int The byte position for the edit end; null for the rest of 77c404cb3bSMatt Perry * the page 7890df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 7990df9a4dSAdrian Lang */ 803f9e3215SAdrian Lang public function finishSectionEdit($end = null) { 8190df9a4dSAdrian Lang list($id, $start, $type, $title) = array_pop($this->sectionedits); 82d9e36cbeSAdrian Lang if(!is_null($end) && $end <= $start) { 8300c13053SAdrian Lang return; 8400c13053SAdrian Lang } 8540868f2fSAdrian Lang $this->doc .= "<!-- EDIT$id ".strtoupper($type).' '; 8640868f2fSAdrian Lang if(!is_null($title)) { 8740868f2fSAdrian Lang $this->doc .= '"'.str_replace('"', '', $title).'" '; 8840868f2fSAdrian Lang } 89d9e36cbeSAdrian Lang $this->doc .= "[$start-".(is_null($end) ? '' : $end).'] -->'; 9090df9a4dSAdrian Lang } 9190df9a4dSAdrian Lang 923dd5c225SAndreas Gohr /** 933dd5c225SAndreas Gohr * Returns the format produced by this renderer. 943dd5c225SAndreas Gohr * 953dd5c225SAndreas Gohr * @return string always 'xhtml' 963dd5c225SAndreas Gohr */ 975f70445dSAndreas Gohr function getFormat() { 985f70445dSAndreas Gohr return 'xhtml'; 995f70445dSAndreas Gohr } 1005f70445dSAndreas Gohr 1013dd5c225SAndreas Gohr /** 1023dd5c225SAndreas Gohr * Initialize the document 1033dd5c225SAndreas Gohr */ 1040cecf9d5Sandi function document_start() { 105c5a8fd96SAndreas Gohr //reset some internals 106c5a8fd96SAndreas Gohr $this->toc = array(); 107c5a8fd96SAndreas Gohr $this->headers = array(); 1080cecf9d5Sandi } 1090cecf9d5Sandi 1103dd5c225SAndreas Gohr /** 1113dd5c225SAndreas Gohr * Finalize the document 1123dd5c225SAndreas Gohr */ 1130cecf9d5Sandi function document_end() { 11490df9a4dSAdrian Lang // Finish open section edits. 11590df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 11690df9a4dSAdrian Lang if($this->sectionedits[count($this->sectionedits) - 1][1] <= 1) { 11790df9a4dSAdrian Lang // If there is only one section, do not write a section edit 11890df9a4dSAdrian Lang // marker. 11990df9a4dSAdrian Lang array_pop($this->sectionedits); 12090df9a4dSAdrian Lang } else { 121d9e36cbeSAdrian Lang $this->finishSectionEdit(); 12290df9a4dSAdrian Lang } 12390df9a4dSAdrian Lang } 12490df9a4dSAdrian Lang 1250cecf9d5Sandi if(count($this->footnotes) > 0) { 126a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 127d74aace9Schris 12816ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 129d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 130d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 131d74aace9Schris 132d74aace9Schris // open the footnote and set the anchor and backlink 133d74aace9Schris $this->doc .= '<div class="fn">'; 13416cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 13529bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 136d74aace9Schris 137d74aace9Schris // get any other footnotes that use the same markup 138d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 139d74aace9Schris 140d74aace9Schris if(count($alt)) { 141d74aace9Schris foreach($alt as $ref) { 142d74aace9Schris // set anchor and backlink for the other footnotes 14316ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 14416ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 145d74aace9Schris } 146d74aace9Schris } 147d74aace9Schris 148d74aace9Schris // add footnote markup and close this footnote 149a2d649c4Sandi $this->doc .= $footnote; 150d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 151d74aace9Schris } 1520cecf9d5Sandi } 153a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1540cecf9d5Sandi } 155c5a8fd96SAndreas Gohr 156b8595a66SAndreas Gohr // Prepare the TOC 157851f2e89SAnika Henke global $conf; 158851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 159b8595a66SAndreas Gohr global $TOC; 160b8595a66SAndreas Gohr $TOC = $this->toc; 1610cecf9d5Sandi } 1623e55d035SAndreas Gohr 1633e55d035SAndreas Gohr // make sure there are no empty paragraphs 16427918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 165e41c4da9SAndreas Gohr } 1660cecf9d5Sandi 1673dd5c225SAndreas Gohr /** 1683dd5c225SAndreas Gohr * Add an item to the TOC 1693dd5c225SAndreas Gohr * 1703dd5c225SAndreas Gohr * @param string $id the hash link 1713dd5c225SAndreas Gohr * @param string $text the text to display 1723dd5c225SAndreas Gohr * @param int $level the nesting level 1733dd5c225SAndreas Gohr */ 174e7856beaSchris function toc_additem($id, $text, $level) { 175af587fa8Sandi global $conf; 176af587fa8Sandi 177c5a8fd96SAndreas Gohr //handle TOC 178c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1797d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 180c5a8fd96SAndreas Gohr } 181e7856beaSchris } 182e7856beaSchris 1833dd5c225SAndreas Gohr /** 1843dd5c225SAndreas Gohr * Render a heading 1853dd5c225SAndreas Gohr * 1863dd5c225SAndreas Gohr * @param string $text the text to display 1873dd5c225SAndreas Gohr * @param int $level header level 1883dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1893dd5c225SAndreas Gohr */ 190e7856beaSchris function header($text, $level, $pos) { 19190df9a4dSAdrian Lang global $conf; 19290df9a4dSAdrian Lang 193bdd8111bSAndreas Gohr if(!$text) return; //skip empty headlines 194e7856beaSchris 195e7856beaSchris $hid = $this->_headerToLink($text, true); 196e7856beaSchris 197e7856beaSchris //only add items within configured levels 198e7856beaSchris $this->toc_additem($hid, $text, $level); 199c5a8fd96SAndreas Gohr 20091459163SAnika Henke // adjust $node to reflect hierarchy of levels 20191459163SAnika Henke $this->node[$level - 1]++; 20291459163SAnika Henke if($level < $this->lastlevel) { 20391459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 20491459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 20591459163SAnika Henke } 20691459163SAnika Henke } 20791459163SAnika Henke $this->lastlevel = $level; 20891459163SAnika Henke 20990df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 21090df9a4dSAdrian Lang count($this->sectionedits) > 0 && 2113dd5c225SAndreas Gohr $this->sectionedits[count($this->sectionedits) - 1][2] === 'section' 2123dd5c225SAndreas Gohr ) { 2136c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 21490df9a4dSAdrian Lang } 21590df9a4dSAdrian Lang 216c5a8fd96SAndreas Gohr // write the header 21790df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 21890df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 21990df9a4dSAdrian Lang $this->doc .= ' class="'.$this->startSectionEdit($pos, 'section', $text).'"'; 22090df9a4dSAdrian Lang } 22116cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 222a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 22316cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2240cecf9d5Sandi } 2250cecf9d5Sandi 2263dd5c225SAndreas Gohr /** 2273dd5c225SAndreas Gohr * Open a new section 2283dd5c225SAndreas Gohr * 2293dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2303dd5c225SAndreas Gohr */ 2310cecf9d5Sandi function section_open($level) { 2329864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2330cecf9d5Sandi } 2340cecf9d5Sandi 2353dd5c225SAndreas Gohr /** 2363dd5c225SAndreas Gohr * Close the current section 2373dd5c225SAndreas Gohr */ 2380cecf9d5Sandi function section_close() { 239a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2400cecf9d5Sandi } 2410cecf9d5Sandi 2423dd5c225SAndreas Gohr /** 2433dd5c225SAndreas Gohr * Render plain text data 2443dd5c225SAndreas Gohr * 2453dd5c225SAndreas Gohr * @param $text 2463dd5c225SAndreas Gohr */ 2470cecf9d5Sandi function cdata($text) { 248a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2490cecf9d5Sandi } 2500cecf9d5Sandi 2513dd5c225SAndreas Gohr /** 2523dd5c225SAndreas Gohr * Open a paragraph 2533dd5c225SAndreas Gohr */ 2540cecf9d5Sandi function p_open() { 25559869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2560cecf9d5Sandi } 2570cecf9d5Sandi 2583dd5c225SAndreas Gohr /** 2593dd5c225SAndreas Gohr * Close a paragraph 2603dd5c225SAndreas Gohr */ 2610cecf9d5Sandi function p_close() { 26259869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2630cecf9d5Sandi } 2640cecf9d5Sandi 2653dd5c225SAndreas Gohr /** 2663dd5c225SAndreas Gohr * Create a line break 2673dd5c225SAndreas Gohr */ 2680cecf9d5Sandi function linebreak() { 269a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2700cecf9d5Sandi } 2710cecf9d5Sandi 2723dd5c225SAndreas Gohr /** 2733dd5c225SAndreas Gohr * Create a horizontal line 2743dd5c225SAndreas Gohr */ 2750cecf9d5Sandi function hr() { 2764beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2770cecf9d5Sandi } 2780cecf9d5Sandi 2793dd5c225SAndreas Gohr /** 2803dd5c225SAndreas Gohr * Start strong (bold) formatting 2813dd5c225SAndreas Gohr */ 2820cecf9d5Sandi function strong_open() { 283a2d649c4Sandi $this->doc .= '<strong>'; 2840cecf9d5Sandi } 2850cecf9d5Sandi 2863dd5c225SAndreas Gohr /** 2873dd5c225SAndreas Gohr * Stop strong (bold) formatting 2883dd5c225SAndreas Gohr */ 2890cecf9d5Sandi function strong_close() { 290a2d649c4Sandi $this->doc .= '</strong>'; 2910cecf9d5Sandi } 2920cecf9d5Sandi 2933dd5c225SAndreas Gohr /** 2943dd5c225SAndreas Gohr * Start emphasis (italics) formatting 2953dd5c225SAndreas Gohr */ 2960cecf9d5Sandi function emphasis_open() { 297a2d649c4Sandi $this->doc .= '<em>'; 2980cecf9d5Sandi } 2990cecf9d5Sandi 3003dd5c225SAndreas Gohr /** 3013dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3023dd5c225SAndreas Gohr */ 3030cecf9d5Sandi function emphasis_close() { 304a2d649c4Sandi $this->doc .= '</em>'; 3050cecf9d5Sandi } 3060cecf9d5Sandi 3073dd5c225SAndreas Gohr /** 3083dd5c225SAndreas Gohr * Start underline formatting 3093dd5c225SAndreas Gohr */ 3100cecf9d5Sandi function underline_open() { 31102e51121SAnika Henke $this->doc .= '<em class="u">'; 3120cecf9d5Sandi } 3130cecf9d5Sandi 3143dd5c225SAndreas Gohr /** 3153dd5c225SAndreas Gohr * Stop underline formatting 3163dd5c225SAndreas Gohr */ 3170cecf9d5Sandi function underline_close() { 31802e51121SAnika Henke $this->doc .= '</em>'; 3190cecf9d5Sandi } 3200cecf9d5Sandi 3213dd5c225SAndreas Gohr /** 3223dd5c225SAndreas Gohr * Start monospace formatting 3233dd5c225SAndreas Gohr */ 3240cecf9d5Sandi function monospace_open() { 325a2d649c4Sandi $this->doc .= '<code>'; 3260cecf9d5Sandi } 3270cecf9d5Sandi 3283dd5c225SAndreas Gohr /** 3293dd5c225SAndreas Gohr * Stop monospace formatting 3303dd5c225SAndreas Gohr */ 3310cecf9d5Sandi function monospace_close() { 332a2d649c4Sandi $this->doc .= '</code>'; 3330cecf9d5Sandi } 3340cecf9d5Sandi 3353dd5c225SAndreas Gohr /** 3363dd5c225SAndreas Gohr * Start a subscript 3373dd5c225SAndreas Gohr */ 3380cecf9d5Sandi function subscript_open() { 339a2d649c4Sandi $this->doc .= '<sub>'; 3400cecf9d5Sandi } 3410cecf9d5Sandi 3423dd5c225SAndreas Gohr /** 3433dd5c225SAndreas Gohr * Stop a subscript 3443dd5c225SAndreas Gohr */ 3450cecf9d5Sandi function subscript_close() { 346a2d649c4Sandi $this->doc .= '</sub>'; 3470cecf9d5Sandi } 3480cecf9d5Sandi 3493dd5c225SAndreas Gohr /** 3503dd5c225SAndreas Gohr * Start a superscript 3513dd5c225SAndreas Gohr */ 3520cecf9d5Sandi function superscript_open() { 353a2d649c4Sandi $this->doc .= '<sup>'; 3540cecf9d5Sandi } 3550cecf9d5Sandi 3563dd5c225SAndreas Gohr /** 3573dd5c225SAndreas Gohr * Stop a superscript 3583dd5c225SAndreas Gohr */ 3590cecf9d5Sandi function superscript_close() { 360a2d649c4Sandi $this->doc .= '</sup>'; 3610cecf9d5Sandi } 3620cecf9d5Sandi 3633dd5c225SAndreas Gohr /** 3643dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3653dd5c225SAndreas Gohr */ 3660cecf9d5Sandi function deleted_open() { 367a2d649c4Sandi $this->doc .= '<del>'; 3680cecf9d5Sandi } 3690cecf9d5Sandi 3703dd5c225SAndreas Gohr /** 3713dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 3723dd5c225SAndreas Gohr */ 3730cecf9d5Sandi function deleted_close() { 374a2d649c4Sandi $this->doc .= '</del>'; 3750cecf9d5Sandi } 3760cecf9d5Sandi 3773fd0b676Sandi /** 3783fd0b676Sandi * Callback for footnote start syntax 3793fd0b676Sandi * 3803fd0b676Sandi * All following content will go to the footnote instead of 381d74aace9Schris * the document. To achieve this the previous rendered content 3823fd0b676Sandi * is moved to $store and $doc is cleared 3833fd0b676Sandi * 3843fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 3853fd0b676Sandi */ 3860cecf9d5Sandi function footnote_open() { 3877764a90aSandi 3887764a90aSandi // move current content to store and record footnote 3897764a90aSandi $this->store = $this->doc; 3907764a90aSandi $this->doc = ''; 3910cecf9d5Sandi } 3920cecf9d5Sandi 3933fd0b676Sandi /** 3943fd0b676Sandi * Callback for footnote end syntax 3953fd0b676Sandi * 3963fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 3973fd0b676Sandi * content is restored from $store again 3983fd0b676Sandi * 3993fd0b676Sandi * @author Andreas Gohr 4003fd0b676Sandi */ 4010cecf9d5Sandi function footnote_close() { 40216ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 40316ec3e37SAndreas Gohr static $fnid = 0; 40416ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 40516ec3e37SAndreas Gohr $fnid++; 4067764a90aSandi 407d74aace9Schris // recover footnote into the stack and restore old content 408d74aace9Schris $footnote = $this->doc; 4097764a90aSandi $this->doc = $this->store; 4107764a90aSandi $this->store = ''; 411d74aace9Schris 412d74aace9Schris // check to see if this footnote has been seen before 413d74aace9Schris $i = array_search($footnote, $this->footnotes); 414d74aace9Schris 415d74aace9Schris if($i === false) { 416d74aace9Schris // its a new footnote, add it to the $footnotes array 41716ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 418d74aace9Schris } else { 41916ec3e37SAndreas Gohr // seen this one before, save a placeholder 42016ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 421d74aace9Schris } 422d74aace9Schris 4236b379cbfSAndreas Gohr // output the footnote reference and link 42416ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4250cecf9d5Sandi } 4260cecf9d5Sandi 4273dd5c225SAndreas Gohr /** 4283dd5c225SAndreas Gohr * Open an unordered list 4293dd5c225SAndreas Gohr */ 4300cecf9d5Sandi function listu_open() { 431a2d649c4Sandi $this->doc .= '<ul>'.DOKU_LF; 4320cecf9d5Sandi } 4330cecf9d5Sandi 4343dd5c225SAndreas Gohr /** 4353dd5c225SAndreas Gohr * Close an unordered list 4363dd5c225SAndreas Gohr */ 4370cecf9d5Sandi function listu_close() { 438a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4390cecf9d5Sandi } 4400cecf9d5Sandi 4413dd5c225SAndreas Gohr /** 4423dd5c225SAndreas Gohr * Open an ordered list 4433dd5c225SAndreas Gohr */ 4440cecf9d5Sandi function listo_open() { 445a2d649c4Sandi $this->doc .= '<ol>'.DOKU_LF; 4460cecf9d5Sandi } 4470cecf9d5Sandi 4483dd5c225SAndreas Gohr /** 4493dd5c225SAndreas Gohr * Close an ordered list 4503dd5c225SAndreas Gohr */ 4510cecf9d5Sandi function listo_close() { 452a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4530cecf9d5Sandi } 4540cecf9d5Sandi 4553dd5c225SAndreas Gohr /** 4563dd5c225SAndreas Gohr * Open a list item 4573dd5c225SAndreas Gohr * 4583dd5c225SAndreas Gohr * @param int $level the nesting level 4593dd5c225SAndreas Gohr */ 4600cecf9d5Sandi function listitem_open($level) { 46159869a4bSAnika Henke $this->doc .= '<li class="level'.$level.'">'; 4620cecf9d5Sandi } 4630cecf9d5Sandi 4643dd5c225SAndreas Gohr /** 4653dd5c225SAndreas Gohr * Close a list item 4663dd5c225SAndreas Gohr */ 4670cecf9d5Sandi function listitem_close() { 468a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 4690cecf9d5Sandi } 4700cecf9d5Sandi 4713dd5c225SAndreas Gohr /** 4723dd5c225SAndreas Gohr * Start the content of a list item 4733dd5c225SAndreas Gohr */ 4740cecf9d5Sandi function listcontent_open() { 47590db23d7Schris $this->doc .= '<div class="li">'; 4760cecf9d5Sandi } 4770cecf9d5Sandi 4783dd5c225SAndreas Gohr /** 4793dd5c225SAndreas Gohr * Stop the content of a list item 4803dd5c225SAndreas Gohr */ 4810cecf9d5Sandi function listcontent_close() { 48259869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 4830cecf9d5Sandi } 4840cecf9d5Sandi 4853dd5c225SAndreas Gohr /** 4863dd5c225SAndreas Gohr * Output unformatted $text 4873dd5c225SAndreas Gohr * 4883dd5c225SAndreas Gohr * Defaults to $this->cdata() 4893dd5c225SAndreas Gohr * 4903dd5c225SAndreas Gohr * @param string $text 4913dd5c225SAndreas Gohr */ 4920cecf9d5Sandi function unformatted($text) { 493a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 4940cecf9d5Sandi } 4950cecf9d5Sandi 4960cecf9d5Sandi /** 4973fd0b676Sandi * Execute PHP code if allowed 4983fd0b676Sandi * 499d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5005d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5015d568b99SChris Smith * 5023fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5030cecf9d5Sandi */ 5045d568b99SChris Smith function php($text, $wrapper = 'code') { 50535a56260SChris Smith global $conf; 50635a56260SChris Smith 507d86d5af0SChris Smith if($conf['phpok']) { 508bad0b545Sandi ob_start(); 5094de671bcSandi eval($text); 5103fd0b676Sandi $this->doc .= ob_get_contents(); 511bad0b545Sandi ob_end_clean(); 512d86d5af0SChris Smith } else { 5135d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 514d86d5af0SChris Smith } 5150cecf9d5Sandi } 5160cecf9d5Sandi 5173dd5c225SAndreas Gohr /** 5183dd5c225SAndreas Gohr * Output block level PHP code 5193dd5c225SAndreas Gohr * 5203dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5213dd5c225SAndreas Gohr * to $doc 5223dd5c225SAndreas Gohr * 5233dd5c225SAndreas Gohr * @param string $text The PHP code 5243dd5c225SAndreas Gohr */ 52507f89c3cSAnika Henke function phpblock($text) { 5265d568b99SChris Smith $this->php($text, 'pre'); 52707f89c3cSAnika Henke } 52807f89c3cSAnika Henke 5290cecf9d5Sandi /** 5303fd0b676Sandi * Insert HTML if allowed 5313fd0b676Sandi * 532d9764001SMichael Hamann * @param string $text html text 5335d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5345d568b99SChris Smith * 5353fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5360cecf9d5Sandi */ 5375d568b99SChris Smith function html($text, $wrapper = 'code') { 53835a56260SChris Smith global $conf; 53935a56260SChris Smith 540d86d5af0SChris Smith if($conf['htmlok']) { 541a2d649c4Sandi $this->doc .= $text; 542d86d5af0SChris Smith } else { 5435d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 544d86d5af0SChris Smith } 5454de671bcSandi } 5460cecf9d5Sandi 5473dd5c225SAndreas Gohr /** 5483dd5c225SAndreas Gohr * Output raw block-level HTML 5493dd5c225SAndreas Gohr * 5503dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 5513dd5c225SAndreas Gohr * 5523dd5c225SAndreas Gohr * @param string $text The HTML 5533dd5c225SAndreas Gohr */ 55407f89c3cSAnika Henke function htmlblock($text) { 5555d568b99SChris Smith $this->html($text, 'pre'); 55607f89c3cSAnika Henke } 55707f89c3cSAnika Henke 5583dd5c225SAndreas Gohr /** 5593dd5c225SAndreas Gohr * Start a block quote 5603dd5c225SAndreas Gohr */ 5610cecf9d5Sandi function quote_open() { 56296331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 5630cecf9d5Sandi } 5640cecf9d5Sandi 5653dd5c225SAndreas Gohr /** 5663dd5c225SAndreas Gohr * Stop a block quote 5673dd5c225SAndreas Gohr */ 5680cecf9d5Sandi function quote_close() { 56996331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 5700cecf9d5Sandi } 5710cecf9d5Sandi 5723dd5c225SAndreas Gohr /** 5733dd5c225SAndreas Gohr * Output preformatted text 5743dd5c225SAndreas Gohr * 5753dd5c225SAndreas Gohr * @param string $text 5763dd5c225SAndreas Gohr */ 5773d491f75SAndreas Gohr function preformatted($text) { 578c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 5793d491f75SAndreas Gohr } 5803d491f75SAndreas Gohr 5813dd5c225SAndreas Gohr /** 5823dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 5833dd5c225SAndreas Gohr * 5843dd5c225SAndreas Gohr * @param string $text text to show 5853dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 5863dd5c225SAndreas Gohr * @param string $filename file path label 5873dd5c225SAndreas Gohr */ 5883d491f75SAndreas Gohr function file($text, $language = null, $filename = null) { 5893d491f75SAndreas Gohr $this->_highlight('file', $text, $language, $filename); 5903d491f75SAndreas Gohr } 5913d491f75SAndreas Gohr 5923dd5c225SAndreas Gohr /** 5933dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 5943dd5c225SAndreas Gohr * 5953dd5c225SAndreas Gohr * @param string $text text to show 5963dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 5973dd5c225SAndreas Gohr * @param string $filename file path label 5983dd5c225SAndreas Gohr */ 5993d491f75SAndreas Gohr function code($text, $language = null, $filename = null) { 6003d491f75SAndreas Gohr $this->_highlight('code', $text, $language, $filename); 6013d491f75SAndreas Gohr } 6023d491f75SAndreas Gohr 6030cecf9d5Sandi /** 6043d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6053fd0b676Sandi * 6063fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6073dd5c225SAndreas Gohr * @param string $type code|file 6083dd5c225SAndreas Gohr * @param string $text text to show 6093dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6103dd5c225SAndreas Gohr * @param string $filename file path label 6110cecf9d5Sandi */ 6123d491f75SAndreas Gohr function _highlight($type, $text, $language = null, $filename = null) { 6133d491f75SAndreas Gohr global $ID; 6143d491f75SAndreas Gohr global $lang; 6153d491f75SAndreas Gohr 6163d491f75SAndreas Gohr if($filename) { 617190c56e8SAndreas Gohr // add icon 61827bf7924STom N Harris list($ext) = mimetype($filename, false); 619190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 620190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 621190c56e8SAndreas Gohr 6223d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 623190c56e8SAndreas Gohr $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6243d491f75SAndreas Gohr $this->doc .= hsc($filename); 6253d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6263d491f75SAndreas Gohr } 6270cecf9d5Sandi 628d43aac1cSGina Haeussge if($text{0} == "\n") { 629d43aac1cSGina Haeussge $text = substr($text, 1); 630d43aac1cSGina Haeussge } 631d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 632d43aac1cSGina Haeussge $text = substr($text, 0, -1); 633d43aac1cSGina Haeussge } 634d43aac1cSGina Haeussge 6350cecf9d5Sandi if(is_null($language)) { 6363d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6370cecf9d5Sandi } else { 6383d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6393d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6403d491f75SAndreas Gohr 6413d491f75SAndreas Gohr $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '').'</pre>'.DOKU_LF; 6420cecf9d5Sandi } 6433d491f75SAndreas Gohr 6443d491f75SAndreas Gohr if($filename) { 6453d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 6463d491f75SAndreas Gohr } 6473d491f75SAndreas Gohr 6483d491f75SAndreas Gohr $this->_codeblock++; 6490cecf9d5Sandi } 6500cecf9d5Sandi 6513dd5c225SAndreas Gohr /** 6523dd5c225SAndreas Gohr * Format an acronym 6533dd5c225SAndreas Gohr * 6543dd5c225SAndreas Gohr * Uses $this->acronyms 6553dd5c225SAndreas Gohr * 6563dd5c225SAndreas Gohr * @param string $acronym 6573dd5c225SAndreas Gohr */ 6580cecf9d5Sandi function acronym($acronym) { 6590cecf9d5Sandi 6600cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 6610cecf9d5Sandi 662433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 6630cecf9d5Sandi 664940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 665940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 6660cecf9d5Sandi 6670cecf9d5Sandi } else { 668a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 6690cecf9d5Sandi } 6700cecf9d5Sandi } 6710cecf9d5Sandi 6723dd5c225SAndreas Gohr /** 6733dd5c225SAndreas Gohr * Format a smiley 6743dd5c225SAndreas Gohr * 6753dd5c225SAndreas Gohr * Uses $this->smiley 6763dd5c225SAndreas Gohr * 6773dd5c225SAndreas Gohr * @param string $smiley 6783dd5c225SAndreas Gohr */ 6790cecf9d5Sandi function smiley($smiley) { 6800cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 681f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 6828e38227fSAnika Henke '" class="icon" alt="'. 683433bef32Sandi $this->_xmlEntities($smiley).'" />'; 6840cecf9d5Sandi } else { 685a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 6860cecf9d5Sandi } 6870cecf9d5Sandi } 6880cecf9d5Sandi 6893dd5c225SAndreas Gohr /** 6903dd5c225SAndreas Gohr * Format an entity 6913dd5c225SAndreas Gohr * 6923dd5c225SAndreas Gohr * Entities are basically small text replacements 6933dd5c225SAndreas Gohr * 6943dd5c225SAndreas Gohr * Uses $this->entities 6953dd5c225SAndreas Gohr * 6963dd5c225SAndreas Gohr * @param string $entity 6974de671bcSandi */ 6980cecf9d5Sandi function entity($entity) { 6990cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 700a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7010cecf9d5Sandi } else { 702a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7030cecf9d5Sandi } 7040cecf9d5Sandi } 7050cecf9d5Sandi 7063dd5c225SAndreas Gohr /** 7073dd5c225SAndreas Gohr * Typographically format a multiply sign 7083dd5c225SAndreas Gohr * 7093dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7103dd5c225SAndreas Gohr * 7113dd5c225SAndreas Gohr * @param string|int $x first value 7123dd5c225SAndreas Gohr * @param string|int $y second value 7133dd5c225SAndreas Gohr */ 7140cecf9d5Sandi function multiplyentity($x, $y) { 715a2d649c4Sandi $this->doc .= "$x×$y"; 7160cecf9d5Sandi } 7170cecf9d5Sandi 7183dd5c225SAndreas Gohr /** 7193dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7203dd5c225SAndreas Gohr */ 7210cecf9d5Sandi function singlequoteopening() { 72271b40da2SAnika Henke global $lang; 72371b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7240cecf9d5Sandi } 7250cecf9d5Sandi 7263dd5c225SAndreas Gohr /** 7273dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7283dd5c225SAndreas Gohr */ 7290cecf9d5Sandi function singlequoteclosing() { 73071b40da2SAnika Henke global $lang; 73171b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7320cecf9d5Sandi } 7330cecf9d5Sandi 7343dd5c225SAndreas Gohr /** 7353dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7363dd5c225SAndreas Gohr */ 73757d757d1SAndreas Gohr function apostrophe() { 73857d757d1SAndreas Gohr global $lang; 739a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 74057d757d1SAndreas Gohr } 74157d757d1SAndreas Gohr 7423dd5c225SAndreas Gohr /** 7433dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 7443dd5c225SAndreas Gohr */ 7450cecf9d5Sandi function doublequoteopening() { 74671b40da2SAnika Henke global $lang; 74771b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 7480cecf9d5Sandi } 7490cecf9d5Sandi 7503dd5c225SAndreas Gohr /** 7513dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 7523dd5c225SAndreas Gohr */ 7530cecf9d5Sandi function doublequoteclosing() { 75471b40da2SAnika Henke global $lang; 75571b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 7560cecf9d5Sandi } 7570cecf9d5Sandi 7580cecf9d5Sandi /** 7593dd5c225SAndreas Gohr * Render a CamelCase link 7603dd5c225SAndreas Gohr * 7613dd5c225SAndreas Gohr * @param string $link The link name 7623dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 7630cecf9d5Sandi */ 7640cecf9d5Sandi function camelcaselink($link) { 76511d0aa47Sandi $this->internallink($link, $link); 7660cecf9d5Sandi } 7670cecf9d5Sandi 7683dd5c225SAndreas Gohr /** 7693dd5c225SAndreas Gohr * Render a page local link 7703dd5c225SAndreas Gohr * 7713dd5c225SAndreas Gohr * @param string $hash hash link identifier 7723dd5c225SAndreas Gohr * @param string $name name for the link 7733dd5c225SAndreas Gohr */ 7740ea51e63SMatt Perry function locallink($hash, $name = null) { 7750b7c14c2Sandi global $ID; 7760b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 7770b7c14c2Sandi $hash = $this->_headerToLink($hash); 778e260f93bSAnika Henke $title = $ID.' ↵'; 7790b7c14c2Sandi $this->doc .= '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 7800b7c14c2Sandi $this->doc .= $name; 7810b7c14c2Sandi $this->doc .= '</a>'; 7820b7c14c2Sandi } 7830b7c14c2Sandi 784cffcc403Sandi /** 7853fd0b676Sandi * Render an internal Wiki Link 7863fd0b676Sandi * 787fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 788cffcc403Sandi * elsewhere - no need to implement them in other renderers 7893fd0b676Sandi * 7903dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 791f23eef27SGerrit Uitslag * @param string $id pageid 792f23eef27SGerrit Uitslag * @param string|null $name link name 793f23eef27SGerrit Uitslag * @param string|null $search adds search url param 794f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 795f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 796f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 797cffcc403Sandi */ 7980ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 799ba11bd29Sandi global $conf; 80037e34a5eSandi global $ID; 801c4dda6afSAnika Henke global $INFO; 80244653a53SAdrian Lang 8033d5e07d9SAdrian Lang $params = ''; 8043d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8053d5e07d9SAdrian Lang if(count($parts) === 2) { 8063d5e07d9SAdrian Lang $id = $parts[0]; 8073d5e07d9SAdrian Lang $params = $parts[1]; 80844653a53SAdrian Lang } 80944653a53SAdrian Lang 810fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 811fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 812fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 813fda14ffcSIzidor Matušov // (some things could be lost) 814fda14ffcSIzidor Matušov if($id === '') { 815fda14ffcSIzidor Matušov $id = $ID; 816fda14ffcSIzidor Matušov } 817fda14ffcSIzidor Matušov 8180339c872Sjan // default name is based on $id as given 8190339c872Sjan $default = $this->_simpleTitle($id); 820ad32e47eSAndreas Gohr 8210339c872Sjan // now first resolve and clean up the $id 82290bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 823fda14ffcSIzidor Matušov 824fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8250e1c636eSandi if(!$isImage) { 8260e1c636eSandi if($exists) { 827ba11bd29Sandi $class = 'wikilink1'; 8280cecf9d5Sandi } else { 829ba11bd29Sandi $class = 'wikilink2'; 83044a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 8310cecf9d5Sandi } 8320cecf9d5Sandi } else { 833ba11bd29Sandi $class = 'media'; 8340cecf9d5Sandi } 8350cecf9d5Sandi 836a1685bedSandi //keep hash anchor 8376d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 838943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 839a1685bedSandi 840ba11bd29Sandi //prepare for formating 841ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 842ba11bd29Sandi $link['style'] = ''; 843ba11bd29Sandi $link['pre'] = ''; 844ba11bd29Sandi $link['suf'] = ''; 84540eb54bbSjan // highlight link to current page 846c4dda6afSAnika Henke if($id == $INFO['id']) { 84792795d04Sandi $link['pre'] = '<span class="curid">'; 84892795d04Sandi $link['suf'] = '</span>'; 84940eb54bbSjan } 8505e163278SAndreas Gohr $link['more'] = ''; 851ba11bd29Sandi $link['class'] = $class; 8525c2eed9aSlisps if($this->date_at) { 8535c2eed9aSlisps $params['at'] = $this->date_at; 8545c2eed9aSlisps } 85544653a53SAdrian Lang $link['url'] = wl($id, $params); 856ba11bd29Sandi $link['name'] = $name; 857ba11bd29Sandi $link['title'] = $id; 858723d78dbSandi //add search string 859723d78dbSandi if($search) { 860546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 861546d3a99SAndreas Gohr if(is_array($search)) { 862546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 863546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 864546d3a99SAndreas Gohr } else { 865546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 866546d3a99SAndreas Gohr } 867723d78dbSandi } 868723d78dbSandi 869a1685bedSandi //keep hash 870a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 871a1685bedSandi 872ba11bd29Sandi //output formatted 873cffcc403Sandi if($returnonly) { 874cffcc403Sandi return $this->_formatLink($link); 875cffcc403Sandi } else { 876a2d649c4Sandi $this->doc .= $this->_formatLink($link); 8770cecf9d5Sandi } 878cffcc403Sandi } 8790cecf9d5Sandi 8803dd5c225SAndreas Gohr /** 8813dd5c225SAndreas Gohr * Render an external link 8823dd5c225SAndreas Gohr * 8833dd5c225SAndreas Gohr * @param string $url full URL with scheme 8843dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 8853dd5c225SAndreas Gohr */ 8860ea51e63SMatt Perry function externallink($url, $name = null) { 887b625487dSandi global $conf; 8880cecf9d5Sandi 889433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 8906f0c5dbfSandi 891b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 892b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 893b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 894b52b1596SAndreas Gohr $scheme = strtolower($scheme); 895b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 896b52b1596SAndreas Gohr 897b52b1596SAndreas Gohr // is there still an URL? 898b52b1596SAndreas Gohr if(!$url) { 899b52b1596SAndreas Gohr $this->doc .= $name; 900b52b1596SAndreas Gohr return; 901b52b1596SAndreas Gohr } 902b52b1596SAndreas Gohr 903b52b1596SAndreas Gohr // set class 9040cecf9d5Sandi if(!$isImage) { 905b625487dSandi $class = 'urlextern'; 9060cecf9d5Sandi } else { 907b625487dSandi $class = 'media'; 9080cecf9d5Sandi } 9090cecf9d5Sandi 910b625487dSandi //prepare for formating 911b625487dSandi $link['target'] = $conf['target']['extern']; 912b625487dSandi $link['style'] = ''; 913b625487dSandi $link['pre'] = ''; 914b625487dSandi $link['suf'] = ''; 9155e163278SAndreas Gohr $link['more'] = ''; 916b625487dSandi $link['class'] = $class; 917b625487dSandi $link['url'] = $url; 918e1c10e4dSchris 919b625487dSandi $link['name'] = $name; 920433bef32Sandi $link['title'] = $this->_xmlEntities($url); 921b625487dSandi if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 9220cecf9d5Sandi 923b625487dSandi //output formatted 924a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9250cecf9d5Sandi } 9260cecf9d5Sandi 9270cecf9d5Sandi /** 9283dd5c225SAndreas Gohr * Render an interwiki link 9293dd5c225SAndreas Gohr * 9303dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 9313dd5c225SAndreas Gohr * 9323dd5c225SAndreas Gohr * @param string $match original link - probably not much use 9333dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 9343dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 9353dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 9360cecf9d5Sandi */ 9370ea51e63SMatt Perry function interwikilink($match, $name = null, $wikiName, $wikiUri) { 938b625487dSandi global $conf; 9390cecf9d5Sandi 94097a3e4e3Sandi $link = array(); 94197a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 94297a3e4e3Sandi $link['pre'] = ''; 94397a3e4e3Sandi $link['suf'] = ''; 9445e163278SAndreas Gohr $link['more'] = ''; 945433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 9460cecf9d5Sandi 94797a3e4e3Sandi //get interwiki URL 9486496c33fSGerrit Uitslag $exists = null; 9496496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 9500cecf9d5Sandi 95197a3e4e3Sandi if(!$isImage) { 9529d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 9539d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 9541c2d1019SAndreas Gohr } else { 9551c2d1019SAndreas Gohr $link['class'] = 'media'; 95697a3e4e3Sandi } 9570cecf9d5Sandi 95897a3e4e3Sandi //do we stay at the same server? Use local target 9592345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 96097a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 96197a3e4e3Sandi } 9626496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 9636496c33fSGerrit Uitslag if($exists) { 9646496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 9656496c33fSGerrit Uitslag } else { 9666496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 9676496c33fSGerrit Uitslag $link['rel'] = 'nofollow'; 9686496c33fSGerrit Uitslag } 9696496c33fSGerrit Uitslag } 9700cecf9d5Sandi 97197a3e4e3Sandi $link['url'] = $url; 97297a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 97397a3e4e3Sandi 97497a3e4e3Sandi //output formatted 975a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9760cecf9d5Sandi } 9770cecf9d5Sandi 9780cecf9d5Sandi /** 9793dd5c225SAndreas Gohr * Link to windows share 9803dd5c225SAndreas Gohr * 9813dd5c225SAndreas Gohr * @param string $url the link 9823dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 9830cecf9d5Sandi */ 9840ea51e63SMatt Perry function windowssharelink($url, $name = null) { 9851d47afe1Sandi global $conf; 9863dd5c225SAndreas Gohr 9871d47afe1Sandi //simple setup 9881d47afe1Sandi $link['target'] = $conf['target']['windows']; 9891d47afe1Sandi $link['pre'] = ''; 9901d47afe1Sandi $link['suf'] = ''; 9911d47afe1Sandi $link['style'] = ''; 9920cecf9d5Sandi 993433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 9940cecf9d5Sandi if(!$isImage) { 9951d47afe1Sandi $link['class'] = 'windows'; 9960cecf9d5Sandi } else { 9971d47afe1Sandi $link['class'] = 'media'; 9980cecf9d5Sandi } 9990cecf9d5Sandi 1000433bef32Sandi $link['title'] = $this->_xmlEntities($url); 10011d47afe1Sandi $url = str_replace('\\', '/', $url); 1002*83b7e38dSMichael Große $url = ltrim($url,'/'); 10031d47afe1Sandi $url = 'file:///'.$url; 10041d47afe1Sandi $link['url'] = $url; 10050cecf9d5Sandi 10061d47afe1Sandi //output formatted 1007a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10080cecf9d5Sandi } 10090cecf9d5Sandi 10103dd5c225SAndreas Gohr /** 10113dd5c225SAndreas Gohr * Render a linked E-Mail Address 10123dd5c225SAndreas Gohr * 10133dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 10143dd5c225SAndreas Gohr * 10153dd5c225SAndreas Gohr * @param string $address Email-Address 10163dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 10173dd5c225SAndreas Gohr */ 10180ea51e63SMatt Perry function emaillink($address, $name = null) { 101971352defSandi global $conf; 102071352defSandi //simple setup 102171352defSandi $link = array(); 102271352defSandi $link['target'] = ''; 102371352defSandi $link['pre'] = ''; 102471352defSandi $link['suf'] = ''; 102571352defSandi $link['style'] = ''; 102671352defSandi $link['more'] = ''; 10270cecf9d5Sandi 1028c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 10290cecf9d5Sandi if(!$isImage) { 1030be96545cSAnika Henke $link['class'] = 'mail'; 10310cecf9d5Sandi } else { 1032be96545cSAnika Henke $link['class'] = 'media'; 10330cecf9d5Sandi } 10340cecf9d5Sandi 103507738714SAndreas Gohr $address = $this->_xmlEntities($address); 103600a7b5adSEsther Brunner $address = obfuscate($address); 103700a7b5adSEsther Brunner $title = $address; 10388c128049SAndreas Gohr 103971352defSandi if(empty($name)) { 104000a7b5adSEsther Brunner $name = $address; 104171352defSandi } 10420cecf9d5Sandi 1043776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1044776b36ecSAndreas Gohr 1045776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 104671352defSandi $link['name'] = $name; 104771352defSandi $link['title'] = $title; 10480cecf9d5Sandi 104971352defSandi //output formatted 1050a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10510cecf9d5Sandi } 10520cecf9d5Sandi 10533dd5c225SAndreas Gohr /** 10543dd5c225SAndreas Gohr * Render an internal media file 10553dd5c225SAndreas Gohr * 10563dd5c225SAndreas Gohr * @param string $src media ID 10573dd5c225SAndreas Gohr * @param string $title descriptive text 10583dd5c225SAndreas Gohr * @param string $align left|center|right 10593dd5c225SAndreas Gohr * @param int $width width of media in pixel 10603dd5c225SAndreas Gohr * @param int $height height of media in pixel 10613dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 10623dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 10633dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 10643dd5c225SAndreas Gohr * @return void|string 10653dd5c225SAndreas Gohr */ 10660ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 10673dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 106837e34a5eSandi global $ID; 106991df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1070cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 10710cecf9d5Sandi 1072d98d4540SBen Coburn $noLink = false; 10738acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1074b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 10753685f775Sandi 10763dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1077b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 107852dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct')); 1079f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 10802a2a2ba2SAnika Henke // don't link movies 108144881bd0Shenning.noren $noLink = true; 108255efc227SAndreas Gohr } else { 10832ca14335SEsther Brunner // add file icons 10849d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 10859d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 108652dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true); 108791328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 108855efc227SAndreas Gohr } 10893685f775Sandi 109091df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 109191df343aSAndreas Gohr 10926fe20453SGina Haeussge //markup non existing files 10934a24b459SKate Arzamastseva if(!$exists) { 10946fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 10954a24b459SKate Arzamastseva } 10966fe20453SGina Haeussge 10973685f775Sandi //output formatted 1098f50634f0SAnika Henke if($return) { 1099f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1100f50634f0SAnika Henke else return $this->_formatLink($link); 1101f50634f0SAnika Henke } else { 1102dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 11032ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 11040cecf9d5Sandi } 1105f50634f0SAnika Henke } 11060cecf9d5Sandi 11073dd5c225SAndreas Gohr /** 11083dd5c225SAndreas Gohr * Render an external media file 11093dd5c225SAndreas Gohr * 11103dd5c225SAndreas Gohr * @param string $src full media URL 11113dd5c225SAndreas Gohr * @param string $title descriptive text 11123dd5c225SAndreas Gohr * @param string $align left|center|right 11133dd5c225SAndreas Gohr * @param int $width width of media in pixel 11143dd5c225SAndreas Gohr * @param int $height height of media in pixel 11153dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11163dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1117410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 11183dd5c225SAndreas Gohr */ 11190ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 1120410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 112191df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1122d98d4540SBen Coburn $noLink = false; 11238acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1124b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1125b739ff0fSPierre Spring 1126b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 11273685f775Sandi 11283dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1129b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 11302ca14335SEsther Brunner // link only jpeg images 113144881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1132f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11332a2a2ba2SAnika Henke // don't link movies 113444881bd0Shenning.noren $noLink = true; 11352ca14335SEsther Brunner } else { 11362ca14335SEsther Brunner // add file icons 113727bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 113827bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 11392ca14335SEsther Brunner } 11402ca14335SEsther Brunner 114191df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 114291df343aSAndreas Gohr 11433685f775Sandi //output formatted 1144410ee62aSAnika Henke if($return) { 1145410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1146410ee62aSAnika Henke else return $this->_formatLink($link); 1147410ee62aSAnika Henke } else { 1148dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 11492ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 11500cecf9d5Sandi } 1151410ee62aSAnika Henke } 11520cecf9d5Sandi 11534826ab45Sandi /** 11543db95becSAndreas Gohr * Renders an RSS feed 1155b625487dSandi * 1156b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1157b625487dSandi */ 11583db95becSAndreas Gohr function rss($url, $params) { 1159b625487dSandi global $lang; 11603db95becSAndreas Gohr global $conf; 11613db95becSAndreas Gohr 11623db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 11633db95becSAndreas Gohr $feed = new FeedParser(); 116400077af8SAndreas Gohr $feed->set_feed_url($url); 1165b625487dSandi 1166b625487dSandi //disable warning while fetching 11673dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 11683dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 11693dd5c225SAndreas Gohr } 11703db95becSAndreas Gohr $rc = $feed->init(); 11713dd5c225SAndreas Gohr if(isset($elvl)) { 11723dd5c225SAndreas Gohr error_reporting($elvl); 11733dd5c225SAndreas Gohr } 1174b625487dSandi 11753db95becSAndreas Gohr //decide on start and end 11763db95becSAndreas Gohr if($params['reverse']) { 11773db95becSAndreas Gohr $mod = -1; 11783db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 11793db95becSAndreas Gohr $end = $start - ($params['max']); 1180b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 11813db95becSAndreas Gohr } else { 11823db95becSAndreas Gohr $mod = 1; 11833db95becSAndreas Gohr $start = 0; 11843db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1185d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 11863db95becSAndreas Gohr } 11873db95becSAndreas Gohr 1188a2d649c4Sandi $this->doc .= '<ul class="rss">'; 11893db95becSAndreas Gohr if($rc) { 11903db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 11911bde1582SAndreas Gohr $item = $feed->get_item($x); 11923db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1193d2ea3363SAndreas Gohr // support feeds without links 1194d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1195d2ea3363SAndreas Gohr if($lnkurl) { 1196793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1197793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 11983dd5c225SAndreas Gohr $this->externallink( 11993dd5c225SAndreas Gohr $item->get_permalink(), 12003dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 12013dd5c225SAndreas Gohr ); 1202d2ea3363SAndreas Gohr } else { 1203d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1204d2ea3363SAndreas Gohr } 12053db95becSAndreas Gohr if($params['author']) { 12061bde1582SAndreas Gohr $author = $item->get_author(0); 12071bde1582SAndreas Gohr if($author) { 12081bde1582SAndreas Gohr $name = $author->get_name(); 12091bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 12101bde1582SAndreas Gohr if($name) $this->doc .= ' '.$lang['by'].' '.$name; 12111bde1582SAndreas Gohr } 12123db95becSAndreas Gohr } 12133db95becSAndreas Gohr if($params['date']) { 12142e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 12153db95becSAndreas Gohr } 12161bde1582SAndreas Gohr if($params['details']) { 12173db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1218173dccb7STom N Harris if($conf['htmlok']) { 12191bde1582SAndreas Gohr $this->doc .= $item->get_description(); 12203db95becSAndreas Gohr } else { 12211bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 12223db95becSAndreas Gohr } 12233db95becSAndreas Gohr $this->doc .= '</div>'; 12243db95becSAndreas Gohr } 12253db95becSAndreas Gohr 12263db95becSAndreas Gohr $this->doc .= '</div></li>'; 1227b625487dSandi } 1228b625487dSandi } else { 12293db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1230a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1231b625487dSandi $this->externallink($url); 123245e147ccSAndreas Gohr if($conf['allowdebug']) { 123345e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 123445e147ccSAndreas Gohr } 12353db95becSAndreas Gohr $this->doc .= '</div></li>'; 1236b625487dSandi } 1237a2d649c4Sandi $this->doc .= '</ul>'; 1238b625487dSandi } 1239b625487dSandi 12403dd5c225SAndreas Gohr /** 12413dd5c225SAndreas Gohr * Start a table 12423dd5c225SAndreas Gohr * 12433dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 12443dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 12453dd5c225SAndreas Gohr * @param int $pos byte position in the original source 12463dd5c225SAndreas Gohr */ 1247619736fdSAdrian Lang function table_open($maxcols = null, $numrows = null, $pos = null) { 1248b5742cedSPierre Spring // initialize the row counter used for classes 1249b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1250619736fdSAdrian Lang $class = 'table'; 1251619736fdSAdrian Lang if($pos !== null) { 1252619736fdSAdrian Lang $class .= ' '.$this->startSectionEdit($pos, 'table'); 1253619736fdSAdrian Lang } 1254619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1255619736fdSAdrian Lang DOKU_LF; 12560cecf9d5Sandi } 12570cecf9d5Sandi 12583dd5c225SAndreas Gohr /** 12593dd5c225SAndreas Gohr * Close a table 12603dd5c225SAndreas Gohr * 12613dd5c225SAndreas Gohr * @param int $pos byte position in the original source 12623dd5c225SAndreas Gohr */ 1263619736fdSAdrian Lang function table_close($pos = null) { 1264a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1265619736fdSAdrian Lang if($pos !== null) { 126690df9a4dSAdrian Lang $this->finishSectionEdit($pos); 12670cecf9d5Sandi } 1268619736fdSAdrian Lang } 12690cecf9d5Sandi 12703dd5c225SAndreas Gohr /** 12713dd5c225SAndreas Gohr * Open a table header 12723dd5c225SAndreas Gohr */ 1273f05a1cc5SGerrit Uitslag function tablethead_open() { 1274f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1275f05a1cc5SGerrit Uitslag } 1276f05a1cc5SGerrit Uitslag 12773dd5c225SAndreas Gohr /** 12783dd5c225SAndreas Gohr * Close a table header 12793dd5c225SAndreas Gohr */ 1280f05a1cc5SGerrit Uitslag function tablethead_close() { 1281f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1282f05a1cc5SGerrit Uitslag } 1283f05a1cc5SGerrit Uitslag 12843dd5c225SAndreas Gohr /** 12853dd5c225SAndreas Gohr * Open a table row 12863dd5c225SAndreas Gohr */ 12870cecf9d5Sandi function tablerow_open() { 1288b5742cedSPierre Spring // initialize the cell counter used for classes 1289b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1290b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 1291b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 12920cecf9d5Sandi } 12930cecf9d5Sandi 12943dd5c225SAndreas Gohr /** 12953dd5c225SAndreas Gohr * Close a table row 12963dd5c225SAndreas Gohr */ 12970cecf9d5Sandi function tablerow_close() { 1298a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 12990cecf9d5Sandi } 13000cecf9d5Sandi 13013dd5c225SAndreas Gohr /** 13023dd5c225SAndreas Gohr * Open a table header cell 13033dd5c225SAndreas Gohr * 13043dd5c225SAndreas Gohr * @param int $colspan 13053dd5c225SAndreas Gohr * @param string $align left|center|right 13063dd5c225SAndreas Gohr * @param int $rowspan 13073dd5c225SAndreas Gohr */ 13080ea51e63SMatt Perry function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 1309b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 13100cecf9d5Sandi if(!is_null($align)) { 1311b5742cedSPierre Spring $class .= ' '.$align.'align'; 13120cecf9d5Sandi } 1313b5742cedSPierre Spring $class .= '"'; 1314b5742cedSPierre Spring $this->doc .= '<th '.$class; 13150cecf9d5Sandi if($colspan > 1) { 1316a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1317a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 13180cecf9d5Sandi } 131925b97867Shakan.sandell if($rowspan > 1) { 132025b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 132125b97867Shakan.sandell } 1322a2d649c4Sandi $this->doc .= '>'; 13230cecf9d5Sandi } 13240cecf9d5Sandi 13253dd5c225SAndreas Gohr /** 13263dd5c225SAndreas Gohr * Close a table header cell 13273dd5c225SAndreas Gohr */ 13280cecf9d5Sandi function tableheader_close() { 1329a2d649c4Sandi $this->doc .= '</th>'; 13300cecf9d5Sandi } 13310cecf9d5Sandi 13323dd5c225SAndreas Gohr /** 13333dd5c225SAndreas Gohr * Open a table cell 13343dd5c225SAndreas Gohr * 13353dd5c225SAndreas Gohr * @param int $colspan 13363dd5c225SAndreas Gohr * @param string $align left|center|right 13373dd5c225SAndreas Gohr * @param int $rowspan 13383dd5c225SAndreas Gohr */ 13390ea51e63SMatt Perry function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 1340b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 13410cecf9d5Sandi if(!is_null($align)) { 1342b5742cedSPierre Spring $class .= ' '.$align.'align'; 13430cecf9d5Sandi } 1344b5742cedSPierre Spring $class .= '"'; 1345b5742cedSPierre Spring $this->doc .= '<td '.$class; 13460cecf9d5Sandi if($colspan > 1) { 1347a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1348a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 13490cecf9d5Sandi } 135025b97867Shakan.sandell if($rowspan > 1) { 135125b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 135225b97867Shakan.sandell } 1353a2d649c4Sandi $this->doc .= '>'; 13540cecf9d5Sandi } 13550cecf9d5Sandi 13563dd5c225SAndreas Gohr /** 13573dd5c225SAndreas Gohr * Close a table cell 13583dd5c225SAndreas Gohr */ 13590cecf9d5Sandi function tablecell_close() { 1360a2d649c4Sandi $this->doc .= '</td>'; 13610cecf9d5Sandi } 13620cecf9d5Sandi 13633dd5c225SAndreas Gohr #region Utility functions 13640cecf9d5Sandi 1365ba11bd29Sandi /** 13663fd0b676Sandi * Build a link 13673fd0b676Sandi * 13683fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1369ba11bd29Sandi * 1370ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1371ba11bd29Sandi */ 1372433bef32Sandi function _formatLink($link) { 1373ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1374ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1375ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1376ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1377ba11bd29Sandi } 1378ba11bd29Sandi //remove double encodings in titles 1379ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1380ba11bd29Sandi 1381453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1382453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1383453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1384453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1385453493f2SAndreas Gohr 1386ba11bd29Sandi $ret = ''; 1387ba11bd29Sandi $ret .= $link['pre']; 1388ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1389bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1390bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1391bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1392bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 139344a6b4c7SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.$link['rel'].'"'; 1394bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1395ba11bd29Sandi $ret .= '>'; 1396ba11bd29Sandi $ret .= $link['name']; 1397ba11bd29Sandi $ret .= '</a>'; 1398ba11bd29Sandi $ret .= $link['suf']; 1399ba11bd29Sandi return $ret; 1400ba11bd29Sandi } 1401ba11bd29Sandi 1402ba11bd29Sandi /** 14033fd0b676Sandi * Renders internal and external media 14043fd0b676Sandi * 14053fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 14063dd5c225SAndreas Gohr * @param string $src media ID 14073dd5c225SAndreas Gohr * @param string $title descriptive text 14083dd5c225SAndreas Gohr * @param string $align left|center|right 14093dd5c225SAndreas Gohr * @param int $width width of media in pixel 14103dd5c225SAndreas Gohr * @param int $height height of media in pixel 14113dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 14123dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 14133dd5c225SAndreas Gohr * @return string 14143fd0b676Sandi */ 14150ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 14160ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 14173fd0b676Sandi 14183fd0b676Sandi $ret = ''; 14193fd0b676Sandi 14203dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 14213fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1422b739ff0fSPierre Spring // first get the $title 1423b739ff0fSPierre Spring if(!is_null($title)) { 1424b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1425b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1426b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1427b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 142867f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1429b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 14303dd5c225SAndreas Gohr if(!empty($cap)) { 1431b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1432b739ff0fSPierre Spring } 1433b739ff0fSPierre Spring } 1434b739ff0fSPierre Spring if(!$render) { 1435b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1436b739ff0fSPierre Spring // return the title of the picture 1437b739ff0fSPierre Spring if(!$title) { 1438b739ff0fSPierre Spring // just show the sourcename 14393009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1440b739ff0fSPierre Spring } 1441b739ff0fSPierre Spring return $title; 1442b739ff0fSPierre Spring } 14433fd0b676Sandi //add image tag 144452dc5eadSlisps $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"'; 14453fd0b676Sandi $ret .= ' class="media'.$align.'"'; 14463fd0b676Sandi 1447b739ff0fSPierre Spring if($title) { 1448b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1449b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 14503fd0b676Sandi } else { 14513fd0b676Sandi $ret .= ' alt=""'; 14523fd0b676Sandi } 14533fd0b676Sandi 14543fd0b676Sandi if(!is_null($width)) 14553fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 14563fd0b676Sandi 14573fd0b676Sandi if(!is_null($height)) 14583fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 14593fd0b676Sandi 14603fd0b676Sandi $ret .= ' />'; 14613fd0b676Sandi 146217954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 14632a2a2ba2SAnika Henke // first get the $title 146417954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 14652a2a2ba2SAnika Henke if(!$render) { 146617954bb5SAnika Henke // if the file is not supposed to be rendered 146717954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 146817954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 14692a2a2ba2SAnika Henke } 14702a2a2ba2SAnika Henke 14712a2a2ba2SAnika Henke $att = array(); 14722a2a2ba2SAnika Henke $att['class'] = "media$align"; 147317954bb5SAnika Henke if($title) { 147417954bb5SAnika Henke $att['title'] = $title; 147517954bb5SAnika Henke } 14762a2a2ba2SAnika Henke 147717954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 147817954bb5SAnika Henke //add video 147979e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1480b44a5dceSAnika Henke } 148117954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1482b44a5dceSAnika Henke //add audio 1483b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 148417954bb5SAnika Henke } 1485b44a5dceSAnika Henke 14863fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 14871c882ba8SAndreas Gohr if(!$render) { 14881c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 14891c882ba8SAndreas Gohr // return the title of the flash 14901c882ba8SAndreas Gohr if(!$title) { 14911c882ba8SAndreas Gohr // just show the sourcename 14923009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 14931c882ba8SAndreas Gohr } 149407bf32b2SAndreas Gohr return $this->_xmlEntities($title); 14951c882ba8SAndreas Gohr } 14961c882ba8SAndreas Gohr 149707bf32b2SAndreas Gohr $att = array(); 149807bf32b2SAndreas Gohr $att['class'] = "media$align"; 149907bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 150007bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 15013dd5c225SAndreas Gohr $ret .= html_flashobject( 15023dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 150307bf32b2SAndreas Gohr array('quality' => 'high'), 150407bf32b2SAndreas Gohr null, 150507bf32b2SAndreas Gohr $att, 15063dd5c225SAndreas Gohr $this->_xmlEntities($title) 15073dd5c225SAndreas Gohr ); 15080f428d7dSAndreas Gohr } elseif($title) { 15093fd0b676Sandi // well at least we have a title to display 15103fd0b676Sandi $ret .= $this->_xmlEntities($title); 15113fd0b676Sandi } else { 15125291ca3aSAndreas Gohr // just show the sourcename 15133009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 15143fd0b676Sandi } 15153fd0b676Sandi 15163fd0b676Sandi return $ret; 15173fd0b676Sandi } 15183fd0b676Sandi 15193dd5c225SAndreas Gohr /** 15203dd5c225SAndreas Gohr * Escape string for output 15213dd5c225SAndreas Gohr * 15223dd5c225SAndreas Gohr * @param $string 15233dd5c225SAndreas Gohr * @return string 15243dd5c225SAndreas Gohr */ 1525433bef32Sandi function _xmlEntities($string) { 1526de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 15270cecf9d5Sandi } 15280cecf9d5Sandi 15298a831f2bSAndreas Gohr /** 15308a831f2bSAndreas Gohr * Creates a linkid from a headline 1531c5a8fd96SAndreas Gohr * 15323dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1533c5a8fd96SAndreas Gohr * @param string $title The headline title 1534c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 15353dd5c225SAndreas Gohr * @return string 15368a831f2bSAndreas Gohr */ 1537c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1538c5a8fd96SAndreas Gohr if($create) { 15394ceab83fSAndreas Gohr return sectionID($title, $this->headers); 15404ceab83fSAndreas Gohr } else { 1541443d207bSAndreas Gohr $check = false; 1542443d207bSAndreas Gohr return sectionID($title, $check); 1543c5a8fd96SAndreas Gohr } 15440cecf9d5Sandi } 15450cecf9d5Sandi 1546af587fa8Sandi /** 15473fd0b676Sandi * Construct a title and handle images in titles 15483fd0b676Sandi * 15490b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 15503dd5c225SAndreas Gohr * @param string|array $title either string title or media array 15513dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 15523dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 15533dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 15543dd5c225SAndreas Gohr * @param string $linktype content|navigation 15553dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 15563fd0b676Sandi */ 15570ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 155844881bd0Shenning.noren $isImage = false; 155929657f9eSAndreas Gohr if(is_array($title)) { 156029657f9eSAndreas Gohr $isImage = true; 156129657f9eSAndreas Gohr return $this->_imageTitle($title); 156229657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1563fe9ec250SChris Smith if(useHeading($linktype) && $id) { 156467c15eceSMichael Hamann $heading = p_get_first_heading($id); 1565bb0a59d4Sjan if($heading) { 1566433bef32Sandi return $this->_xmlEntities($heading); 1567bb0a59d4Sjan } 1568bb0a59d4Sjan } 1569433bef32Sandi return $this->_xmlEntities($default); 157068c26e6dSMichael Klier } else { 157168c26e6dSMichael Klier return $this->_xmlEntities($title); 15720cecf9d5Sandi } 15730cecf9d5Sandi } 15740cecf9d5Sandi 15750cecf9d5Sandi /** 15763dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 15773fd0b676Sandi * 15783fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 15793dd5c225SAndreas Gohr * @param string $img 15803dd5c225SAndreas Gohr * @return string HTML img tag or similar 15810cecf9d5Sandi */ 1582433bef32Sandi function _imageTitle($img) { 1583d9baf1a7SKazutaka Miyasaka global $ID; 1584d9baf1a7SKazutaka Miyasaka 1585d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1586d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 15873dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1588d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1589cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1590d9baf1a7SKazutaka Miyasaka } 1591d9baf1a7SKazutaka Miyasaka 15923dd5c225SAndreas Gohr return $this->_media( 15933dd5c225SAndreas Gohr $img['src'], 15944826ab45Sandi $img['title'], 15954826ab45Sandi $img['align'], 15964826ab45Sandi $img['width'], 15974826ab45Sandi $img['height'], 15983dd5c225SAndreas Gohr $img['cache'] 15993dd5c225SAndreas Gohr ); 16000cecf9d5Sandi } 1601b739ff0fSPierre Spring 1602b739ff0fSPierre Spring /** 16033dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 16043dd5c225SAndreas Gohr * 16053dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1606b739ff0fSPierre Spring * 1607b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 16083dd5c225SAndreas Gohr * @param string $src media ID 16093dd5c225SAndreas Gohr * @param string $title descriptive text 16103dd5c225SAndreas Gohr * @param string $align left|center|right 16113dd5c225SAndreas Gohr * @param int $width width of media in pixel 16123dd5c225SAndreas Gohr * @param int $height height of media in pixel 16133dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 16143dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 16153dd5c225SAndreas Gohr * @return array associative array with link config 1616b739ff0fSPierre Spring */ 1617d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1618b739ff0fSPierre Spring global $conf; 1619b739ff0fSPierre Spring 1620b739ff0fSPierre Spring $link = array(); 1621b739ff0fSPierre Spring $link['class'] = 'media'; 1622b739ff0fSPierre Spring $link['style'] = ''; 1623b739ff0fSPierre Spring $link['pre'] = ''; 1624b739ff0fSPierre Spring $link['suf'] = ''; 1625b739ff0fSPierre Spring $link['more'] = ''; 1626b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1627b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1628b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1629b739ff0fSPierre Spring 1630b739ff0fSPierre Spring return $link; 1631b739ff0fSPierre Spring } 163291459163SAnika Henke 16332a2a2ba2SAnika Henke /** 16342a2a2ba2SAnika Henke * Embed video(s) in HTML 16352a2a2ba2SAnika Henke * 16362a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 16372a2a2ba2SAnika Henke * 16382a2a2ba2SAnika Henke * @param string $src - ID of video to embed 16392a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 16402a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 16412a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1642f50634f0SAnika Henke * @return string 16432a2a2ba2SAnika Henke */ 164479e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 16452a2a2ba2SAnika Henke // prepare width and height 16462a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 16472a2a2ba2SAnika Henke $atts['width'] = (int) $width; 16482a2a2ba2SAnika Henke $atts['height'] = (int) $height; 16492a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 16502a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 16512a2a2ba2SAnika Henke 1652410ee62aSAnika Henke $posterUrl = ''; 1653410ee62aSAnika Henke $files = array(); 1654410ee62aSAnika Henke $isExternal = media_isexternal($src); 1655410ee62aSAnika Henke 1656410ee62aSAnika Henke if ($isExternal) { 1657410ee62aSAnika Henke // take direct source for external files 1658702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1659410ee62aSAnika Henke $files[$srcMime] = $src; 1660410ee62aSAnika Henke } else { 16613d7a9e0aSAnika Henke // prepare alternative formats 16623d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1663410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 166499f943f6SAnika Henke $poster = media_alternativefiles($src, array('jpg', 'png'), true); 166599f943f6SAnika Henke if(!empty($poster)) { 16662d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 166799f943f6SAnika Henke } 1668410ee62aSAnika Henke } 16692a2a2ba2SAnika Henke 1670f50634f0SAnika Henke $out = ''; 167179e53fe5SAnika Henke // open video tag 1672f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 16733641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1674f50634f0SAnika Henke $out .= '>'.NL; 16753641199aSAnika Henke $fallback = ''; 167679e53fe5SAnika Henke 167779e53fe5SAnika Henke // output source for each alternative video format 1678410ee62aSAnika Henke foreach($files as $mime => $file) { 1679410ee62aSAnika Henke if ($isExternal) { 1680410ee62aSAnika Henke $url = $file; 1681410ee62aSAnika Henke $linkType = 'externalmedia'; 1682410ee62aSAnika Henke } else { 16832d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1684410ee62aSAnika Henke $linkType = 'internalmedia'; 1685410ee62aSAnika Henke } 168617954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 16873d7a9e0aSAnika Henke 1688f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 168979e53fe5SAnika Henke // alternative content (just a link to the file) 1690410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 16913d7a9e0aSAnika Henke } 16922a2a2ba2SAnika Henke 16932a2a2ba2SAnika Henke // finish 16943641199aSAnika Henke $out .= $fallback; 1695f50634f0SAnika Henke $out .= '</video>'.NL; 1696f50634f0SAnika Henke return $out; 16972a2a2ba2SAnika Henke } 16982a2a2ba2SAnika Henke 1699b44a5dceSAnika Henke /** 1700b44a5dceSAnika Henke * Embed audio in HTML 1701b44a5dceSAnika Henke * 1702b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1703b44a5dceSAnika Henke * 1704b44a5dceSAnika Henke * @param string $src - ID of audio to embed 17056d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1706f50634f0SAnika Henke * @return string 1707b44a5dceSAnika Henke */ 1708b44a5dceSAnika Henke function _audio($src, $atts = null) { 1709702e97d3SAnika Henke $files = array(); 1710410ee62aSAnika Henke $isExternal = media_isexternal($src); 1711b44a5dceSAnika Henke 1712410ee62aSAnika Henke if ($isExternal) { 1713410ee62aSAnika Henke // take direct source for external files 1714702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1715410ee62aSAnika Henke $files[$srcMime] = $src; 1716410ee62aSAnika Henke } else { 1717b44a5dceSAnika Henke // prepare alternative formats 1718b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1719410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1720410ee62aSAnika Henke } 1721b44a5dceSAnika Henke 1722f50634f0SAnika Henke $out = ''; 1723b44a5dceSAnika Henke // open audio tag 1724f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 17253641199aSAnika Henke $fallback = ''; 1726b44a5dceSAnika Henke 1727b44a5dceSAnika Henke // output source for each alternative audio format 1728410ee62aSAnika Henke foreach($files as $mime => $file) { 1729410ee62aSAnika Henke if ($isExternal) { 1730410ee62aSAnika Henke $url = $file; 1731410ee62aSAnika Henke $linkType = 'externalmedia'; 1732410ee62aSAnika Henke } else { 17332d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1734410ee62aSAnika Henke $linkType = 'internalmedia'; 1735410ee62aSAnika Henke } 173617954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1737b44a5dceSAnika Henke 1738f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1739b44a5dceSAnika Henke // alternative content (just a link to the file) 1740410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1741b44a5dceSAnika Henke } 1742b44a5dceSAnika Henke 1743b44a5dceSAnika Henke // finish 17443641199aSAnika Henke $out .= $fallback; 1745f50634f0SAnika Henke $out .= '</audio>'.NL; 1746f50634f0SAnika Henke return $out; 1747b44a5dceSAnika Henke } 1748b44a5dceSAnika Henke 17495c2eed9aSlisps /** 175052dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 17515c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 17525c2eed9aSlisps * 17535c2eed9aSlisps * @author lisps 17545c2eed9aSlisps * @param string $media_id 17555c2eed9aSlisps * @access protected 17565c2eed9aSlisps * @return string revision ('' for current) 17575c2eed9aSlisps */ 175852dc5eadSlisps function _getLastMediaRevisionAt($media_id){ 175952dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 176078b874e6Slisps $pagelog = new MediaChangeLog($media_id); 176178b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 17625c2eed9aSlisps } 17635c2eed9aSlisps 17643dd5c225SAndreas Gohr #endregion 17650cecf9d5Sandi} 17660cecf9d5Sandi 1767e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1768