10cecf9d5Sandi<?php 2b625487dSandi/** 3b625487dSandi * Renderer for XHTML output 4b625487dSandi * 5b4f2363aSAndreas Gohr * This is DokuWiki's main renderer used to display page content in the wiki 6b4f2363aSAndreas Gohr * 7b625487dSandi * @author Harry Fuecks <hfuecks@gmail.com> 8b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 93dd5c225SAndreas Gohr * 100cecf9d5Sandi */ 11ac83b9d8Sandiclass Doku_Renderer_xhtml extends Doku_Renderer { 123dd5c225SAndreas Gohr /** @var array store the table of contents */ 133dd5c225SAndreas Gohr public $toc = array(); 140cecf9d5Sandi 153dd5c225SAndreas Gohr /** @var array A stack of section edit data */ 163dd5c225SAndreas Gohr protected $sectionedits = array(); 17*de369923SAndreas Gohr 18*de369923SAndreas Gohr /** @var string|int link pages and media against this revision */ 19*de369923SAndreas Gohr public $date_at = ''; 20c5a8fd96SAndreas Gohr 213dd5c225SAndreas Gohr /** @var int last section edit id, used by startSectionEdit */ 223dd5c225SAndreas Gohr protected $lastsecid = 0; 230cecf9d5Sandi 2416ec3e37SAndreas Gohr /** @var array a list of footnotes, list starts at 1! */ 253dd5c225SAndreas Gohr protected $footnotes = array(); 267764a90aSandi 273dd5c225SAndreas Gohr /** @var int current section level */ 283dd5c225SAndreas Gohr protected $lastlevel = 0; 293dd5c225SAndreas Gohr /** @var array section node tracker */ 303dd5c225SAndreas Gohr protected $node = array(0, 0, 0, 0, 0); 313dd5c225SAndreas Gohr 323dd5c225SAndreas Gohr /** @var string temporary $doc store */ 333dd5c225SAndreas Gohr protected $store = ''; 343dd5c225SAndreas Gohr 353dd5c225SAndreas Gohr /** @var array global counter, for table classes etc. */ 363dd5c225SAndreas Gohr protected $_counter = array(); // 373dd5c225SAndreas Gohr 383dd5c225SAndreas Gohr /** @var int counts the code and file blocks, used to provide download links */ 393dd5c225SAndreas Gohr protected $_codeblock = 0; 403dd5c225SAndreas Gohr 413dd5c225SAndreas Gohr /** @var array list of allowed URL schemes */ 423dd5c225SAndreas Gohr protected $schemes = null; 43b5742cedSPierre Spring 4490df9a4dSAdrian Lang /** 4590df9a4dSAdrian Lang * Register a new edit section range 4690df9a4dSAdrian Lang * 4742ea7f44SGerrit Uitslag * @param int $start The byte position for the edit start 48ec57f119SLarsDW223 * @param array $data Associative array with section data: 49ec57f119SLarsDW223 * Key 'name': the section name/title 50ec57f119SLarsDW223 * Key 'target': the target for the section edit, 51ec57f119SLarsDW223 * e.g. 'section' or 'table' 52ec57f119SLarsDW223 * Key 'hid': header id 53ec57f119SLarsDW223 * Key 'codeblockOffset': actual code block index 54ec57f119SLarsDW223 * Key 'start': set in startSectionEdit(), 55ec57f119SLarsDW223 * do not set yourself 56ec57f119SLarsDW223 * Key 'range': calculated from 'start' and 57ec57f119SLarsDW223 * $key in finishSectionEdit(), 58ec57f119SLarsDW223 * do not set yourself 5990df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 6042ea7f44SGerrit Uitslag * 6190df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 6290df9a4dSAdrian Lang */ 63ec57f119SLarsDW223 public function startSectionEdit($start, $data) { 64ec57f119SLarsDW223 if (!is_array($data)) { 65ac025fdfSAndreas Gohr msg( 66ac025fdfSAndreas Gohr sprintf( 67ac025fdfSAndreas Gohr 'startSectionEdit: $data "%s" is NOT an array! One of your plugins needs an update.', 68ac025fdfSAndreas Gohr hsc((string) $data) 69ac025fdfSAndreas Gohr ), -1 70ac025fdfSAndreas Gohr ); 71ac025fdfSAndreas Gohr 72ac025fdfSAndreas Gohr // @deprecated 2018-04-14, backward compatibility 73ac025fdfSAndreas Gohr $args = func_get_args(); 74ac025fdfSAndreas Gohr $data = array(); 75ac025fdfSAndreas Gohr if(isset($args[1])) $data['target'] = $args[1]; 76ac025fdfSAndreas Gohr if(isset($args[2])) $data['name'] = $args[2]; 77ac025fdfSAndreas Gohr if(isset($args[3])) $data['hid'] = $args[3]; 78ec57f119SLarsDW223 } 79ec57f119SLarsDW223 $data['secid'] = ++$this->lastsecid; 80ec57f119SLarsDW223 $data['start'] = $start; 81ec57f119SLarsDW223 $this->sectionedits[] = $data; 82ec57f119SLarsDW223 return 'sectionedit'.$data['secid']; 8390df9a4dSAdrian Lang } 8490df9a4dSAdrian Lang 8590df9a4dSAdrian Lang /** 8690df9a4dSAdrian Lang * Finish an edit section range 8790df9a4dSAdrian Lang * 8842ea7f44SGerrit Uitslag * @param int $end The byte position for the edit end; null for the rest of the page 8942ea7f44SGerrit Uitslag * 9090df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 9190df9a4dSAdrian Lang */ 922571786cSLarsDW223 public function finishSectionEdit($end = null, $hid = null) { 93ec57f119SLarsDW223 $data = array_pop($this->sectionedits); 94ec57f119SLarsDW223 if(!is_null($end) && $end <= $data['start']) { 9500c13053SAdrian Lang return; 9600c13053SAdrian Lang } 972571786cSLarsDW223 if(!is_null($hid)) { 98ec57f119SLarsDW223 $data['hid'] .= $hid; 992571786cSLarsDW223 } 100ec57f119SLarsDW223 $data['range'] = $data['start'].'-'.(is_null($end) ? '' : $end); 101ec57f119SLarsDW223 unset($data['start']); 102ada0d779SMichael Hamann $this->doc .= '<!-- EDIT'.hsc(json_encode ($data)).' -->'; 10390df9a4dSAdrian Lang } 10490df9a4dSAdrian Lang 1053dd5c225SAndreas Gohr /** 1063dd5c225SAndreas Gohr * Returns the format produced by this renderer. 1073dd5c225SAndreas Gohr * 1083dd5c225SAndreas Gohr * @return string always 'xhtml' 1093dd5c225SAndreas Gohr */ 110*de369923SAndreas Gohr public function getFormat() { 1115f70445dSAndreas Gohr return 'xhtml'; 1125f70445dSAndreas Gohr } 1135f70445dSAndreas Gohr 1143dd5c225SAndreas Gohr /** 1153dd5c225SAndreas Gohr * Initialize the document 1163dd5c225SAndreas Gohr */ 117*de369923SAndreas Gohr public function document_start() { 118c5a8fd96SAndreas Gohr //reset some internals 119c5a8fd96SAndreas Gohr $this->toc = array(); 1200cecf9d5Sandi } 1210cecf9d5Sandi 1223dd5c225SAndreas Gohr /** 1233dd5c225SAndreas Gohr * Finalize the document 1243dd5c225SAndreas Gohr */ 125*de369923SAndreas Gohr public function document_end() { 12690df9a4dSAdrian Lang // Finish open section edits. 12790df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 128ec57f119SLarsDW223 if($this->sectionedits[count($this->sectionedits) - 1]['start'] <= 1) { 12990df9a4dSAdrian Lang // If there is only one section, do not write a section edit 13090df9a4dSAdrian Lang // marker. 13190df9a4dSAdrian Lang array_pop($this->sectionedits); 13290df9a4dSAdrian Lang } else { 133d9e36cbeSAdrian Lang $this->finishSectionEdit(); 13490df9a4dSAdrian Lang } 13590df9a4dSAdrian Lang } 13690df9a4dSAdrian Lang 1370cecf9d5Sandi if(count($this->footnotes) > 0) { 138a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 139d74aace9Schris 14016ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 141d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 142d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 143d74aace9Schris 144d74aace9Schris // open the footnote and set the anchor and backlink 145d74aace9Schris $this->doc .= '<div class="fn">'; 14616cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 14729bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 148d74aace9Schris 149d74aace9Schris // get any other footnotes that use the same markup 150d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 151d74aace9Schris 152d74aace9Schris if(count($alt)) { 153d74aace9Schris foreach($alt as $ref) { 154d74aace9Schris // set anchor and backlink for the other footnotes 15516ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 15616ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 157d74aace9Schris } 158d74aace9Schris } 159d74aace9Schris 160d74aace9Schris // add footnote markup and close this footnote 161694afa06SAnika Henke $this->doc .= '<div class="content">'.$footnote.'</div>'; 162d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 163d74aace9Schris } 1640cecf9d5Sandi } 165a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1660cecf9d5Sandi } 167c5a8fd96SAndreas Gohr 168b8595a66SAndreas Gohr // Prepare the TOC 169851f2e89SAnika Henke global $conf; 17064159a61SAndreas Gohr if( 17164159a61SAndreas Gohr $this->info['toc'] && 17264159a61SAndreas Gohr is_array($this->toc) && 17364159a61SAndreas Gohr $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads'] 17464159a61SAndreas Gohr ) { 175b8595a66SAndreas Gohr global $TOC; 176b8595a66SAndreas Gohr $TOC = $this->toc; 1770cecf9d5Sandi } 1783e55d035SAndreas Gohr 1793e55d035SAndreas Gohr // make sure there are no empty paragraphs 18027918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 181e41c4da9SAndreas Gohr } 1820cecf9d5Sandi 1833dd5c225SAndreas Gohr /** 1843dd5c225SAndreas Gohr * Add an item to the TOC 1853dd5c225SAndreas Gohr * 1863dd5c225SAndreas Gohr * @param string $id the hash link 1873dd5c225SAndreas Gohr * @param string $text the text to display 1883dd5c225SAndreas Gohr * @param int $level the nesting level 1893dd5c225SAndreas Gohr */ 190*de369923SAndreas Gohr public function toc_additem($id, $text, $level) { 191af587fa8Sandi global $conf; 192af587fa8Sandi 193c5a8fd96SAndreas Gohr //handle TOC 194c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1957d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 196c5a8fd96SAndreas Gohr } 197e7856beaSchris } 198e7856beaSchris 1993dd5c225SAndreas Gohr /** 2003dd5c225SAndreas Gohr * Render a heading 2013dd5c225SAndreas Gohr * 2023dd5c225SAndreas Gohr * @param string $text the text to display 2033dd5c225SAndreas Gohr * @param int $level header level 2043dd5c225SAndreas Gohr * @param int $pos byte position in the original source 2053dd5c225SAndreas Gohr */ 206*de369923SAndreas Gohr public function header($text, $level, $pos) { 20790df9a4dSAdrian Lang global $conf; 20890df9a4dSAdrian Lang 209f515db7fSAndreas Gohr if(blank($text)) return; //skip empty headlines 210e7856beaSchris 211e7856beaSchris $hid = $this->_headerToLink($text, true); 212e7856beaSchris 213e7856beaSchris //only add items within configured levels 214e7856beaSchris $this->toc_additem($hid, $text, $level); 215c5a8fd96SAndreas Gohr 21691459163SAnika Henke // adjust $node to reflect hierarchy of levels 21791459163SAnika Henke $this->node[$level - 1]++; 21891459163SAnika Henke if($level < $this->lastlevel) { 21991459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 22091459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 22191459163SAnika Henke } 22291459163SAnika Henke } 22391459163SAnika Henke $this->lastlevel = $level; 22491459163SAnika Henke 22590df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 22690df9a4dSAdrian Lang count($this->sectionedits) > 0 && 227ec57f119SLarsDW223 $this->sectionedits[count($this->sectionedits) - 1]['target'] === 'section' 2283dd5c225SAndreas Gohr ) { 2296c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 23090df9a4dSAdrian Lang } 23190df9a4dSAdrian Lang 232c5a8fd96SAndreas Gohr // write the header 23390df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 23490df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 235ec57f119SLarsDW223 $data = array(); 236ec57f119SLarsDW223 $data['target'] = 'section'; 237ec57f119SLarsDW223 $data['name'] = $text; 238ec57f119SLarsDW223 $data['hid'] = $hid; 239ec57f119SLarsDW223 $data['codeblockOffset'] = $this->_codeblock; 240ec57f119SLarsDW223 $this->doc .= ' class="'.$this->startSectionEdit($pos, $data).'"'; 24190df9a4dSAdrian Lang } 24216cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 243a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 24416cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2450cecf9d5Sandi } 2460cecf9d5Sandi 2473dd5c225SAndreas Gohr /** 2483dd5c225SAndreas Gohr * Open a new section 2493dd5c225SAndreas Gohr * 2503dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2513dd5c225SAndreas Gohr */ 252*de369923SAndreas Gohr public function section_open($level) { 2539864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2540cecf9d5Sandi } 2550cecf9d5Sandi 2563dd5c225SAndreas Gohr /** 2573dd5c225SAndreas Gohr * Close the current section 2583dd5c225SAndreas Gohr */ 259*de369923SAndreas Gohr public function section_close() { 260a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2610cecf9d5Sandi } 2620cecf9d5Sandi 2633dd5c225SAndreas Gohr /** 2643dd5c225SAndreas Gohr * Render plain text data 2653dd5c225SAndreas Gohr * 2663dd5c225SAndreas Gohr * @param $text 2673dd5c225SAndreas Gohr */ 268*de369923SAndreas Gohr public function cdata($text) { 269a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2700cecf9d5Sandi } 2710cecf9d5Sandi 2723dd5c225SAndreas Gohr /** 2733dd5c225SAndreas Gohr * Open a paragraph 2743dd5c225SAndreas Gohr */ 275*de369923SAndreas Gohr public function p_open() { 27659869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2770cecf9d5Sandi } 2780cecf9d5Sandi 2793dd5c225SAndreas Gohr /** 2803dd5c225SAndreas Gohr * Close a paragraph 2813dd5c225SAndreas Gohr */ 282*de369923SAndreas Gohr public function p_close() { 28359869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2840cecf9d5Sandi } 2850cecf9d5Sandi 2863dd5c225SAndreas Gohr /** 2873dd5c225SAndreas Gohr * Create a line break 2883dd5c225SAndreas Gohr */ 289*de369923SAndreas Gohr public function linebreak() { 290a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2910cecf9d5Sandi } 2920cecf9d5Sandi 2933dd5c225SAndreas Gohr /** 2943dd5c225SAndreas Gohr * Create a horizontal line 2953dd5c225SAndreas Gohr */ 296*de369923SAndreas Gohr public function hr() { 2974beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2980cecf9d5Sandi } 2990cecf9d5Sandi 3003dd5c225SAndreas Gohr /** 3013dd5c225SAndreas Gohr * Start strong (bold) formatting 3023dd5c225SAndreas Gohr */ 303*de369923SAndreas Gohr public function strong_open() { 304a2d649c4Sandi $this->doc .= '<strong>'; 3050cecf9d5Sandi } 3060cecf9d5Sandi 3073dd5c225SAndreas Gohr /** 3083dd5c225SAndreas Gohr * Stop strong (bold) formatting 3093dd5c225SAndreas Gohr */ 310*de369923SAndreas Gohr public function strong_close() { 311a2d649c4Sandi $this->doc .= '</strong>'; 3120cecf9d5Sandi } 3130cecf9d5Sandi 3143dd5c225SAndreas Gohr /** 3153dd5c225SAndreas Gohr * Start emphasis (italics) formatting 3163dd5c225SAndreas Gohr */ 317*de369923SAndreas Gohr public function emphasis_open() { 318a2d649c4Sandi $this->doc .= '<em>'; 3190cecf9d5Sandi } 3200cecf9d5Sandi 3213dd5c225SAndreas Gohr /** 3223dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3233dd5c225SAndreas Gohr */ 324*de369923SAndreas Gohr public function emphasis_close() { 325a2d649c4Sandi $this->doc .= '</em>'; 3260cecf9d5Sandi } 3270cecf9d5Sandi 3283dd5c225SAndreas Gohr /** 3293dd5c225SAndreas Gohr * Start underline formatting 3303dd5c225SAndreas Gohr */ 331*de369923SAndreas Gohr public function underline_open() { 33202e51121SAnika Henke $this->doc .= '<em class="u">'; 3330cecf9d5Sandi } 3340cecf9d5Sandi 3353dd5c225SAndreas Gohr /** 3363dd5c225SAndreas Gohr * Stop underline formatting 3373dd5c225SAndreas Gohr */ 338*de369923SAndreas Gohr public function underline_close() { 33902e51121SAnika Henke $this->doc .= '</em>'; 3400cecf9d5Sandi } 3410cecf9d5Sandi 3423dd5c225SAndreas Gohr /** 3433dd5c225SAndreas Gohr * Start monospace formatting 3443dd5c225SAndreas Gohr */ 345*de369923SAndreas Gohr public function monospace_open() { 346a2d649c4Sandi $this->doc .= '<code>'; 3470cecf9d5Sandi } 3480cecf9d5Sandi 3493dd5c225SAndreas Gohr /** 3503dd5c225SAndreas Gohr * Stop monospace formatting 3513dd5c225SAndreas Gohr */ 352*de369923SAndreas Gohr public function monospace_close() { 353a2d649c4Sandi $this->doc .= '</code>'; 3540cecf9d5Sandi } 3550cecf9d5Sandi 3563dd5c225SAndreas Gohr /** 3573dd5c225SAndreas Gohr * Start a subscript 3583dd5c225SAndreas Gohr */ 359*de369923SAndreas Gohr public function subscript_open() { 360a2d649c4Sandi $this->doc .= '<sub>'; 3610cecf9d5Sandi } 3620cecf9d5Sandi 3633dd5c225SAndreas Gohr /** 3643dd5c225SAndreas Gohr * Stop a subscript 3653dd5c225SAndreas Gohr */ 366*de369923SAndreas Gohr public function subscript_close() { 367a2d649c4Sandi $this->doc .= '</sub>'; 3680cecf9d5Sandi } 3690cecf9d5Sandi 3703dd5c225SAndreas Gohr /** 3713dd5c225SAndreas Gohr * Start a superscript 3723dd5c225SAndreas Gohr */ 373*de369923SAndreas Gohr public function superscript_open() { 374a2d649c4Sandi $this->doc .= '<sup>'; 3750cecf9d5Sandi } 3760cecf9d5Sandi 3773dd5c225SAndreas Gohr /** 3783dd5c225SAndreas Gohr * Stop a superscript 3793dd5c225SAndreas Gohr */ 380*de369923SAndreas Gohr public function superscript_close() { 381a2d649c4Sandi $this->doc .= '</sup>'; 3820cecf9d5Sandi } 3830cecf9d5Sandi 3843dd5c225SAndreas Gohr /** 3853dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3863dd5c225SAndreas Gohr */ 387*de369923SAndreas Gohr public function deleted_open() { 388a2d649c4Sandi $this->doc .= '<del>'; 3890cecf9d5Sandi } 3900cecf9d5Sandi 3913dd5c225SAndreas Gohr /** 3923dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 3933dd5c225SAndreas Gohr */ 394*de369923SAndreas Gohr public function deleted_close() { 395a2d649c4Sandi $this->doc .= '</del>'; 3960cecf9d5Sandi } 3970cecf9d5Sandi 3983fd0b676Sandi /** 3993fd0b676Sandi * Callback for footnote start syntax 4003fd0b676Sandi * 4013fd0b676Sandi * All following content will go to the footnote instead of 402d74aace9Schris * the document. To achieve this the previous rendered content 4033fd0b676Sandi * is moved to $store and $doc is cleared 4043fd0b676Sandi * 4053fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 4063fd0b676Sandi */ 407*de369923SAndreas Gohr public function footnote_open() { 4087764a90aSandi 4097764a90aSandi // move current content to store and record footnote 4107764a90aSandi $this->store = $this->doc; 4117764a90aSandi $this->doc = ''; 4120cecf9d5Sandi } 4130cecf9d5Sandi 4143fd0b676Sandi /** 4153fd0b676Sandi * Callback for footnote end syntax 4163fd0b676Sandi * 4173fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 4183fd0b676Sandi * content is restored from $store again 4193fd0b676Sandi * 4203fd0b676Sandi * @author Andreas Gohr 4213fd0b676Sandi */ 422*de369923SAndreas Gohr public function footnote_close() { 42316ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 42416ec3e37SAndreas Gohr static $fnid = 0; 42516ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 42616ec3e37SAndreas Gohr $fnid++; 4277764a90aSandi 428d74aace9Schris // recover footnote into the stack and restore old content 429d74aace9Schris $footnote = $this->doc; 4307764a90aSandi $this->doc = $this->store; 4317764a90aSandi $this->store = ''; 432d74aace9Schris 433d74aace9Schris // check to see if this footnote has been seen before 434d74aace9Schris $i = array_search($footnote, $this->footnotes); 435d74aace9Schris 436d74aace9Schris if($i === false) { 437d74aace9Schris // its a new footnote, add it to the $footnotes array 43816ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 439d74aace9Schris } else { 44016ec3e37SAndreas Gohr // seen this one before, save a placeholder 44116ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 442d74aace9Schris } 443d74aace9Schris 4446b379cbfSAndreas Gohr // output the footnote reference and link 44516ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4460cecf9d5Sandi } 4470cecf9d5Sandi 4483dd5c225SAndreas Gohr /** 4493dd5c225SAndreas Gohr * Open an unordered list 4500c4c0281SGerrit Uitslag * 4517d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4523dd5c225SAndreas Gohr */ 453*de369923SAndreas Gohr public function listu_open($classes = null) { 4540c4c0281SGerrit Uitslag $class = ''; 4550c4c0281SGerrit Uitslag if($classes !== null) { 4562e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4570c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4580c4c0281SGerrit Uitslag } 4590c4c0281SGerrit Uitslag $this->doc .= "<ul$class>".DOKU_LF; 4600cecf9d5Sandi } 4610cecf9d5Sandi 4623dd5c225SAndreas Gohr /** 4633dd5c225SAndreas Gohr * Close an unordered list 4643dd5c225SAndreas Gohr */ 465*de369923SAndreas Gohr public function listu_close() { 466a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4670cecf9d5Sandi } 4680cecf9d5Sandi 4693dd5c225SAndreas Gohr /** 4703dd5c225SAndreas Gohr * Open an ordered list 4710c4c0281SGerrit Uitslag * 4727d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4733dd5c225SAndreas Gohr */ 474*de369923SAndreas Gohr public function listo_open($classes = null) { 4750c4c0281SGerrit Uitslag $class = ''; 4760c4c0281SGerrit Uitslag if($classes !== null) { 4772e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4780c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4790c4c0281SGerrit Uitslag } 4800c4c0281SGerrit Uitslag $this->doc .= "<ol$class>".DOKU_LF; 4810cecf9d5Sandi } 4820cecf9d5Sandi 4833dd5c225SAndreas Gohr /** 4843dd5c225SAndreas Gohr * Close an ordered list 4853dd5c225SAndreas Gohr */ 486*de369923SAndreas Gohr public function listo_close() { 487a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4880cecf9d5Sandi } 4890cecf9d5Sandi 4903dd5c225SAndreas Gohr /** 4913dd5c225SAndreas Gohr * Open a list item 4923dd5c225SAndreas Gohr * 4933dd5c225SAndreas Gohr * @param int $level the nesting level 494e3a24861SChristopher Smith * @param bool $node true when a node; false when a leaf 4953dd5c225SAndreas Gohr */ 496*de369923SAndreas Gohr public function listitem_open($level, $node=false) { 497e3a24861SChristopher Smith $branching = $node ? ' node' : ''; 498e3a24861SChristopher Smith $this->doc .= '<li class="level'.$level.$branching.'">'; 4990cecf9d5Sandi } 5000cecf9d5Sandi 5013dd5c225SAndreas Gohr /** 5023dd5c225SAndreas Gohr * Close a list item 5033dd5c225SAndreas Gohr */ 504*de369923SAndreas Gohr public function listitem_close() { 505a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 5060cecf9d5Sandi } 5070cecf9d5Sandi 5083dd5c225SAndreas Gohr /** 5093dd5c225SAndreas Gohr * Start the content of a list item 5103dd5c225SAndreas Gohr */ 511*de369923SAndreas Gohr public function listcontent_open() { 51290db23d7Schris $this->doc .= '<div class="li">'; 5130cecf9d5Sandi } 5140cecf9d5Sandi 5153dd5c225SAndreas Gohr /** 5163dd5c225SAndreas Gohr * Stop the content of a list item 5173dd5c225SAndreas Gohr */ 518*de369923SAndreas Gohr public function listcontent_close() { 51959869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 5200cecf9d5Sandi } 5210cecf9d5Sandi 5223dd5c225SAndreas Gohr /** 5233dd5c225SAndreas Gohr * Output unformatted $text 5243dd5c225SAndreas Gohr * 5253dd5c225SAndreas Gohr * Defaults to $this->cdata() 5263dd5c225SAndreas Gohr * 5273dd5c225SAndreas Gohr * @param string $text 5283dd5c225SAndreas Gohr */ 529*de369923SAndreas Gohr public function unformatted($text) { 530a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 5310cecf9d5Sandi } 5320cecf9d5Sandi 5330cecf9d5Sandi /** 5343fd0b676Sandi * Execute PHP code if allowed 5353fd0b676Sandi * 536d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5375d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5385d568b99SChris Smith * 5393fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5400cecf9d5Sandi */ 541*de369923SAndreas Gohr public function php($text, $wrapper = 'code') { 54235a56260SChris Smith global $conf; 54335a56260SChris Smith 544d86d5af0SChris Smith if($conf['phpok']) { 545bad0b545Sandi ob_start(); 5464de671bcSandi eval($text); 5473fd0b676Sandi $this->doc .= ob_get_contents(); 548bad0b545Sandi ob_end_clean(); 549d86d5af0SChris Smith } else { 5505d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 551d86d5af0SChris Smith } 5520cecf9d5Sandi } 5530cecf9d5Sandi 5543dd5c225SAndreas Gohr /** 5553dd5c225SAndreas Gohr * Output block level PHP code 5563dd5c225SAndreas Gohr * 5573dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5583dd5c225SAndreas Gohr * to $doc 5593dd5c225SAndreas Gohr * 5603dd5c225SAndreas Gohr * @param string $text The PHP code 5613dd5c225SAndreas Gohr */ 562*de369923SAndreas Gohr public function phpblock($text) { 5635d568b99SChris Smith $this->php($text, 'pre'); 56407f89c3cSAnika Henke } 56507f89c3cSAnika Henke 5660cecf9d5Sandi /** 5673fd0b676Sandi * Insert HTML if allowed 5683fd0b676Sandi * 569d9764001SMichael Hamann * @param string $text html text 5705d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5715d568b99SChris Smith * 5723fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5730cecf9d5Sandi */ 574*de369923SAndreas Gohr public function html($text, $wrapper = 'code') { 57535a56260SChris Smith global $conf; 57635a56260SChris Smith 577d86d5af0SChris Smith if($conf['htmlok']) { 578a2d649c4Sandi $this->doc .= $text; 579d86d5af0SChris Smith } else { 5805d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 581d86d5af0SChris Smith } 5824de671bcSandi } 5830cecf9d5Sandi 5843dd5c225SAndreas Gohr /** 5853dd5c225SAndreas Gohr * Output raw block-level HTML 5863dd5c225SAndreas Gohr * 5873dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 5883dd5c225SAndreas Gohr * 5893dd5c225SAndreas Gohr * @param string $text The HTML 5903dd5c225SAndreas Gohr */ 591*de369923SAndreas Gohr public function htmlblock($text) { 5925d568b99SChris Smith $this->html($text, 'pre'); 59307f89c3cSAnika Henke } 59407f89c3cSAnika Henke 5953dd5c225SAndreas Gohr /** 5963dd5c225SAndreas Gohr * Start a block quote 5973dd5c225SAndreas Gohr */ 598*de369923SAndreas Gohr public function quote_open() { 59996331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 6000cecf9d5Sandi } 6010cecf9d5Sandi 6023dd5c225SAndreas Gohr /** 6033dd5c225SAndreas Gohr * Stop a block quote 6043dd5c225SAndreas Gohr */ 605*de369923SAndreas Gohr public function quote_close() { 60696331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 6070cecf9d5Sandi } 6080cecf9d5Sandi 6093dd5c225SAndreas Gohr /** 6103dd5c225SAndreas Gohr * Output preformatted text 6113dd5c225SAndreas Gohr * 6123dd5c225SAndreas Gohr * @param string $text 6133dd5c225SAndreas Gohr */ 614*de369923SAndreas Gohr public function preformatted($text) { 615c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 6163d491f75SAndreas Gohr } 6173d491f75SAndreas Gohr 6183dd5c225SAndreas Gohr /** 6193dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 6203dd5c225SAndreas Gohr * 6213dd5c225SAndreas Gohr * @param string $text text to show 6223dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6233dd5c225SAndreas Gohr * @param string $filename file path label 624e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6253dd5c225SAndreas Gohr */ 626*de369923SAndreas Gohr public function file($text, $language = null, $filename = null, $options=null) { 627e2d88156SLarsDW223 $this->_highlight('file', $text, $language, $filename, $options); 6283d491f75SAndreas Gohr } 6293d491f75SAndreas Gohr 6303dd5c225SAndreas Gohr /** 6313dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 6323dd5c225SAndreas Gohr * 6333dd5c225SAndreas Gohr * @param string $text text to show 6343dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6353dd5c225SAndreas Gohr * @param string $filename file path label 636e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6373dd5c225SAndreas Gohr */ 638*de369923SAndreas Gohr public function code($text, $language = null, $filename = null, $options=null) { 639e2d88156SLarsDW223 $this->_highlight('code', $text, $language, $filename, $options); 6403d491f75SAndreas Gohr } 6413d491f75SAndreas Gohr 6420cecf9d5Sandi /** 6433d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6443fd0b676Sandi * 6453fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6463dd5c225SAndreas Gohr * @param string $type code|file 6473dd5c225SAndreas Gohr * @param string $text text to show 6483dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6493dd5c225SAndreas Gohr * @param string $filename file path label 650e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6510cecf9d5Sandi */ 652*de369923SAndreas Gohr public function _highlight($type, $text, $language = null, $filename = null, $options = null) { 6533d491f75SAndreas Gohr global $ID; 6543d491f75SAndreas Gohr global $lang; 655ec57f119SLarsDW223 global $INPUT; 6563d491f75SAndreas Gohr 65756bd9509SPhy $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language); 65856bd9509SPhy 6593d491f75SAndreas Gohr if($filename) { 660190c56e8SAndreas Gohr // add icon 66127bf7924STom N Harris list($ext) = mimetype($filename, false); 662190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 663190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 664190c56e8SAndreas Gohr 665ec57f119SLarsDW223 $offset = 0; 666ec57f119SLarsDW223 if ($INPUT->has('codeblockOffset')) { 667ec57f119SLarsDW223 $offset = $INPUT->str('codeblockOffset'); 668ec57f119SLarsDW223 } 6693d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 67064159a61SAndreas Gohr $this->doc .= '<dt><a href="' . 67164159a61SAndreas Gohr exportlink( 67264159a61SAndreas Gohr $ID, 67364159a61SAndreas Gohr 'code', 67464159a61SAndreas Gohr array('codeblock' => $offset + $this->_codeblock) 67564159a61SAndreas Gohr ) . '" title="' . $lang['download'] . '" class="' . $class . '">'; 6763d491f75SAndreas Gohr $this->doc .= hsc($filename); 6773d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6783d491f75SAndreas Gohr } 6790cecf9d5Sandi 680d43aac1cSGina Haeussge if($text{0} == "\n") { 681d43aac1cSGina Haeussge $text = substr($text, 1); 682d43aac1cSGina Haeussge } 683d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 684d43aac1cSGina Haeussge $text = substr($text, 0, -1); 685d43aac1cSGina Haeussge } 686d43aac1cSGina Haeussge 687a056e285SPhy if(empty($language)) { // empty is faster than is_null and can prevent '' string 6883d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6890cecf9d5Sandi } else { 6903d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6913d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6923d491f75SAndreas Gohr 69364159a61SAndreas Gohr $this->doc .= "<pre class=\"$class $language\">" . 69464159a61SAndreas Gohr p_xhtml_cached_geshi($text, $language, '', $options) . 69564159a61SAndreas Gohr '</pre>' . DOKU_LF; 6960cecf9d5Sandi } 6973d491f75SAndreas Gohr 6983d491f75SAndreas Gohr if($filename) { 6993d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 7003d491f75SAndreas Gohr } 7013d491f75SAndreas Gohr 7023d491f75SAndreas Gohr $this->_codeblock++; 7030cecf9d5Sandi } 7040cecf9d5Sandi 7053dd5c225SAndreas Gohr /** 7063dd5c225SAndreas Gohr * Format an acronym 7073dd5c225SAndreas Gohr * 7083dd5c225SAndreas Gohr * Uses $this->acronyms 7093dd5c225SAndreas Gohr * 7103dd5c225SAndreas Gohr * @param string $acronym 7113dd5c225SAndreas Gohr */ 712*de369923SAndreas Gohr public function acronym($acronym) { 7130cecf9d5Sandi 7140cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 7150cecf9d5Sandi 716433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 7170cecf9d5Sandi 718940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 719940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 7200cecf9d5Sandi 7210cecf9d5Sandi } else { 722a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 7230cecf9d5Sandi } 7240cecf9d5Sandi } 7250cecf9d5Sandi 7263dd5c225SAndreas Gohr /** 7273dd5c225SAndreas Gohr * Format a smiley 7283dd5c225SAndreas Gohr * 7293dd5c225SAndreas Gohr * Uses $this->smiley 7303dd5c225SAndreas Gohr * 7313dd5c225SAndreas Gohr * @param string $smiley 7323dd5c225SAndreas Gohr */ 733*de369923SAndreas Gohr public function smiley($smiley) { 7340cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 735f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 7368e38227fSAnika Henke '" class="icon" alt="'. 737433bef32Sandi $this->_xmlEntities($smiley).'" />'; 7380cecf9d5Sandi } else { 739a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 7400cecf9d5Sandi } 7410cecf9d5Sandi } 7420cecf9d5Sandi 7433dd5c225SAndreas Gohr /** 7443dd5c225SAndreas Gohr * Format an entity 7453dd5c225SAndreas Gohr * 7463dd5c225SAndreas Gohr * Entities are basically small text replacements 7473dd5c225SAndreas Gohr * 7483dd5c225SAndreas Gohr * Uses $this->entities 7493dd5c225SAndreas Gohr * 7503dd5c225SAndreas Gohr * @param string $entity 7514de671bcSandi */ 752*de369923SAndreas Gohr public function entity($entity) { 7530cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 754a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7550cecf9d5Sandi } else { 756a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7570cecf9d5Sandi } 7580cecf9d5Sandi } 7590cecf9d5Sandi 7603dd5c225SAndreas Gohr /** 7613dd5c225SAndreas Gohr * Typographically format a multiply sign 7623dd5c225SAndreas Gohr * 7633dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7643dd5c225SAndreas Gohr * 7653dd5c225SAndreas Gohr * @param string|int $x first value 7663dd5c225SAndreas Gohr * @param string|int $y second value 7673dd5c225SAndreas Gohr */ 768*de369923SAndreas Gohr public function multiplyentity($x, $y) { 769a2d649c4Sandi $this->doc .= "$x×$y"; 7700cecf9d5Sandi } 7710cecf9d5Sandi 7723dd5c225SAndreas Gohr /** 7733dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7743dd5c225SAndreas Gohr */ 775*de369923SAndreas Gohr public function singlequoteopening() { 77671b40da2SAnika Henke global $lang; 77771b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7780cecf9d5Sandi } 7790cecf9d5Sandi 7803dd5c225SAndreas Gohr /** 7813dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7823dd5c225SAndreas Gohr */ 783*de369923SAndreas Gohr public function singlequoteclosing() { 78471b40da2SAnika Henke global $lang; 78571b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7860cecf9d5Sandi } 7870cecf9d5Sandi 7883dd5c225SAndreas Gohr /** 7893dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7903dd5c225SAndreas Gohr */ 791*de369923SAndreas Gohr public function apostrophe() { 79257d757d1SAndreas Gohr global $lang; 793a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 79457d757d1SAndreas Gohr } 79557d757d1SAndreas Gohr 7963dd5c225SAndreas Gohr /** 7973dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 7983dd5c225SAndreas Gohr */ 799*de369923SAndreas Gohr public function doublequoteopening() { 80071b40da2SAnika Henke global $lang; 80171b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 8020cecf9d5Sandi } 8030cecf9d5Sandi 8043dd5c225SAndreas Gohr /** 8053dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 8063dd5c225SAndreas Gohr */ 807*de369923SAndreas Gohr public function doublequoteclosing() { 80871b40da2SAnika Henke global $lang; 80971b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 8100cecf9d5Sandi } 8110cecf9d5Sandi 8120cecf9d5Sandi /** 8133dd5c225SAndreas Gohr * Render a CamelCase link 8143dd5c225SAndreas Gohr * 8153dd5c225SAndreas Gohr * @param string $link The link name 816122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8170c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8180c4c0281SGerrit Uitslag * 8193dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 8200cecf9d5Sandi */ 821*de369923SAndreas Gohr public function camelcaselink($link, $returnonly = false) { 822122f2d46SAndreas Böhler if($returnonly) { 823122f2d46SAndreas Böhler return $this->internallink($link, $link, null, true); 824122f2d46SAndreas Böhler } else { 82511d0aa47Sandi $this->internallink($link, $link); 8260cecf9d5Sandi } 827122f2d46SAndreas Böhler } 8280cecf9d5Sandi 8293dd5c225SAndreas Gohr /** 8303dd5c225SAndreas Gohr * Render a page local link 8313dd5c225SAndreas Gohr * 8323dd5c225SAndreas Gohr * @param string $hash hash link identifier 8333dd5c225SAndreas Gohr * @param string $name name for the link 834122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8350c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8363dd5c225SAndreas Gohr */ 837*de369923SAndreas Gohr public function locallink($hash, $name = null, $returnonly = false) { 8380b7c14c2Sandi global $ID; 8390b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 8400b7c14c2Sandi $hash = $this->_headerToLink($hash); 841e260f93bSAnika Henke $title = $ID.' ↵'; 842122f2d46SAndreas Böhler 843122f2d46SAndreas Böhler $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 844122f2d46SAndreas Böhler $doc .= $name; 845122f2d46SAndreas Böhler $doc .= '</a>'; 846122f2d46SAndreas Böhler 847122f2d46SAndreas Böhler if($returnonly) { 848122f2d46SAndreas Böhler return $doc; 849122f2d46SAndreas Böhler } else { 850122f2d46SAndreas Böhler $this->doc .= $doc; 851122f2d46SAndreas Böhler } 8520b7c14c2Sandi } 8530b7c14c2Sandi 854cffcc403Sandi /** 8553fd0b676Sandi * Render an internal Wiki Link 8563fd0b676Sandi * 857fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 858cffcc403Sandi * elsewhere - no need to implement them in other renderers 8593fd0b676Sandi * 8603dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 861f23eef27SGerrit Uitslag * @param string $id pageid 862f23eef27SGerrit Uitslag * @param string|null $name link name 863f23eef27SGerrit Uitslag * @param string|null $search adds search url param 864f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 865f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 866f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 867cffcc403Sandi */ 868*de369923SAndreas Gohr public function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 869ba11bd29Sandi global $conf; 87037e34a5eSandi global $ID; 871c4dda6afSAnika Henke global $INFO; 87244653a53SAdrian Lang 8733d5e07d9SAdrian Lang $params = ''; 8743d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8753d5e07d9SAdrian Lang if(count($parts) === 2) { 8763d5e07d9SAdrian Lang $id = $parts[0]; 8773d5e07d9SAdrian Lang $params = $parts[1]; 87844653a53SAdrian Lang } 87944653a53SAdrian Lang 880fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 881fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 882fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 883fda14ffcSIzidor Matušov // (some things could be lost) 884fda14ffcSIzidor Matušov if($id === '') { 885fda14ffcSIzidor Matušov $id = $ID; 886fda14ffcSIzidor Matušov } 887fda14ffcSIzidor Matušov 8880339c872Sjan // default name is based on $id as given 8890339c872Sjan $default = $this->_simpleTitle($id); 890ad32e47eSAndreas Gohr 8910339c872Sjan // now first resolve and clean up the $id 89290bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 893fda14ffcSIzidor Matušov 89459bc3b48SGerrit Uitslag $link = array(); 895fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8960e1c636eSandi if(!$isImage) { 8970e1c636eSandi if($exists) { 898ba11bd29Sandi $class = 'wikilink1'; 8990cecf9d5Sandi } else { 900ba11bd29Sandi $class = 'wikilink2'; 90144a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 9020cecf9d5Sandi } 9030cecf9d5Sandi } else { 904ba11bd29Sandi $class = 'media'; 9050cecf9d5Sandi } 9060cecf9d5Sandi 907a1685bedSandi //keep hash anchor 9086d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 909943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 910a1685bedSandi 911ba11bd29Sandi //prepare for formating 912ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 913ba11bd29Sandi $link['style'] = ''; 914ba11bd29Sandi $link['pre'] = ''; 915ba11bd29Sandi $link['suf'] = ''; 91640eb54bbSjan // highlight link to current page 917c4dda6afSAnika Henke if($id == $INFO['id']) { 91892795d04Sandi $link['pre'] = '<span class="curid">'; 91992795d04Sandi $link['suf'] = '</span>'; 92040eb54bbSjan } 9215e163278SAndreas Gohr $link['more'] = ''; 922ba11bd29Sandi $link['class'] = $class; 9235c2eed9aSlisps if($this->date_at) { 924912a6d48SPhy $params = $params.'&at='.rawurlencode($this->date_at); 9255c2eed9aSlisps } 92644653a53SAdrian Lang $link['url'] = wl($id, $params); 927ba11bd29Sandi $link['name'] = $name; 928ba11bd29Sandi $link['title'] = $id; 929723d78dbSandi //add search string 930723d78dbSandi if($search) { 931546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 932546d3a99SAndreas Gohr if(is_array($search)) { 933546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 934546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 935546d3a99SAndreas Gohr } else { 936546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 937546d3a99SAndreas Gohr } 938723d78dbSandi } 939723d78dbSandi 940a1685bedSandi //keep hash 941a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 942a1685bedSandi 943ba11bd29Sandi //output formatted 944cffcc403Sandi if($returnonly) { 945cffcc403Sandi return $this->_formatLink($link); 946cffcc403Sandi } else { 947a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9480cecf9d5Sandi } 949cffcc403Sandi } 9500cecf9d5Sandi 9513dd5c225SAndreas Gohr /** 9523dd5c225SAndreas Gohr * Render an external link 9533dd5c225SAndreas Gohr * 9543dd5c225SAndreas Gohr * @param string $url full URL with scheme 9553dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 956122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 9570c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9583dd5c225SAndreas Gohr */ 959*de369923SAndreas Gohr public function externallink($url, $name = null, $returnonly = false) { 960b625487dSandi global $conf; 9610cecf9d5Sandi 962433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 9636f0c5dbfSandi 964b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 965b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 966b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 967b52b1596SAndreas Gohr $scheme = strtolower($scheme); 968b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 969b52b1596SAndreas Gohr 970b52b1596SAndreas Gohr // is there still an URL? 971b52b1596SAndreas Gohr if(!$url) { 97249cef4fdSAndreas Böhler if($returnonly) { 97349cef4fdSAndreas Böhler return $name; 97449cef4fdSAndreas Böhler } else { 975b52b1596SAndreas Gohr $this->doc .= $name; 97649cef4fdSAndreas Böhler } 977b52b1596SAndreas Gohr return; 978b52b1596SAndreas Gohr } 979b52b1596SAndreas Gohr 980b52b1596SAndreas Gohr // set class 9810cecf9d5Sandi if(!$isImage) { 982b625487dSandi $class = 'urlextern'; 9830cecf9d5Sandi } else { 984b625487dSandi $class = 'media'; 9850cecf9d5Sandi } 9860cecf9d5Sandi 987b625487dSandi //prepare for formating 98859bc3b48SGerrit Uitslag $link = array(); 989b625487dSandi $link['target'] = $conf['target']['extern']; 990b625487dSandi $link['style'] = ''; 991b625487dSandi $link['pre'] = ''; 992b625487dSandi $link['suf'] = ''; 9935e163278SAndreas Gohr $link['more'] = ''; 994b625487dSandi $link['class'] = $class; 995b625487dSandi $link['url'] = $url; 996914045f3SAndreas Gohr $link['rel'] = ''; 997e1c10e4dSchris 998b625487dSandi $link['name'] = $name; 999433bef32Sandi $link['title'] = $this->_xmlEntities($url); 1000914045f3SAndreas Gohr if($conf['relnofollow']) $link['rel'] .= ' nofollow'; 1001914045f3SAndreas Gohr if($conf['target']['extern']) $link['rel'] .= ' noopener'; 10020cecf9d5Sandi 1003b625487dSandi //output formatted 1004122f2d46SAndreas Böhler if($returnonly) { 1005122f2d46SAndreas Böhler return $this->_formatLink($link); 1006122f2d46SAndreas Böhler } else { 1007a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10080cecf9d5Sandi } 1009122f2d46SAndreas Böhler } 10100cecf9d5Sandi 10110cecf9d5Sandi /** 10123dd5c225SAndreas Gohr * Render an interwiki link 10133dd5c225SAndreas Gohr * 10143dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 10153dd5c225SAndreas Gohr * 10163dd5c225SAndreas Gohr * @param string $match original link - probably not much use 10173dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 10183dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 10193dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 1020122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10210c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10220cecf9d5Sandi */ 1023*de369923SAndreas Gohr public function interwikilink($match, $name, $wikiName, $wikiUri, $returnonly = false) { 1024b625487dSandi global $conf; 10250cecf9d5Sandi 102697a3e4e3Sandi $link = array(); 102797a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 102897a3e4e3Sandi $link['pre'] = ''; 102997a3e4e3Sandi $link['suf'] = ''; 10305e163278SAndreas Gohr $link['more'] = ''; 1031433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 1032914045f3SAndreas Gohr $link['rel'] = ''; 10330cecf9d5Sandi 103497a3e4e3Sandi //get interwiki URL 10356496c33fSGerrit Uitslag $exists = null; 10366496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 10370cecf9d5Sandi 103897a3e4e3Sandi if(!$isImage) { 10399d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 10409d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 10411c2d1019SAndreas Gohr } else { 10421c2d1019SAndreas Gohr $link['class'] = 'media'; 104397a3e4e3Sandi } 10440cecf9d5Sandi 104597a3e4e3Sandi //do we stay at the same server? Use local target 10462345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 104797a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 104897a3e4e3Sandi } 10496496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 10506496c33fSGerrit Uitslag if($exists) { 10516496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 10526496c33fSGerrit Uitslag } else { 10536496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 1054914045f3SAndreas Gohr $link['rel'] .= ' nofollow'; 10556496c33fSGerrit Uitslag } 10566496c33fSGerrit Uitslag } 1057914045f3SAndreas Gohr if($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 10580cecf9d5Sandi 105997a3e4e3Sandi $link['url'] = $url; 106097a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 106197a3e4e3Sandi 106297a3e4e3Sandi //output formatted 1063122f2d46SAndreas Böhler if($returnonly) { 1064122f2d46SAndreas Böhler return $this->_formatLink($link); 1065122f2d46SAndreas Böhler } else { 1066a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10670cecf9d5Sandi } 1068122f2d46SAndreas Böhler } 10690cecf9d5Sandi 10700cecf9d5Sandi /** 10713dd5c225SAndreas Gohr * Link to windows share 10723dd5c225SAndreas Gohr * 10733dd5c225SAndreas Gohr * @param string $url the link 10743dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1075122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10760c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10770cecf9d5Sandi */ 1078*de369923SAndreas Gohr public function windowssharelink($url, $name = null, $returnonly = false) { 10791d47afe1Sandi global $conf; 10803dd5c225SAndreas Gohr 10811d47afe1Sandi //simple setup 108259bc3b48SGerrit Uitslag $link = array(); 10831d47afe1Sandi $link['target'] = $conf['target']['windows']; 10841d47afe1Sandi $link['pre'] = ''; 10851d47afe1Sandi $link['suf'] = ''; 10861d47afe1Sandi $link['style'] = ''; 10870cecf9d5Sandi 1088433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 10890cecf9d5Sandi if(!$isImage) { 10901d47afe1Sandi $link['class'] = 'windows'; 10910cecf9d5Sandi } else { 10921d47afe1Sandi $link['class'] = 'media'; 10930cecf9d5Sandi } 10940cecf9d5Sandi 1095433bef32Sandi $link['title'] = $this->_xmlEntities($url); 10961d47afe1Sandi $url = str_replace('\\', '/', $url); 10971d47afe1Sandi $url = 'file:///'.$url; 10981d47afe1Sandi $link['url'] = $url; 10990cecf9d5Sandi 11001d47afe1Sandi //output formatted 1101122f2d46SAndreas Böhler if($returnonly) { 1102122f2d46SAndreas Böhler return $this->_formatLink($link); 1103122f2d46SAndreas Böhler } else { 1104a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11050cecf9d5Sandi } 1106122f2d46SAndreas Böhler } 11070cecf9d5Sandi 11083dd5c225SAndreas Gohr /** 11093dd5c225SAndreas Gohr * Render a linked E-Mail Address 11103dd5c225SAndreas Gohr * 11113dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 11123dd5c225SAndreas Gohr * 11133dd5c225SAndreas Gohr * @param string $address Email-Address 11143dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1115122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 11160c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 11173dd5c225SAndreas Gohr */ 1118*de369923SAndreas Gohr public function emaillink($address, $name = null, $returnonly = false) { 111971352defSandi global $conf; 112071352defSandi //simple setup 112171352defSandi $link = array(); 112271352defSandi $link['target'] = ''; 112371352defSandi $link['pre'] = ''; 112471352defSandi $link['suf'] = ''; 112571352defSandi $link['style'] = ''; 112671352defSandi $link['more'] = ''; 11270cecf9d5Sandi 1128c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 11290cecf9d5Sandi if(!$isImage) { 1130be96545cSAnika Henke $link['class'] = 'mail'; 11310cecf9d5Sandi } else { 1132be96545cSAnika Henke $link['class'] = 'media'; 11330cecf9d5Sandi } 11340cecf9d5Sandi 113507738714SAndreas Gohr $address = $this->_xmlEntities($address); 113600a7b5adSEsther Brunner $address = obfuscate($address); 113700a7b5adSEsther Brunner $title = $address; 11388c128049SAndreas Gohr 113971352defSandi if(empty($name)) { 114000a7b5adSEsther Brunner $name = $address; 114171352defSandi } 11420cecf9d5Sandi 1143776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1144776b36ecSAndreas Gohr 1145776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 114671352defSandi $link['name'] = $name; 114771352defSandi $link['title'] = $title; 11480cecf9d5Sandi 114971352defSandi //output formatted 1150122f2d46SAndreas Böhler if($returnonly) { 1151122f2d46SAndreas Böhler return $this->_formatLink($link); 1152122f2d46SAndreas Böhler } else { 1153a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11540cecf9d5Sandi } 1155122f2d46SAndreas Böhler } 11560cecf9d5Sandi 11573dd5c225SAndreas Gohr /** 11583dd5c225SAndreas Gohr * Render an internal media file 11593dd5c225SAndreas Gohr * 11603dd5c225SAndreas Gohr * @param string $src media ID 11613dd5c225SAndreas Gohr * @param string $title descriptive text 11623dd5c225SAndreas Gohr * @param string $align left|center|right 11633dd5c225SAndreas Gohr * @param int $width width of media in pixel 11643dd5c225SAndreas Gohr * @param int $height height of media in pixel 11653dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11663dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 11673dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 11680c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11693dd5c225SAndreas Gohr */ 1170*de369923SAndreas Gohr public function internalmedia($src, $title = null, $align = null, $width = null, 11713dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 117237e34a5eSandi global $ID; 11738f34cf3dSMichael Große if (strpos($src, '#') !== false) { 117491df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 11758f34cf3dSMichael Große } 1176cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 11770cecf9d5Sandi 1178d98d4540SBen Coburn $noLink = false; 11798acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1180b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 11813685f775Sandi 11823dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1183b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 118464159a61SAndreas Gohr $link['url'] = ml( 118564159a61SAndreas Gohr $src, 118664159a61SAndreas Gohr array( 118764159a61SAndreas Gohr 'id' => $ID, 118864159a61SAndreas Gohr 'cache' => $cache, 118964159a61SAndreas Gohr 'rev' => $this->_getLastMediaRevisionAt($src) 119064159a61SAndreas Gohr ), 119164159a61SAndreas Gohr ($linking == 'direct') 119264159a61SAndreas Gohr ); 1193f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11942a2a2ba2SAnika Henke // don't link movies 119544881bd0Shenning.noren $noLink = true; 119655efc227SAndreas Gohr } else { 11972ca14335SEsther Brunner // add file icons 11989d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 11999d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 120064159a61SAndreas Gohr $link['url'] = ml( 120164159a61SAndreas Gohr $src, 120264159a61SAndreas Gohr array( 120364159a61SAndreas Gohr 'id' => $ID, 120464159a61SAndreas Gohr 'cache' => $cache, 120564159a61SAndreas Gohr 'rev' => $this->_getLastMediaRevisionAt($src) 120664159a61SAndreas Gohr ), 120764159a61SAndreas Gohr true 120864159a61SAndreas Gohr ); 120991328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 121055efc227SAndreas Gohr } 12113685f775Sandi 12128f34cf3dSMichael Große if (!empty($hash)) $link['url'] .= '#'.$hash; 121391df343aSAndreas Gohr 12146fe20453SGina Haeussge //markup non existing files 12154a24b459SKate Arzamastseva if(!$exists) { 12166fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 12174a24b459SKate Arzamastseva } 12186fe20453SGina Haeussge 12193685f775Sandi //output formatted 1220f50634f0SAnika Henke if($return) { 1221f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1222f50634f0SAnika Henke else return $this->_formatLink($link); 1223f50634f0SAnika Henke } else { 1224dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12252ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12260cecf9d5Sandi } 1227f50634f0SAnika Henke } 12280cecf9d5Sandi 12293dd5c225SAndreas Gohr /** 12303dd5c225SAndreas Gohr * Render an external media file 12313dd5c225SAndreas Gohr * 12323dd5c225SAndreas Gohr * @param string $src full media URL 12333dd5c225SAndreas Gohr * @param string $title descriptive text 12343dd5c225SAndreas Gohr * @param string $align left|center|right 12353dd5c225SAndreas Gohr * @param int $width width of media in pixel 12363dd5c225SAndreas Gohr * @param int $height height of media in pixel 12373dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 12383dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1239410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 12400c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 12413dd5c225SAndreas Gohr */ 1242*de369923SAndreas Gohr public function externalmedia($src, $title = null, $align = null, $width = null, 1243410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 12446efc45a2SDmitry Katsubo if(link_isinterwiki($src)){ 12456efc45a2SDmitry Katsubo list($shortcut, $reference) = explode('>', $src, 2); 12466efc45a2SDmitry Katsubo $exists = null; 12476efc45a2SDmitry Katsubo $src = $this->_resolveInterWiki($shortcut, $reference, $exists); 12486efc45a2SDmitry Katsubo } 124991df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1250d98d4540SBen Coburn $noLink = false; 12518acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1252b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1253b739ff0fSPierre Spring 1254b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 12553685f775Sandi 12563dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1257b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 12582ca14335SEsther Brunner // link only jpeg images 125944881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1260f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 12612a2a2ba2SAnika Henke // don't link movies 126244881bd0Shenning.noren $noLink = true; 12632ca14335SEsther Brunner } else { 12642ca14335SEsther Brunner // add file icons 126527bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 126627bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 12672ca14335SEsther Brunner } 12682ca14335SEsther Brunner 126991df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 127091df343aSAndreas Gohr 12713685f775Sandi //output formatted 1272410ee62aSAnika Henke if($return) { 1273410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1274410ee62aSAnika Henke else return $this->_formatLink($link); 1275410ee62aSAnika Henke } else { 1276dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12772ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12780cecf9d5Sandi } 1279410ee62aSAnika Henke } 12800cecf9d5Sandi 12814826ab45Sandi /** 12823db95becSAndreas Gohr * Renders an RSS feed 1283b625487dSandi * 12840c4c0281SGerrit Uitslag * @param string $url URL of the feed 12850c4c0281SGerrit Uitslag * @param array $params Finetuning of the output 12860c4c0281SGerrit Uitslag * 1287b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1288b625487dSandi */ 1289*de369923SAndreas Gohr public function rss($url, $params) { 1290b625487dSandi global $lang; 12913db95becSAndreas Gohr global $conf; 12923db95becSAndreas Gohr 12933db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 12943db95becSAndreas Gohr $feed = new FeedParser(); 129500077af8SAndreas Gohr $feed->set_feed_url($url); 1296b625487dSandi 1297b625487dSandi //disable warning while fetching 12983dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 12993dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 13003dd5c225SAndreas Gohr } 13013db95becSAndreas Gohr $rc = $feed->init(); 13023dd5c225SAndreas Gohr if(isset($elvl)) { 13033dd5c225SAndreas Gohr error_reporting($elvl); 13043dd5c225SAndreas Gohr } 1305b625487dSandi 130638c6f603SRobin H. Johnson if($params['nosort']) $feed->enable_order_by_date(false); 130738c6f603SRobin H. Johnson 13083db95becSAndreas Gohr //decide on start and end 13093db95becSAndreas Gohr if($params['reverse']) { 13103db95becSAndreas Gohr $mod = -1; 13113db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 13123db95becSAndreas Gohr $end = $start - ($params['max']); 1313b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 13143db95becSAndreas Gohr } else { 13153db95becSAndreas Gohr $mod = 1; 13163db95becSAndreas Gohr $start = 0; 13173db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1318d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 13193db95becSAndreas Gohr } 13203db95becSAndreas Gohr 1321a2d649c4Sandi $this->doc .= '<ul class="rss">'; 13223db95becSAndreas Gohr if($rc) { 13233db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 13241bde1582SAndreas Gohr $item = $feed->get_item($x); 13253db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1326d2ea3363SAndreas Gohr // support feeds without links 1327d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1328d2ea3363SAndreas Gohr if($lnkurl) { 1329793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1330793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 13313dd5c225SAndreas Gohr $this->externallink( 13323dd5c225SAndreas Gohr $item->get_permalink(), 13333dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 13343dd5c225SAndreas Gohr ); 1335d2ea3363SAndreas Gohr } else { 1336d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1337d2ea3363SAndreas Gohr } 13383db95becSAndreas Gohr if($params['author']) { 13391bde1582SAndreas Gohr $author = $item->get_author(0); 13401bde1582SAndreas Gohr if($author) { 13411bde1582SAndreas Gohr $name = $author->get_name(); 13421bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 1343163c2842SPhy if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name); 13441bde1582SAndreas Gohr } 13453db95becSAndreas Gohr } 13463db95becSAndreas Gohr if($params['date']) { 13472e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 13483db95becSAndreas Gohr } 13491bde1582SAndreas Gohr if($params['details']) { 13503db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1351173dccb7STom N Harris if($conf['htmlok']) { 13521bde1582SAndreas Gohr $this->doc .= $item->get_description(); 13533db95becSAndreas Gohr } else { 13541bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 13553db95becSAndreas Gohr } 13563db95becSAndreas Gohr $this->doc .= '</div>'; 13573db95becSAndreas Gohr } 13583db95becSAndreas Gohr 13593db95becSAndreas Gohr $this->doc .= '</div></li>'; 1360b625487dSandi } 1361b625487dSandi } else { 13623db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1363a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1364b625487dSandi $this->externallink($url); 136545e147ccSAndreas Gohr if($conf['allowdebug']) { 136645e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 136745e147ccSAndreas Gohr } 13683db95becSAndreas Gohr $this->doc .= '</div></li>'; 1369b625487dSandi } 1370a2d649c4Sandi $this->doc .= '</ul>'; 1371b625487dSandi } 1372b625487dSandi 13733dd5c225SAndreas Gohr /** 13743dd5c225SAndreas Gohr * Start a table 13753dd5c225SAndreas Gohr * 13763dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 13773dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 13783dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13797d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 13803dd5c225SAndreas Gohr */ 1381*de369923SAndreas Gohr public function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 1382b5742cedSPierre Spring // initialize the row counter used for classes 1383b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1384619736fdSAdrian Lang $class = 'table'; 13850c4c0281SGerrit Uitslag if($classes !== null) { 13862e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 13870c4c0281SGerrit Uitslag $class .= ' ' . $classes; 13880c4c0281SGerrit Uitslag } 1389619736fdSAdrian Lang if($pos !== null) { 139006917fceSMichael Große $hid = $this->_headerToLink($class, true); 1391ec57f119SLarsDW223 $data = array(); 1392ec57f119SLarsDW223 $data['target'] = 'table'; 1393ec57f119SLarsDW223 $data['name'] = ''; 1394ec57f119SLarsDW223 $data['hid'] = $hid; 1395ec57f119SLarsDW223 $class .= ' '.$this->startSectionEdit($pos, $data); 1396619736fdSAdrian Lang } 1397619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1398619736fdSAdrian Lang DOKU_LF; 13990cecf9d5Sandi } 14000cecf9d5Sandi 14013dd5c225SAndreas Gohr /** 14023dd5c225SAndreas Gohr * Close a table 14033dd5c225SAndreas Gohr * 14043dd5c225SAndreas Gohr * @param int $pos byte position in the original source 14053dd5c225SAndreas Gohr */ 1406*de369923SAndreas Gohr public function table_close($pos = null) { 1407a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1408619736fdSAdrian Lang if($pos !== null) { 140990df9a4dSAdrian Lang $this->finishSectionEdit($pos); 14100cecf9d5Sandi } 1411619736fdSAdrian Lang } 14120cecf9d5Sandi 14133dd5c225SAndreas Gohr /** 14143dd5c225SAndreas Gohr * Open a table header 14153dd5c225SAndreas Gohr */ 1416*de369923SAndreas Gohr public function tablethead_open() { 1417f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1418f05a1cc5SGerrit Uitslag } 1419f05a1cc5SGerrit Uitslag 14203dd5c225SAndreas Gohr /** 14213dd5c225SAndreas Gohr * Close a table header 14223dd5c225SAndreas Gohr */ 1423*de369923SAndreas Gohr public function tablethead_close() { 1424f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1425f05a1cc5SGerrit Uitslag } 1426f05a1cc5SGerrit Uitslag 14273dd5c225SAndreas Gohr /** 14285a93f869SAnika Henke * Open a table body 14295a93f869SAnika Henke */ 1430*de369923SAndreas Gohr public function tabletbody_open() { 14315a93f869SAnika Henke $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF; 14325a93f869SAnika Henke } 14335a93f869SAnika Henke 14345a93f869SAnika Henke /** 14355a93f869SAnika Henke * Close a table body 14365a93f869SAnika Henke */ 1437*de369923SAndreas Gohr public function tabletbody_close() { 14385a93f869SAnika Henke $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF; 14395a93f869SAnika Henke } 14405a93f869SAnika Henke 14415a93f869SAnika Henke /** 1442d2a99739SAndreas Gohr * Open a table footer 1443d2a99739SAndreas Gohr */ 1444*de369923SAndreas Gohr public function tabletfoot_open() { 144544f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF; 1446d2a99739SAndreas Gohr } 1447d2a99739SAndreas Gohr 1448d2a99739SAndreas Gohr /** 1449d2a99739SAndreas Gohr * Close a table footer 1450d2a99739SAndreas Gohr */ 1451*de369923SAndreas Gohr public function tabletfoot_close() { 145244f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF; 1453d2a99739SAndreas Gohr } 1454d2a99739SAndreas Gohr 1455d2a99739SAndreas Gohr /** 14563dd5c225SAndreas Gohr * Open a table row 14570c4c0281SGerrit Uitslag * 14587d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14593dd5c225SAndreas Gohr */ 1460*de369923SAndreas Gohr public function tablerow_open($classes = null) { 1461b5742cedSPierre Spring // initialize the cell counter used for classes 1462b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1463b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 14640c4c0281SGerrit Uitslag if($classes !== null) { 14652e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14660c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14670c4c0281SGerrit Uitslag } 1468b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 14690cecf9d5Sandi } 14700cecf9d5Sandi 14713dd5c225SAndreas Gohr /** 14723dd5c225SAndreas Gohr * Close a table row 14733dd5c225SAndreas Gohr */ 1474*de369923SAndreas Gohr public function tablerow_close() { 1475a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 14760cecf9d5Sandi } 14770cecf9d5Sandi 14783dd5c225SAndreas Gohr /** 14793dd5c225SAndreas Gohr * Open a table header cell 14803dd5c225SAndreas Gohr * 14813dd5c225SAndreas Gohr * @param int $colspan 14823dd5c225SAndreas Gohr * @param string $align left|center|right 14833dd5c225SAndreas Gohr * @param int $rowspan 14847d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14853dd5c225SAndreas Gohr */ 1486*de369923SAndreas Gohr public function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1487b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14880cecf9d5Sandi if(!is_null($align)) { 1489b5742cedSPierre Spring $class .= ' '.$align.'align'; 14900cecf9d5Sandi } 14910c4c0281SGerrit Uitslag if($classes !== null) { 14922e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14930c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14940c4c0281SGerrit Uitslag } 1495b5742cedSPierre Spring $class .= '"'; 1496b5742cedSPierre Spring $this->doc .= '<th '.$class; 14970cecf9d5Sandi if($colspan > 1) { 1498a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1499a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 15000cecf9d5Sandi } 150125b97867Shakan.sandell if($rowspan > 1) { 150225b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 150325b97867Shakan.sandell } 1504a2d649c4Sandi $this->doc .= '>'; 15050cecf9d5Sandi } 15060cecf9d5Sandi 15073dd5c225SAndreas Gohr /** 15083dd5c225SAndreas Gohr * Close a table header cell 15093dd5c225SAndreas Gohr */ 1510*de369923SAndreas Gohr public function tableheader_close() { 1511a2d649c4Sandi $this->doc .= '</th>'; 15120cecf9d5Sandi } 15130cecf9d5Sandi 15143dd5c225SAndreas Gohr /** 15153dd5c225SAndreas Gohr * Open a table cell 15163dd5c225SAndreas Gohr * 15173dd5c225SAndreas Gohr * @param int $colspan 15183dd5c225SAndreas Gohr * @param string $align left|center|right 15193dd5c225SAndreas Gohr * @param int $rowspan 15207d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 15213dd5c225SAndreas Gohr */ 1522*de369923SAndreas Gohr public function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1523b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 15240cecf9d5Sandi if(!is_null($align)) { 1525b5742cedSPierre Spring $class .= ' '.$align.'align'; 15260cecf9d5Sandi } 15270c4c0281SGerrit Uitslag if($classes !== null) { 15282e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 15290c4c0281SGerrit Uitslag $class .= ' ' . $classes; 15300c4c0281SGerrit Uitslag } 1531b5742cedSPierre Spring $class .= '"'; 1532b5742cedSPierre Spring $this->doc .= '<td '.$class; 15330cecf9d5Sandi if($colspan > 1) { 1534a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1535a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 15360cecf9d5Sandi } 153725b97867Shakan.sandell if($rowspan > 1) { 153825b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 153925b97867Shakan.sandell } 1540a2d649c4Sandi $this->doc .= '>'; 15410cecf9d5Sandi } 15420cecf9d5Sandi 15433dd5c225SAndreas Gohr /** 15443dd5c225SAndreas Gohr * Close a table cell 15453dd5c225SAndreas Gohr */ 1546*de369923SAndreas Gohr public function tablecell_close() { 1547a2d649c4Sandi $this->doc .= '</td>'; 15480cecf9d5Sandi } 15490cecf9d5Sandi 1550cea664bdSLarsDW223 /** 1551cea664bdSLarsDW223 * Returns the current header level. 1552cea664bdSLarsDW223 * (required e.g. by the filelist plugin) 1553cea664bdSLarsDW223 * 1554cea664bdSLarsDW223 * @return int The current header level 1555cea664bdSLarsDW223 */ 1556*de369923SAndreas Gohr public function getLastlevel() { 1557cea664bdSLarsDW223 return $this->lastlevel; 1558cea664bdSLarsDW223 } 1559cea664bdSLarsDW223 15603dd5c225SAndreas Gohr #region Utility functions 15610cecf9d5Sandi 1562ba11bd29Sandi /** 15633fd0b676Sandi * Build a link 15643fd0b676Sandi * 15653fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1566ba11bd29Sandi * 15670c4c0281SGerrit Uitslag * @param array $link attributes of a link 15680c4c0281SGerrit Uitslag * @return string 15690c4c0281SGerrit Uitslag * 1570ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1571ba11bd29Sandi */ 1572*de369923SAndreas Gohr public function _formatLink($link) { 1573ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1574ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1575ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1576ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1577ba11bd29Sandi } 1578ba11bd29Sandi //remove double encodings in titles 1579ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1580ba11bd29Sandi 1581453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1582453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1583453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1584453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1585453493f2SAndreas Gohr 1586ba11bd29Sandi $ret = ''; 1587ba11bd29Sandi $ret .= $link['pre']; 1588ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1589bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1590bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1591bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1592bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 1593914045f3SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"'; 1594bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1595ba11bd29Sandi $ret .= '>'; 1596ba11bd29Sandi $ret .= $link['name']; 1597ba11bd29Sandi $ret .= '</a>'; 1598ba11bd29Sandi $ret .= $link['suf']; 1599ba11bd29Sandi return $ret; 1600ba11bd29Sandi } 1601ba11bd29Sandi 1602ba11bd29Sandi /** 16033fd0b676Sandi * Renders internal and external media 16043fd0b676Sandi * 16053fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 16063dd5c225SAndreas Gohr * @param string $src media ID 16073dd5c225SAndreas Gohr * @param string $title descriptive text 16083dd5c225SAndreas Gohr * @param string $align left|center|right 16093dd5c225SAndreas Gohr * @param int $width width of media in pixel 16103dd5c225SAndreas Gohr * @param int $height height of media in pixel 16113dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 16123dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 16133dd5c225SAndreas Gohr * @return string 16143fd0b676Sandi */ 1615*de369923SAndreas Gohr public function _media($src, $title = null, $align = null, $width = null, 16160ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 16173fd0b676Sandi 16183fd0b676Sandi $ret = ''; 16193fd0b676Sandi 16203dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 16213fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1622b739ff0fSPierre Spring // first get the $title 1623b739ff0fSPierre Spring if(!is_null($title)) { 1624b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1625b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1626b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1627b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 162867f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1629b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 16303dd5c225SAndreas Gohr if(!empty($cap)) { 1631b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1632b739ff0fSPierre Spring } 1633b739ff0fSPierre Spring } 1634b739ff0fSPierre Spring if(!$render) { 1635b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1636b739ff0fSPierre Spring // return the title of the picture 1637b739ff0fSPierre Spring if(!$title) { 1638b739ff0fSPierre Spring // just show the sourcename 16393009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1640b739ff0fSPierre Spring } 1641b739ff0fSPierre Spring return $title; 1642b739ff0fSPierre Spring } 16433fd0b676Sandi //add image tag 164464159a61SAndreas Gohr $ret .= '<img src="' . ml( 164564159a61SAndreas Gohr $src, 164664159a61SAndreas Gohr array( 164764159a61SAndreas Gohr 'w' => $width, 'h' => $height, 164864159a61SAndreas Gohr 'cache' => $cache, 164964159a61SAndreas Gohr 'rev' => $this->_getLastMediaRevisionAt($src) 165064159a61SAndreas Gohr ) 165164159a61SAndreas Gohr ) . '"'; 16523fd0b676Sandi $ret .= ' class="media'.$align.'"'; 16533fd0b676Sandi 1654b739ff0fSPierre Spring if($title) { 1655b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1656b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 16573fd0b676Sandi } else { 16583fd0b676Sandi $ret .= ' alt=""'; 16593fd0b676Sandi } 16603fd0b676Sandi 16613fd0b676Sandi if(!is_null($width)) 16623fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 16633fd0b676Sandi 16643fd0b676Sandi if(!is_null($height)) 16653fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 16663fd0b676Sandi 16673fd0b676Sandi $ret .= ' />'; 16683fd0b676Sandi 166917954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 16702a2a2ba2SAnika Henke // first get the $title 167117954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 16722a2a2ba2SAnika Henke if(!$render) { 167317954bb5SAnika Henke // if the file is not supposed to be rendered 167417954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 167517954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 16762a2a2ba2SAnika Henke } 16772a2a2ba2SAnika Henke 16782a2a2ba2SAnika Henke $att = array(); 16792a2a2ba2SAnika Henke $att['class'] = "media$align"; 168017954bb5SAnika Henke if($title) { 168117954bb5SAnika Henke $att['title'] = $title; 168217954bb5SAnika Henke } 16832a2a2ba2SAnika Henke 168417954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 168517954bb5SAnika Henke //add video 168679e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1687b44a5dceSAnika Henke } 168817954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1689b44a5dceSAnika Henke //add audio 1690b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 169117954bb5SAnika Henke } 1692b44a5dceSAnika Henke 16933fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 16941c882ba8SAndreas Gohr if(!$render) { 16951c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 16961c882ba8SAndreas Gohr // return the title of the flash 16971c882ba8SAndreas Gohr if(!$title) { 16981c882ba8SAndreas Gohr // just show the sourcename 16993009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 17001c882ba8SAndreas Gohr } 170107bf32b2SAndreas Gohr return $this->_xmlEntities($title); 17021c882ba8SAndreas Gohr } 17031c882ba8SAndreas Gohr 170407bf32b2SAndreas Gohr $att = array(); 170507bf32b2SAndreas Gohr $att['class'] = "media$align"; 170607bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 170707bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 17083dd5c225SAndreas Gohr $ret .= html_flashobject( 17093dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 171007bf32b2SAndreas Gohr array('quality' => 'high'), 171107bf32b2SAndreas Gohr null, 171207bf32b2SAndreas Gohr $att, 17133dd5c225SAndreas Gohr $this->_xmlEntities($title) 17143dd5c225SAndreas Gohr ); 17150f428d7dSAndreas Gohr } elseif($title) { 17163fd0b676Sandi // well at least we have a title to display 17173fd0b676Sandi $ret .= $this->_xmlEntities($title); 17183fd0b676Sandi } else { 17195291ca3aSAndreas Gohr // just show the sourcename 17203009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 17213fd0b676Sandi } 17223fd0b676Sandi 17233fd0b676Sandi return $ret; 17243fd0b676Sandi } 17253fd0b676Sandi 17263dd5c225SAndreas Gohr /** 17273dd5c225SAndreas Gohr * Escape string for output 17283dd5c225SAndreas Gohr * 17293dd5c225SAndreas Gohr * @param $string 17303dd5c225SAndreas Gohr * @return string 17313dd5c225SAndreas Gohr */ 1732*de369923SAndreas Gohr public function _xmlEntities($string) { 1733de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 17340cecf9d5Sandi } 17350cecf9d5Sandi 1736*de369923SAndreas Gohr 17370cecf9d5Sandi 1738af587fa8Sandi /** 17393fd0b676Sandi * Construct a title and handle images in titles 17403fd0b676Sandi * 17410b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 17423dd5c225SAndreas Gohr * @param string|array $title either string title or media array 17433dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 17443dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 17453dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 17463dd5c225SAndreas Gohr * @param string $linktype content|navigation 17473dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 17483fd0b676Sandi */ 1749*de369923SAndreas Gohr public function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 175044881bd0Shenning.noren $isImage = false; 175129657f9eSAndreas Gohr if(is_array($title)) { 175229657f9eSAndreas Gohr $isImage = true; 175329657f9eSAndreas Gohr return $this->_imageTitle($title); 175429657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1755fe9ec250SChris Smith if(useHeading($linktype) && $id) { 175667c15eceSMichael Hamann $heading = p_get_first_heading($id); 1757f515db7fSAndreas Gohr if(!blank($heading)) { 1758433bef32Sandi return $this->_xmlEntities($heading); 1759bb0a59d4Sjan } 1760bb0a59d4Sjan } 1761433bef32Sandi return $this->_xmlEntities($default); 176268c26e6dSMichael Klier } else { 176368c26e6dSMichael Klier return $this->_xmlEntities($title); 17640cecf9d5Sandi } 17650cecf9d5Sandi } 17660cecf9d5Sandi 17670cecf9d5Sandi /** 17683dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 17693fd0b676Sandi * 17703fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1771e0c26282SGerrit Uitslag * @param array $img 17723dd5c225SAndreas Gohr * @return string HTML img tag or similar 17730cecf9d5Sandi */ 1774*de369923SAndreas Gohr public function _imageTitle($img) { 1775d9baf1a7SKazutaka Miyasaka global $ID; 1776d9baf1a7SKazutaka Miyasaka 1777d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1778d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 17793dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1780d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1781cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1782d9baf1a7SKazutaka Miyasaka } 1783d9baf1a7SKazutaka Miyasaka 17843dd5c225SAndreas Gohr return $this->_media( 17853dd5c225SAndreas Gohr $img['src'], 17864826ab45Sandi $img['title'], 17874826ab45Sandi $img['align'], 17884826ab45Sandi $img['width'], 17894826ab45Sandi $img['height'], 17903dd5c225SAndreas Gohr $img['cache'] 17913dd5c225SAndreas Gohr ); 17920cecf9d5Sandi } 1793b739ff0fSPierre Spring 1794b739ff0fSPierre Spring /** 17953dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 17963dd5c225SAndreas Gohr * 17973dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1798b739ff0fSPierre Spring * 1799b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 18003dd5c225SAndreas Gohr * @param string $src media ID 18013dd5c225SAndreas Gohr * @param string $title descriptive text 18023dd5c225SAndreas Gohr * @param string $align left|center|right 18033dd5c225SAndreas Gohr * @param int $width width of media in pixel 18043dd5c225SAndreas Gohr * @param int $height height of media in pixel 18053dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 18063dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 18073dd5c225SAndreas Gohr * @return array associative array with link config 1808b739ff0fSPierre Spring */ 1809*de369923SAndreas Gohr public function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1810b739ff0fSPierre Spring global $conf; 1811b739ff0fSPierre Spring 1812b739ff0fSPierre Spring $link = array(); 1813b739ff0fSPierre Spring $link['class'] = 'media'; 1814b739ff0fSPierre Spring $link['style'] = ''; 1815b739ff0fSPierre Spring $link['pre'] = ''; 1816b739ff0fSPierre Spring $link['suf'] = ''; 1817b739ff0fSPierre Spring $link['more'] = ''; 1818b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1819bc3d2252SAndreas Gohr if($conf['target']['media']) $link['rel'] = 'noopener'; 1820b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1821b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1822b739ff0fSPierre Spring 1823b739ff0fSPierre Spring return $link; 1824b739ff0fSPierre Spring } 182591459163SAnika Henke 18262a2a2ba2SAnika Henke /** 18272a2a2ba2SAnika Henke * Embed video(s) in HTML 18282a2a2ba2SAnika Henke * 18292a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 18300877a1f1SSchplurtz le Déboulonné * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> 18312a2a2ba2SAnika Henke * 18322a2a2ba2SAnika Henke * @param string $src - ID of video to embed 18332a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 18342a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 18352a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1836f50634f0SAnika Henke * @return string 18372a2a2ba2SAnika Henke */ 1838*de369923SAndreas Gohr public function _video($src, $width, $height, $atts = null) { 18392a2a2ba2SAnika Henke // prepare width and height 18402a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 18412a2a2ba2SAnika Henke $atts['width'] = (int) $width; 18422a2a2ba2SAnika Henke $atts['height'] = (int) $height; 18432a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 18442a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 18452a2a2ba2SAnika Henke 1846410ee62aSAnika Henke $posterUrl = ''; 1847410ee62aSAnika Henke $files = array(); 18480877a1f1SSchplurtz le Déboulonné $tracks = array(); 1849410ee62aSAnika Henke $isExternal = media_isexternal($src); 1850410ee62aSAnika Henke 1851410ee62aSAnika Henke if ($isExternal) { 1852410ee62aSAnika Henke // take direct source for external files 1853702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1854410ee62aSAnika Henke $files[$srcMime] = $src; 1855410ee62aSAnika Henke } else { 18563d7a9e0aSAnika Henke // prepare alternative formats 18573d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1858410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 185959bc3b48SGerrit Uitslag $poster = media_alternativefiles($src, array('jpg', 'png')); 18600877a1f1SSchplurtz le Déboulonné $tracks = media_trackfiles($src); 186199f943f6SAnika Henke if(!empty($poster)) { 18622d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 186399f943f6SAnika Henke } 1864410ee62aSAnika Henke } 18652a2a2ba2SAnika Henke 1866f50634f0SAnika Henke $out = ''; 186779e53fe5SAnika Henke // open video tag 1868f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 18693641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1870f50634f0SAnika Henke $out .= '>'.NL; 18713641199aSAnika Henke $fallback = ''; 187279e53fe5SAnika Henke 187379e53fe5SAnika Henke // output source for each alternative video format 1874410ee62aSAnika Henke foreach($files as $mime => $file) { 1875410ee62aSAnika Henke if ($isExternal) { 1876410ee62aSAnika Henke $url = $file; 1877410ee62aSAnika Henke $linkType = 'externalmedia'; 1878410ee62aSAnika Henke } else { 18792d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1880410ee62aSAnika Henke $linkType = 'internalmedia'; 1881410ee62aSAnika Henke } 188217954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 18833d7a9e0aSAnika Henke 1884f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 188579e53fe5SAnika Henke // alternative content (just a link to the file) 188664159a61SAndreas Gohr $fallback .= $this->$linkType( 188764159a61SAndreas Gohr $file, 188864159a61SAndreas Gohr $title, 188964159a61SAndreas Gohr null, 189064159a61SAndreas Gohr null, 189164159a61SAndreas Gohr null, 189264159a61SAndreas Gohr $cache = null, 189364159a61SAndreas Gohr $linking = 'linkonly', 189464159a61SAndreas Gohr $return = true 189564159a61SAndreas Gohr ); 18963d7a9e0aSAnika Henke } 18972a2a2ba2SAnika Henke 18980877a1f1SSchplurtz le Déboulonné // output each track if any 18990877a1f1SSchplurtz le Déboulonné foreach( $tracks as $trackid => $info ) { 190023c61bbeSSchplurtz le Déboulonné list( $kind, $srclang ) = array_map( 'hsc', $info ); 190123c61bbeSSchplurtz le Déboulonné $out .= "<track kind=\"$kind\" srclang=\"$srclang\" "; 190223c61bbeSSchplurtz le Déboulonné $out .= "label=\"$srclang\" "; 19030877a1f1SSchplurtz le Déboulonné $out .= 'src="'.ml($trackid, '', true).'">'.NL; 19040877a1f1SSchplurtz le Déboulonné } 19050877a1f1SSchplurtz le Déboulonné 19062a2a2ba2SAnika Henke // finish 19073641199aSAnika Henke $out .= $fallback; 1908f50634f0SAnika Henke $out .= '</video>'.NL; 1909f50634f0SAnika Henke return $out; 19102a2a2ba2SAnika Henke } 19112a2a2ba2SAnika Henke 1912b44a5dceSAnika Henke /** 1913b44a5dceSAnika Henke * Embed audio in HTML 1914b44a5dceSAnika Henke * 1915b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1916b44a5dceSAnika Henke * 1917b44a5dceSAnika Henke * @param string $src - ID of audio to embed 19186d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1919f50634f0SAnika Henke * @return string 1920b44a5dceSAnika Henke */ 1921*de369923SAndreas Gohr public function _audio($src, $atts = array()) { 1922702e97d3SAnika Henke $files = array(); 1923410ee62aSAnika Henke $isExternal = media_isexternal($src); 1924b44a5dceSAnika Henke 1925410ee62aSAnika Henke if ($isExternal) { 1926410ee62aSAnika Henke // take direct source for external files 1927702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1928410ee62aSAnika Henke $files[$srcMime] = $src; 1929410ee62aSAnika Henke } else { 1930b44a5dceSAnika Henke // prepare alternative formats 1931b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1932410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1933410ee62aSAnika Henke } 1934b44a5dceSAnika Henke 1935f50634f0SAnika Henke $out = ''; 1936b44a5dceSAnika Henke // open audio tag 1937f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 19383641199aSAnika Henke $fallback = ''; 1939b44a5dceSAnika Henke 1940b44a5dceSAnika Henke // output source for each alternative audio format 1941410ee62aSAnika Henke foreach($files as $mime => $file) { 1942410ee62aSAnika Henke if ($isExternal) { 1943410ee62aSAnika Henke $url = $file; 1944410ee62aSAnika Henke $linkType = 'externalmedia'; 1945410ee62aSAnika Henke } else { 19462d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1947410ee62aSAnika Henke $linkType = 'internalmedia'; 1948410ee62aSAnika Henke } 194917954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1950b44a5dceSAnika Henke 1951f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1952b44a5dceSAnika Henke // alternative content (just a link to the file) 195364159a61SAndreas Gohr $fallback .= $this->$linkType( 195464159a61SAndreas Gohr $file, 195564159a61SAndreas Gohr $title, 195664159a61SAndreas Gohr null, 195764159a61SAndreas Gohr null, 195864159a61SAndreas Gohr null, 195964159a61SAndreas Gohr $cache = null, 196064159a61SAndreas Gohr $linking = 'linkonly', 196164159a61SAndreas Gohr $return = true 196264159a61SAndreas Gohr ); 1963b44a5dceSAnika Henke } 1964b44a5dceSAnika Henke 1965b44a5dceSAnika Henke // finish 19663641199aSAnika Henke $out .= $fallback; 1967f50634f0SAnika Henke $out .= '</audio>'.NL; 1968f50634f0SAnika Henke return $out; 1969b44a5dceSAnika Henke } 1970b44a5dceSAnika Henke 19715c2eed9aSlisps /** 197252dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 19735c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 19745c2eed9aSlisps * 19755c2eed9aSlisps * @author lisps 19765c2eed9aSlisps * @param string $media_id 19775c2eed9aSlisps * @access protected 19785c2eed9aSlisps * @return string revision ('' for current) 19795c2eed9aSlisps */ 1980*de369923SAndreas Gohr protected function _getLastMediaRevisionAt($media_id){ 198152dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 198278b874e6Slisps $pagelog = new MediaChangeLog($media_id); 198378b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 19845c2eed9aSlisps } 19855c2eed9aSlisps 19863dd5c225SAndreas Gohr #endregion 19870cecf9d5Sandi} 19880cecf9d5Sandi 1989e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1990