10cecf9d5Sandi<?php 2b625487dSandi/** 3b625487dSandi * Renderer for XHTML output 4b625487dSandi * 5*b4f2363aSAndreas Gohr * This is DokuWiki's main renderer used to display page content in the wiki 6*b4f2363aSAndreas 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(); 174bde2196Slisps var $date_at = ''; // link pages and media against this revision 18c5a8fd96SAndreas Gohr 193dd5c225SAndreas Gohr /** @var int last section edit id, used by startSectionEdit */ 203dd5c225SAndreas Gohr protected $lastsecid = 0; 210cecf9d5Sandi 223dd5c225SAndreas Gohr /** @var array the list of headers used to create unique link ids */ 233dd5c225SAndreas Gohr protected $headers = array(); 243dd5c225SAndreas Gohr 2516ec3e37SAndreas Gohr /** @var array a list of footnotes, list starts at 1! */ 263dd5c225SAndreas Gohr protected $footnotes = array(); 277764a90aSandi 283dd5c225SAndreas Gohr /** @var int current section level */ 293dd5c225SAndreas Gohr protected $lastlevel = 0; 303dd5c225SAndreas Gohr /** @var array section node tracker */ 313dd5c225SAndreas Gohr protected $node = array(0, 0, 0, 0, 0); 323dd5c225SAndreas Gohr 333dd5c225SAndreas Gohr /** @var string temporary $doc store */ 343dd5c225SAndreas Gohr protected $store = ''; 353dd5c225SAndreas Gohr 363dd5c225SAndreas Gohr /** @var array global counter, for table classes etc. */ 373dd5c225SAndreas Gohr protected $_counter = array(); // 383dd5c225SAndreas Gohr 393dd5c225SAndreas Gohr /** @var int counts the code and file blocks, used to provide download links */ 403dd5c225SAndreas Gohr protected $_codeblock = 0; 413dd5c225SAndreas Gohr 423dd5c225SAndreas Gohr /** @var array list of allowed URL schemes */ 433dd5c225SAndreas Gohr protected $schemes = null; 44b5742cedSPierre Spring 4590df9a4dSAdrian Lang /** 4690df9a4dSAdrian Lang * Register a new edit section range 4790df9a4dSAdrian Lang * 4842ea7f44SGerrit Uitslag * @param int $start The byte position for the edit start 49ec57f119SLarsDW223 * @param array $data Associative array with section data: 50ec57f119SLarsDW223 * Key 'name': the section name/title 51ec57f119SLarsDW223 * Key 'target': the target for the section edit, 52ec57f119SLarsDW223 * e.g. 'section' or 'table' 53ec57f119SLarsDW223 * Key 'hid': header id 54ec57f119SLarsDW223 * Key 'codeblockOffset': actual code block index 55ec57f119SLarsDW223 * Key 'start': set in startSectionEdit(), 56ec57f119SLarsDW223 * do not set yourself 57ec57f119SLarsDW223 * Key 'range': calculated from 'start' and 58ec57f119SLarsDW223 * $key in finishSectionEdit(), 59ec57f119SLarsDW223 * do not set yourself 6090df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 6142ea7f44SGerrit Uitslag * 6290df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 6390df9a4dSAdrian Lang */ 64ec57f119SLarsDW223 public function startSectionEdit($start, $data) { 65ec57f119SLarsDW223 if (!is_array($data)) { 66ac025fdfSAndreas Gohr msg( 67ac025fdfSAndreas Gohr sprintf( 68ac025fdfSAndreas Gohr 'startSectionEdit: $data "%s" is NOT an array! One of your plugins needs an update.', 69ac025fdfSAndreas Gohr hsc((string) $data) 70ac025fdfSAndreas Gohr ), -1 71ac025fdfSAndreas Gohr ); 72ac025fdfSAndreas Gohr 73ac025fdfSAndreas Gohr // @deprecated 2018-04-14, backward compatibility 74ac025fdfSAndreas Gohr $args = func_get_args(); 75ac025fdfSAndreas Gohr $data = array(); 76ac025fdfSAndreas Gohr if(isset($args[1])) $data['target'] = $args[1]; 77ac025fdfSAndreas Gohr if(isset($args[2])) $data['name'] = $args[2]; 78ac025fdfSAndreas Gohr if(isset($args[3])) $data['hid'] = $args[3]; 79ec57f119SLarsDW223 } 80ec57f119SLarsDW223 $data['secid'] = ++$this->lastsecid; 81ec57f119SLarsDW223 $data['start'] = $start; 82ec57f119SLarsDW223 $this->sectionedits[] = $data; 83ec57f119SLarsDW223 return 'sectionedit'.$data['secid']; 8490df9a4dSAdrian Lang } 8590df9a4dSAdrian Lang 8690df9a4dSAdrian Lang /** 8790df9a4dSAdrian Lang * Finish an edit section range 8890df9a4dSAdrian Lang * 8942ea7f44SGerrit Uitslag * @param int $end The byte position for the edit end; null for the rest of the page 9042ea7f44SGerrit Uitslag * 9190df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 9290df9a4dSAdrian Lang */ 932571786cSLarsDW223 public function finishSectionEdit($end = null, $hid = null) { 94ec57f119SLarsDW223 $data = array_pop($this->sectionedits); 95ec57f119SLarsDW223 if(!is_null($end) && $end <= $data['start']) { 9600c13053SAdrian Lang return; 9700c13053SAdrian Lang } 982571786cSLarsDW223 if(!is_null($hid)) { 99ec57f119SLarsDW223 $data['hid'] .= $hid; 1002571786cSLarsDW223 } 101ec57f119SLarsDW223 $data['range'] = $data['start'].'-'.(is_null($end) ? '' : $end); 102ec57f119SLarsDW223 unset($data['start']); 103ada0d779SMichael Hamann $this->doc .= '<!-- EDIT'.hsc(json_encode ($data)).' -->'; 10490df9a4dSAdrian Lang } 10590df9a4dSAdrian Lang 1063dd5c225SAndreas Gohr /** 1073dd5c225SAndreas Gohr * Returns the format produced by this renderer. 1083dd5c225SAndreas Gohr * 1093dd5c225SAndreas Gohr * @return string always 'xhtml' 1103dd5c225SAndreas Gohr */ 1115f70445dSAndreas Gohr function getFormat() { 1125f70445dSAndreas Gohr return 'xhtml'; 1135f70445dSAndreas Gohr } 1145f70445dSAndreas Gohr 1153dd5c225SAndreas Gohr /** 1163dd5c225SAndreas Gohr * Initialize the document 1173dd5c225SAndreas Gohr */ 1180cecf9d5Sandi function document_start() { 119c5a8fd96SAndreas Gohr //reset some internals 120c5a8fd96SAndreas Gohr $this->toc = array(); 121c5a8fd96SAndreas Gohr $this->headers = array(); 1220cecf9d5Sandi } 1230cecf9d5Sandi 1243dd5c225SAndreas Gohr /** 1253dd5c225SAndreas Gohr * Finalize the document 1263dd5c225SAndreas Gohr */ 1270cecf9d5Sandi function document_end() { 12890df9a4dSAdrian Lang // Finish open section edits. 12990df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 130ec57f119SLarsDW223 if($this->sectionedits[count($this->sectionedits) - 1]['start'] <= 1) { 13190df9a4dSAdrian Lang // If there is only one section, do not write a section edit 13290df9a4dSAdrian Lang // marker. 13390df9a4dSAdrian Lang array_pop($this->sectionedits); 13490df9a4dSAdrian Lang } else { 135d9e36cbeSAdrian Lang $this->finishSectionEdit(); 13690df9a4dSAdrian Lang } 13790df9a4dSAdrian Lang } 13890df9a4dSAdrian Lang 1390cecf9d5Sandi if(count($this->footnotes) > 0) { 140a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 141d74aace9Schris 14216ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 143d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 144d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 145d74aace9Schris 146d74aace9Schris // open the footnote and set the anchor and backlink 147d74aace9Schris $this->doc .= '<div class="fn">'; 14816cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 14929bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 150d74aace9Schris 151d74aace9Schris // get any other footnotes that use the same markup 152d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 153d74aace9Schris 154d74aace9Schris if(count($alt)) { 155d74aace9Schris foreach($alt as $ref) { 156d74aace9Schris // set anchor and backlink for the other footnotes 15716ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 15816ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 159d74aace9Schris } 160d74aace9Schris } 161d74aace9Schris 162d74aace9Schris // add footnote markup and close this footnote 163694afa06SAnika Henke $this->doc .= '<div class="content">'.$footnote.'</div>'; 164d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 165d74aace9Schris } 1660cecf9d5Sandi } 167a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1680cecf9d5Sandi } 169c5a8fd96SAndreas Gohr 170b8595a66SAndreas Gohr // Prepare the TOC 171851f2e89SAnika Henke global $conf; 172851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 173b8595a66SAndreas Gohr global $TOC; 174b8595a66SAndreas Gohr $TOC = $this->toc; 1750cecf9d5Sandi } 1763e55d035SAndreas Gohr 1773e55d035SAndreas Gohr // make sure there are no empty paragraphs 17827918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 179e41c4da9SAndreas Gohr } 1800cecf9d5Sandi 1813dd5c225SAndreas Gohr /** 1823dd5c225SAndreas Gohr * Add an item to the TOC 1833dd5c225SAndreas Gohr * 1843dd5c225SAndreas Gohr * @param string $id the hash link 1853dd5c225SAndreas Gohr * @param string $text the text to display 1863dd5c225SAndreas Gohr * @param int $level the nesting level 1873dd5c225SAndreas Gohr */ 188e7856beaSchris function toc_additem($id, $text, $level) { 189af587fa8Sandi global $conf; 190af587fa8Sandi 191c5a8fd96SAndreas Gohr //handle TOC 192c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1937d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 194c5a8fd96SAndreas Gohr } 195e7856beaSchris } 196e7856beaSchris 1973dd5c225SAndreas Gohr /** 1983dd5c225SAndreas Gohr * Render a heading 1993dd5c225SAndreas Gohr * 2003dd5c225SAndreas Gohr * @param string $text the text to display 2013dd5c225SAndreas Gohr * @param int $level header level 2023dd5c225SAndreas Gohr * @param int $pos byte position in the original source 2033dd5c225SAndreas Gohr */ 204e7856beaSchris function header($text, $level, $pos) { 20590df9a4dSAdrian Lang global $conf; 20690df9a4dSAdrian Lang 207f515db7fSAndreas Gohr if(blank($text)) return; //skip empty headlines 208e7856beaSchris 209e7856beaSchris $hid = $this->_headerToLink($text, true); 210e7856beaSchris 211e7856beaSchris //only add items within configured levels 212e7856beaSchris $this->toc_additem($hid, $text, $level); 213c5a8fd96SAndreas Gohr 21491459163SAnika Henke // adjust $node to reflect hierarchy of levels 21591459163SAnika Henke $this->node[$level - 1]++; 21691459163SAnika Henke if($level < $this->lastlevel) { 21791459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 21891459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 21991459163SAnika Henke } 22091459163SAnika Henke } 22191459163SAnika Henke $this->lastlevel = $level; 22291459163SAnika Henke 22390df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 22490df9a4dSAdrian Lang count($this->sectionedits) > 0 && 225ec57f119SLarsDW223 $this->sectionedits[count($this->sectionedits) - 1]['target'] === 'section' 2263dd5c225SAndreas Gohr ) { 2276c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 22890df9a4dSAdrian Lang } 22990df9a4dSAdrian Lang 230c5a8fd96SAndreas Gohr // write the header 23190df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 23290df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 233ec57f119SLarsDW223 $data = array(); 234ec57f119SLarsDW223 $data['target'] = 'section'; 235ec57f119SLarsDW223 $data['name'] = $text; 236ec57f119SLarsDW223 $data['hid'] = $hid; 237ec57f119SLarsDW223 $data['codeblockOffset'] = $this->_codeblock; 238ec57f119SLarsDW223 $this->doc .= ' class="'.$this->startSectionEdit($pos, $data).'"'; 23990df9a4dSAdrian Lang } 24016cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 241a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 24216cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2430cecf9d5Sandi } 2440cecf9d5Sandi 2453dd5c225SAndreas Gohr /** 2463dd5c225SAndreas Gohr * Open a new section 2473dd5c225SAndreas Gohr * 2483dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2493dd5c225SAndreas Gohr */ 2500cecf9d5Sandi function section_open($level) { 2519864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2520cecf9d5Sandi } 2530cecf9d5Sandi 2543dd5c225SAndreas Gohr /** 2553dd5c225SAndreas Gohr * Close the current section 2563dd5c225SAndreas Gohr */ 2570cecf9d5Sandi function section_close() { 258a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2590cecf9d5Sandi } 2600cecf9d5Sandi 2613dd5c225SAndreas Gohr /** 2623dd5c225SAndreas Gohr * Render plain text data 2633dd5c225SAndreas Gohr * 2643dd5c225SAndreas Gohr * @param $text 2653dd5c225SAndreas Gohr */ 2660cecf9d5Sandi function cdata($text) { 267a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2680cecf9d5Sandi } 2690cecf9d5Sandi 2703dd5c225SAndreas Gohr /** 2713dd5c225SAndreas Gohr * Open a paragraph 2723dd5c225SAndreas Gohr */ 2730cecf9d5Sandi function p_open() { 27459869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2750cecf9d5Sandi } 2760cecf9d5Sandi 2773dd5c225SAndreas Gohr /** 2783dd5c225SAndreas Gohr * Close a paragraph 2793dd5c225SAndreas Gohr */ 2800cecf9d5Sandi function p_close() { 28159869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2820cecf9d5Sandi } 2830cecf9d5Sandi 2843dd5c225SAndreas Gohr /** 2853dd5c225SAndreas Gohr * Create a line break 2863dd5c225SAndreas Gohr */ 2870cecf9d5Sandi function linebreak() { 288a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2890cecf9d5Sandi } 2900cecf9d5Sandi 2913dd5c225SAndreas Gohr /** 2923dd5c225SAndreas Gohr * Create a horizontal line 2933dd5c225SAndreas Gohr */ 2940cecf9d5Sandi function hr() { 2954beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2960cecf9d5Sandi } 2970cecf9d5Sandi 2983dd5c225SAndreas Gohr /** 2993dd5c225SAndreas Gohr * Start strong (bold) formatting 3003dd5c225SAndreas Gohr */ 3010cecf9d5Sandi function strong_open() { 302a2d649c4Sandi $this->doc .= '<strong>'; 3030cecf9d5Sandi } 3040cecf9d5Sandi 3053dd5c225SAndreas Gohr /** 3063dd5c225SAndreas Gohr * Stop strong (bold) formatting 3073dd5c225SAndreas Gohr */ 3080cecf9d5Sandi function strong_close() { 309a2d649c4Sandi $this->doc .= '</strong>'; 3100cecf9d5Sandi } 3110cecf9d5Sandi 3123dd5c225SAndreas Gohr /** 3133dd5c225SAndreas Gohr * Start emphasis (italics) formatting 3143dd5c225SAndreas Gohr */ 3150cecf9d5Sandi function emphasis_open() { 316a2d649c4Sandi $this->doc .= '<em>'; 3170cecf9d5Sandi } 3180cecf9d5Sandi 3193dd5c225SAndreas Gohr /** 3203dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3213dd5c225SAndreas Gohr */ 3220cecf9d5Sandi function emphasis_close() { 323a2d649c4Sandi $this->doc .= '</em>'; 3240cecf9d5Sandi } 3250cecf9d5Sandi 3263dd5c225SAndreas Gohr /** 3273dd5c225SAndreas Gohr * Start underline formatting 3283dd5c225SAndreas Gohr */ 3290cecf9d5Sandi function underline_open() { 33002e51121SAnika Henke $this->doc .= '<em class="u">'; 3310cecf9d5Sandi } 3320cecf9d5Sandi 3333dd5c225SAndreas Gohr /** 3343dd5c225SAndreas Gohr * Stop underline formatting 3353dd5c225SAndreas Gohr */ 3360cecf9d5Sandi function underline_close() { 33702e51121SAnika Henke $this->doc .= '</em>'; 3380cecf9d5Sandi } 3390cecf9d5Sandi 3403dd5c225SAndreas Gohr /** 3413dd5c225SAndreas Gohr * Start monospace formatting 3423dd5c225SAndreas Gohr */ 3430cecf9d5Sandi function monospace_open() { 344a2d649c4Sandi $this->doc .= '<code>'; 3450cecf9d5Sandi } 3460cecf9d5Sandi 3473dd5c225SAndreas Gohr /** 3483dd5c225SAndreas Gohr * Stop monospace formatting 3493dd5c225SAndreas Gohr */ 3500cecf9d5Sandi function monospace_close() { 351a2d649c4Sandi $this->doc .= '</code>'; 3520cecf9d5Sandi } 3530cecf9d5Sandi 3543dd5c225SAndreas Gohr /** 3553dd5c225SAndreas Gohr * Start a subscript 3563dd5c225SAndreas Gohr */ 3570cecf9d5Sandi function subscript_open() { 358a2d649c4Sandi $this->doc .= '<sub>'; 3590cecf9d5Sandi } 3600cecf9d5Sandi 3613dd5c225SAndreas Gohr /** 3623dd5c225SAndreas Gohr * Stop a subscript 3633dd5c225SAndreas Gohr */ 3640cecf9d5Sandi function subscript_close() { 365a2d649c4Sandi $this->doc .= '</sub>'; 3660cecf9d5Sandi } 3670cecf9d5Sandi 3683dd5c225SAndreas Gohr /** 3693dd5c225SAndreas Gohr * Start a superscript 3703dd5c225SAndreas Gohr */ 3710cecf9d5Sandi function superscript_open() { 372a2d649c4Sandi $this->doc .= '<sup>'; 3730cecf9d5Sandi } 3740cecf9d5Sandi 3753dd5c225SAndreas Gohr /** 3763dd5c225SAndreas Gohr * Stop a superscript 3773dd5c225SAndreas Gohr */ 3780cecf9d5Sandi function superscript_close() { 379a2d649c4Sandi $this->doc .= '</sup>'; 3800cecf9d5Sandi } 3810cecf9d5Sandi 3823dd5c225SAndreas Gohr /** 3833dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3843dd5c225SAndreas Gohr */ 3850cecf9d5Sandi function deleted_open() { 386a2d649c4Sandi $this->doc .= '<del>'; 3870cecf9d5Sandi } 3880cecf9d5Sandi 3893dd5c225SAndreas Gohr /** 3903dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 3913dd5c225SAndreas Gohr */ 3920cecf9d5Sandi function deleted_close() { 393a2d649c4Sandi $this->doc .= '</del>'; 3940cecf9d5Sandi } 3950cecf9d5Sandi 3963fd0b676Sandi /** 3973fd0b676Sandi * Callback for footnote start syntax 3983fd0b676Sandi * 3993fd0b676Sandi * All following content will go to the footnote instead of 400d74aace9Schris * the document. To achieve this the previous rendered content 4013fd0b676Sandi * is moved to $store and $doc is cleared 4023fd0b676Sandi * 4033fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 4043fd0b676Sandi */ 4050cecf9d5Sandi function footnote_open() { 4067764a90aSandi 4077764a90aSandi // move current content to store and record footnote 4087764a90aSandi $this->store = $this->doc; 4097764a90aSandi $this->doc = ''; 4100cecf9d5Sandi } 4110cecf9d5Sandi 4123fd0b676Sandi /** 4133fd0b676Sandi * Callback for footnote end syntax 4143fd0b676Sandi * 4153fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 4163fd0b676Sandi * content is restored from $store again 4173fd0b676Sandi * 4183fd0b676Sandi * @author Andreas Gohr 4193fd0b676Sandi */ 4200cecf9d5Sandi function footnote_close() { 42116ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 42216ec3e37SAndreas Gohr static $fnid = 0; 42316ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 42416ec3e37SAndreas Gohr $fnid++; 4257764a90aSandi 426d74aace9Schris // recover footnote into the stack and restore old content 427d74aace9Schris $footnote = $this->doc; 4287764a90aSandi $this->doc = $this->store; 4297764a90aSandi $this->store = ''; 430d74aace9Schris 431d74aace9Schris // check to see if this footnote has been seen before 432d74aace9Schris $i = array_search($footnote, $this->footnotes); 433d74aace9Schris 434d74aace9Schris if($i === false) { 435d74aace9Schris // its a new footnote, add it to the $footnotes array 43616ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 437d74aace9Schris } else { 43816ec3e37SAndreas Gohr // seen this one before, save a placeholder 43916ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 440d74aace9Schris } 441d74aace9Schris 4426b379cbfSAndreas Gohr // output the footnote reference and link 44316ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4440cecf9d5Sandi } 4450cecf9d5Sandi 4463dd5c225SAndreas Gohr /** 4473dd5c225SAndreas Gohr * Open an unordered list 4480c4c0281SGerrit Uitslag * 4497d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4503dd5c225SAndreas Gohr */ 4510c4c0281SGerrit Uitslag function listu_open($classes = null) { 4520c4c0281SGerrit Uitslag $class = ''; 4530c4c0281SGerrit Uitslag if($classes !== null) { 4542e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4550c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4560c4c0281SGerrit Uitslag } 4570c4c0281SGerrit Uitslag $this->doc .= "<ul$class>".DOKU_LF; 4580cecf9d5Sandi } 4590cecf9d5Sandi 4603dd5c225SAndreas Gohr /** 4613dd5c225SAndreas Gohr * Close an unordered list 4623dd5c225SAndreas Gohr */ 4630cecf9d5Sandi function listu_close() { 464a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4650cecf9d5Sandi } 4660cecf9d5Sandi 4673dd5c225SAndreas Gohr /** 4683dd5c225SAndreas Gohr * Open an ordered list 4690c4c0281SGerrit Uitslag * 4707d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4713dd5c225SAndreas Gohr */ 4720c4c0281SGerrit Uitslag function listo_open($classes = null) { 4730c4c0281SGerrit Uitslag $class = ''; 4740c4c0281SGerrit Uitslag if($classes !== null) { 4752e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4760c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4770c4c0281SGerrit Uitslag } 4780c4c0281SGerrit Uitslag $this->doc .= "<ol$class>".DOKU_LF; 4790cecf9d5Sandi } 4800cecf9d5Sandi 4813dd5c225SAndreas Gohr /** 4823dd5c225SAndreas Gohr * Close an ordered list 4833dd5c225SAndreas Gohr */ 4840cecf9d5Sandi function listo_close() { 485a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4860cecf9d5Sandi } 4870cecf9d5Sandi 4883dd5c225SAndreas Gohr /** 4893dd5c225SAndreas Gohr * Open a list item 4903dd5c225SAndreas Gohr * 4913dd5c225SAndreas Gohr * @param int $level the nesting level 492e3a24861SChristopher Smith * @param bool $node true when a node; false when a leaf 4933dd5c225SAndreas Gohr */ 494e3a24861SChristopher Smith function listitem_open($level, $node=false) { 495e3a24861SChristopher Smith $branching = $node ? ' node' : ''; 496e3a24861SChristopher Smith $this->doc .= '<li class="level'.$level.$branching.'">'; 4970cecf9d5Sandi } 4980cecf9d5Sandi 4993dd5c225SAndreas Gohr /** 5003dd5c225SAndreas Gohr * Close a list item 5013dd5c225SAndreas Gohr */ 5020cecf9d5Sandi function listitem_close() { 503a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 5040cecf9d5Sandi } 5050cecf9d5Sandi 5063dd5c225SAndreas Gohr /** 5073dd5c225SAndreas Gohr * Start the content of a list item 5083dd5c225SAndreas Gohr */ 5090cecf9d5Sandi function listcontent_open() { 51090db23d7Schris $this->doc .= '<div class="li">'; 5110cecf9d5Sandi } 5120cecf9d5Sandi 5133dd5c225SAndreas Gohr /** 5143dd5c225SAndreas Gohr * Stop the content of a list item 5153dd5c225SAndreas Gohr */ 5160cecf9d5Sandi function listcontent_close() { 51759869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 5180cecf9d5Sandi } 5190cecf9d5Sandi 5203dd5c225SAndreas Gohr /** 5213dd5c225SAndreas Gohr * Output unformatted $text 5223dd5c225SAndreas Gohr * 5233dd5c225SAndreas Gohr * Defaults to $this->cdata() 5243dd5c225SAndreas Gohr * 5253dd5c225SAndreas Gohr * @param string $text 5263dd5c225SAndreas Gohr */ 5270cecf9d5Sandi function unformatted($text) { 528a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 5290cecf9d5Sandi } 5300cecf9d5Sandi 5310cecf9d5Sandi /** 5323fd0b676Sandi * Execute PHP code if allowed 5333fd0b676Sandi * 534d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5355d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5365d568b99SChris Smith * 5373fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5380cecf9d5Sandi */ 5395d568b99SChris Smith function php($text, $wrapper = 'code') { 54035a56260SChris Smith global $conf; 54135a56260SChris Smith 542d86d5af0SChris Smith if($conf['phpok']) { 543bad0b545Sandi ob_start(); 5444de671bcSandi eval($text); 5453fd0b676Sandi $this->doc .= ob_get_contents(); 546bad0b545Sandi ob_end_clean(); 547d86d5af0SChris Smith } else { 5485d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 549d86d5af0SChris Smith } 5500cecf9d5Sandi } 5510cecf9d5Sandi 5523dd5c225SAndreas Gohr /** 5533dd5c225SAndreas Gohr * Output block level PHP code 5543dd5c225SAndreas Gohr * 5553dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5563dd5c225SAndreas Gohr * to $doc 5573dd5c225SAndreas Gohr * 5583dd5c225SAndreas Gohr * @param string $text The PHP code 5593dd5c225SAndreas Gohr */ 56007f89c3cSAnika Henke function phpblock($text) { 5615d568b99SChris Smith $this->php($text, 'pre'); 56207f89c3cSAnika Henke } 56307f89c3cSAnika Henke 5640cecf9d5Sandi /** 5653fd0b676Sandi * Insert HTML if allowed 5663fd0b676Sandi * 567d9764001SMichael Hamann * @param string $text html text 5685d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5695d568b99SChris Smith * 5703fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5710cecf9d5Sandi */ 5725d568b99SChris Smith function html($text, $wrapper = 'code') { 57335a56260SChris Smith global $conf; 57435a56260SChris Smith 575d86d5af0SChris Smith if($conf['htmlok']) { 576a2d649c4Sandi $this->doc .= $text; 577d86d5af0SChris Smith } else { 5785d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 579d86d5af0SChris Smith } 5804de671bcSandi } 5810cecf9d5Sandi 5823dd5c225SAndreas Gohr /** 5833dd5c225SAndreas Gohr * Output raw block-level HTML 5843dd5c225SAndreas Gohr * 5853dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 5863dd5c225SAndreas Gohr * 5873dd5c225SAndreas Gohr * @param string $text The HTML 5883dd5c225SAndreas Gohr */ 58907f89c3cSAnika Henke function htmlblock($text) { 5905d568b99SChris Smith $this->html($text, 'pre'); 59107f89c3cSAnika Henke } 59207f89c3cSAnika Henke 5933dd5c225SAndreas Gohr /** 5943dd5c225SAndreas Gohr * Start a block quote 5953dd5c225SAndreas Gohr */ 5960cecf9d5Sandi function quote_open() { 59796331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 5980cecf9d5Sandi } 5990cecf9d5Sandi 6003dd5c225SAndreas Gohr /** 6013dd5c225SAndreas Gohr * Stop a block quote 6023dd5c225SAndreas Gohr */ 6030cecf9d5Sandi function quote_close() { 60496331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 6050cecf9d5Sandi } 6060cecf9d5Sandi 6073dd5c225SAndreas Gohr /** 6083dd5c225SAndreas Gohr * Output preformatted text 6093dd5c225SAndreas Gohr * 6103dd5c225SAndreas Gohr * @param string $text 6113dd5c225SAndreas Gohr */ 6123d491f75SAndreas Gohr function preformatted($text) { 613c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 6143d491f75SAndreas Gohr } 6153d491f75SAndreas Gohr 6163dd5c225SAndreas Gohr /** 6173dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 6183dd5c225SAndreas Gohr * 6193dd5c225SAndreas Gohr * @param string $text text to show 6203dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6213dd5c225SAndreas Gohr * @param string $filename file path label 622e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6233dd5c225SAndreas Gohr */ 624e2d88156SLarsDW223 function file($text, $language = null, $filename = null, $options=null) { 625e2d88156SLarsDW223 $this->_highlight('file', $text, $language, $filename, $options); 6263d491f75SAndreas Gohr } 6273d491f75SAndreas Gohr 6283dd5c225SAndreas Gohr /** 6293dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 6303dd5c225SAndreas Gohr * 6313dd5c225SAndreas Gohr * @param string $text text to show 6323dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6333dd5c225SAndreas Gohr * @param string $filename file path label 634e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6353dd5c225SAndreas Gohr */ 636e2d88156SLarsDW223 function code($text, $language = null, $filename = null, $options=null) { 637e2d88156SLarsDW223 $this->_highlight('code', $text, $language, $filename, $options); 6383d491f75SAndreas Gohr } 6393d491f75SAndreas Gohr 6400cecf9d5Sandi /** 6413d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6423fd0b676Sandi * 6433fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6443dd5c225SAndreas Gohr * @param string $type code|file 6453dd5c225SAndreas Gohr * @param string $text text to show 6463dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6473dd5c225SAndreas Gohr * @param string $filename file path label 648e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6490cecf9d5Sandi */ 650e2d88156SLarsDW223 function _highlight($type, $text, $language = null, $filename = null, $options = null) { 6513d491f75SAndreas Gohr global $ID; 6523d491f75SAndreas Gohr global $lang; 653ec57f119SLarsDW223 global $INPUT; 6543d491f75SAndreas Gohr 65556bd9509SPhy $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language); 65656bd9509SPhy 6573d491f75SAndreas Gohr if($filename) { 658190c56e8SAndreas Gohr // add icon 65927bf7924STom N Harris list($ext) = mimetype($filename, false); 660190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 661190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 662190c56e8SAndreas Gohr 663ec57f119SLarsDW223 $offset = 0; 664ec57f119SLarsDW223 if ($INPUT->has('codeblockOffset')) { 665ec57f119SLarsDW223 $offset = $INPUT->str('codeblockOffset'); 666ec57f119SLarsDW223 } 6673d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 668ec57f119SLarsDW223 $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $offset+$this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6693d491f75SAndreas Gohr $this->doc .= hsc($filename); 6703d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6713d491f75SAndreas Gohr } 6720cecf9d5Sandi 673d43aac1cSGina Haeussge if($text{0} == "\n") { 674d43aac1cSGina Haeussge $text = substr($text, 1); 675d43aac1cSGina Haeussge } 676d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 677d43aac1cSGina Haeussge $text = substr($text, 0, -1); 678d43aac1cSGina Haeussge } 679d43aac1cSGina Haeussge 680a056e285SPhy if(empty($language)) { // empty is faster than is_null and can prevent '' string 6813d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6820cecf9d5Sandi } else { 6833d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6843d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6853d491f75SAndreas Gohr 686e2d88156SLarsDW223 $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '', $options).'</pre>'.DOKU_LF; 6870cecf9d5Sandi } 6883d491f75SAndreas Gohr 6893d491f75SAndreas Gohr if($filename) { 6903d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 6913d491f75SAndreas Gohr } 6923d491f75SAndreas Gohr 6933d491f75SAndreas Gohr $this->_codeblock++; 6940cecf9d5Sandi } 6950cecf9d5Sandi 6963dd5c225SAndreas Gohr /** 6973dd5c225SAndreas Gohr * Format an acronym 6983dd5c225SAndreas Gohr * 6993dd5c225SAndreas Gohr * Uses $this->acronyms 7003dd5c225SAndreas Gohr * 7013dd5c225SAndreas Gohr * @param string $acronym 7023dd5c225SAndreas Gohr */ 7030cecf9d5Sandi function acronym($acronym) { 7040cecf9d5Sandi 7050cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 7060cecf9d5Sandi 707433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 7080cecf9d5Sandi 709940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 710940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 7110cecf9d5Sandi 7120cecf9d5Sandi } else { 713a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 7140cecf9d5Sandi } 7150cecf9d5Sandi } 7160cecf9d5Sandi 7173dd5c225SAndreas Gohr /** 7183dd5c225SAndreas Gohr * Format a smiley 7193dd5c225SAndreas Gohr * 7203dd5c225SAndreas Gohr * Uses $this->smiley 7213dd5c225SAndreas Gohr * 7223dd5c225SAndreas Gohr * @param string $smiley 7233dd5c225SAndreas Gohr */ 7240cecf9d5Sandi function smiley($smiley) { 7250cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 726f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 7278e38227fSAnika Henke '" class="icon" alt="'. 728433bef32Sandi $this->_xmlEntities($smiley).'" />'; 7290cecf9d5Sandi } else { 730a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 7310cecf9d5Sandi } 7320cecf9d5Sandi } 7330cecf9d5Sandi 7343dd5c225SAndreas Gohr /** 7353dd5c225SAndreas Gohr * Format an entity 7363dd5c225SAndreas Gohr * 7373dd5c225SAndreas Gohr * Entities are basically small text replacements 7383dd5c225SAndreas Gohr * 7393dd5c225SAndreas Gohr * Uses $this->entities 7403dd5c225SAndreas Gohr * 7413dd5c225SAndreas Gohr * @param string $entity 7424de671bcSandi */ 7430cecf9d5Sandi function entity($entity) { 7440cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 745a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7460cecf9d5Sandi } else { 747a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7480cecf9d5Sandi } 7490cecf9d5Sandi } 7500cecf9d5Sandi 7513dd5c225SAndreas Gohr /** 7523dd5c225SAndreas Gohr * Typographically format a multiply sign 7533dd5c225SAndreas Gohr * 7543dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7553dd5c225SAndreas Gohr * 7563dd5c225SAndreas Gohr * @param string|int $x first value 7573dd5c225SAndreas Gohr * @param string|int $y second value 7583dd5c225SAndreas Gohr */ 7590cecf9d5Sandi function multiplyentity($x, $y) { 760a2d649c4Sandi $this->doc .= "$x×$y"; 7610cecf9d5Sandi } 7620cecf9d5Sandi 7633dd5c225SAndreas Gohr /** 7643dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7653dd5c225SAndreas Gohr */ 7660cecf9d5Sandi function singlequoteopening() { 76771b40da2SAnika Henke global $lang; 76871b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7690cecf9d5Sandi } 7700cecf9d5Sandi 7713dd5c225SAndreas Gohr /** 7723dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7733dd5c225SAndreas Gohr */ 7740cecf9d5Sandi function singlequoteclosing() { 77571b40da2SAnika Henke global $lang; 77671b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7770cecf9d5Sandi } 7780cecf9d5Sandi 7793dd5c225SAndreas Gohr /** 7803dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7813dd5c225SAndreas Gohr */ 78257d757d1SAndreas Gohr function apostrophe() { 78357d757d1SAndreas Gohr global $lang; 784a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 78557d757d1SAndreas Gohr } 78657d757d1SAndreas Gohr 7873dd5c225SAndreas Gohr /** 7883dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 7893dd5c225SAndreas Gohr */ 7900cecf9d5Sandi function doublequoteopening() { 79171b40da2SAnika Henke global $lang; 79271b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 7930cecf9d5Sandi } 7940cecf9d5Sandi 7953dd5c225SAndreas Gohr /** 7963dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 7973dd5c225SAndreas Gohr */ 7980cecf9d5Sandi function doublequoteclosing() { 79971b40da2SAnika Henke global $lang; 80071b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 8010cecf9d5Sandi } 8020cecf9d5Sandi 8030cecf9d5Sandi /** 8043dd5c225SAndreas Gohr * Render a CamelCase link 8053dd5c225SAndreas Gohr * 8063dd5c225SAndreas Gohr * @param string $link The link name 807122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8080c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8090c4c0281SGerrit Uitslag * 8103dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 8110cecf9d5Sandi */ 812122f2d46SAndreas Böhler function camelcaselink($link, $returnonly = false) { 813122f2d46SAndreas Böhler if($returnonly) { 814122f2d46SAndreas Böhler return $this->internallink($link, $link, null, true); 815122f2d46SAndreas Böhler } else { 81611d0aa47Sandi $this->internallink($link, $link); 8170cecf9d5Sandi } 818122f2d46SAndreas Böhler } 8190cecf9d5Sandi 8203dd5c225SAndreas Gohr /** 8213dd5c225SAndreas Gohr * Render a page local link 8223dd5c225SAndreas Gohr * 8233dd5c225SAndreas Gohr * @param string $hash hash link identifier 8243dd5c225SAndreas Gohr * @param string $name name for the link 825122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8260c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8273dd5c225SAndreas Gohr */ 828122f2d46SAndreas Böhler function locallink($hash, $name = null, $returnonly = false) { 8290b7c14c2Sandi global $ID; 8300b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 8310b7c14c2Sandi $hash = $this->_headerToLink($hash); 832e260f93bSAnika Henke $title = $ID.' ↵'; 833122f2d46SAndreas Böhler 834122f2d46SAndreas Böhler $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 835122f2d46SAndreas Böhler $doc .= $name; 836122f2d46SAndreas Böhler $doc .= '</a>'; 837122f2d46SAndreas Böhler 838122f2d46SAndreas Böhler if($returnonly) { 839122f2d46SAndreas Böhler return $doc; 840122f2d46SAndreas Böhler } else { 841122f2d46SAndreas Böhler $this->doc .= $doc; 842122f2d46SAndreas Böhler } 8430b7c14c2Sandi } 8440b7c14c2Sandi 845cffcc403Sandi /** 8463fd0b676Sandi * Render an internal Wiki Link 8473fd0b676Sandi * 848fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 849cffcc403Sandi * elsewhere - no need to implement them in other renderers 8503fd0b676Sandi * 8513dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 852f23eef27SGerrit Uitslag * @param string $id pageid 853f23eef27SGerrit Uitslag * @param string|null $name link name 854f23eef27SGerrit Uitslag * @param string|null $search adds search url param 855f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 856f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 857f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 858cffcc403Sandi */ 8590ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 860ba11bd29Sandi global $conf; 86137e34a5eSandi global $ID; 862c4dda6afSAnika Henke global $INFO; 86344653a53SAdrian Lang 8643d5e07d9SAdrian Lang $params = ''; 8653d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8663d5e07d9SAdrian Lang if(count($parts) === 2) { 8673d5e07d9SAdrian Lang $id = $parts[0]; 8683d5e07d9SAdrian Lang $params = $parts[1]; 86944653a53SAdrian Lang } 87044653a53SAdrian Lang 871fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 872fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 873fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 874fda14ffcSIzidor Matušov // (some things could be lost) 875fda14ffcSIzidor Matušov if($id === '') { 876fda14ffcSIzidor Matušov $id = $ID; 877fda14ffcSIzidor Matušov } 878fda14ffcSIzidor Matušov 8790339c872Sjan // default name is based on $id as given 8800339c872Sjan $default = $this->_simpleTitle($id); 881ad32e47eSAndreas Gohr 8820339c872Sjan // now first resolve and clean up the $id 88390bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 884fda14ffcSIzidor Matušov 88559bc3b48SGerrit Uitslag $link = array(); 886fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8870e1c636eSandi if(!$isImage) { 8880e1c636eSandi if($exists) { 889ba11bd29Sandi $class = 'wikilink1'; 8900cecf9d5Sandi } else { 891ba11bd29Sandi $class = 'wikilink2'; 89244a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 8930cecf9d5Sandi } 8940cecf9d5Sandi } else { 895ba11bd29Sandi $class = 'media'; 8960cecf9d5Sandi } 8970cecf9d5Sandi 898a1685bedSandi //keep hash anchor 8996d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 900943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 901a1685bedSandi 902ba11bd29Sandi //prepare for formating 903ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 904ba11bd29Sandi $link['style'] = ''; 905ba11bd29Sandi $link['pre'] = ''; 906ba11bd29Sandi $link['suf'] = ''; 90740eb54bbSjan // highlight link to current page 908c4dda6afSAnika Henke if($id == $INFO['id']) { 90992795d04Sandi $link['pre'] = '<span class="curid">'; 91092795d04Sandi $link['suf'] = '</span>'; 91140eb54bbSjan } 9125e163278SAndreas Gohr $link['more'] = ''; 913ba11bd29Sandi $link['class'] = $class; 9145c2eed9aSlisps if($this->date_at) { 915912a6d48SPhy $params = $params.'&at='.rawurlencode($this->date_at); 9165c2eed9aSlisps } 91744653a53SAdrian Lang $link['url'] = wl($id, $params); 918ba11bd29Sandi $link['name'] = $name; 919ba11bd29Sandi $link['title'] = $id; 920723d78dbSandi //add search string 921723d78dbSandi if($search) { 922546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 923546d3a99SAndreas Gohr if(is_array($search)) { 924546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 925546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 926546d3a99SAndreas Gohr } else { 927546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 928546d3a99SAndreas Gohr } 929723d78dbSandi } 930723d78dbSandi 931a1685bedSandi //keep hash 932a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 933a1685bedSandi 934ba11bd29Sandi //output formatted 935cffcc403Sandi if($returnonly) { 936cffcc403Sandi return $this->_formatLink($link); 937cffcc403Sandi } else { 938a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9390cecf9d5Sandi } 940cffcc403Sandi } 9410cecf9d5Sandi 9423dd5c225SAndreas Gohr /** 9433dd5c225SAndreas Gohr * Render an external link 9443dd5c225SAndreas Gohr * 9453dd5c225SAndreas Gohr * @param string $url full URL with scheme 9463dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 947122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 9480c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9493dd5c225SAndreas Gohr */ 950122f2d46SAndreas Böhler function externallink($url, $name = null, $returnonly = false) { 951b625487dSandi global $conf; 9520cecf9d5Sandi 953433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 9546f0c5dbfSandi 955b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 956b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 957b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 958b52b1596SAndreas Gohr $scheme = strtolower($scheme); 959b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 960b52b1596SAndreas Gohr 961b52b1596SAndreas Gohr // is there still an URL? 962b52b1596SAndreas Gohr if(!$url) { 96349cef4fdSAndreas Böhler if($returnonly) { 96449cef4fdSAndreas Böhler return $name; 96549cef4fdSAndreas Böhler } else { 966b52b1596SAndreas Gohr $this->doc .= $name; 96749cef4fdSAndreas Böhler } 968b52b1596SAndreas Gohr return; 969b52b1596SAndreas Gohr } 970b52b1596SAndreas Gohr 971b52b1596SAndreas Gohr // set class 9720cecf9d5Sandi if(!$isImage) { 973b625487dSandi $class = 'urlextern'; 9740cecf9d5Sandi } else { 975b625487dSandi $class = 'media'; 9760cecf9d5Sandi } 9770cecf9d5Sandi 978b625487dSandi //prepare for formating 97959bc3b48SGerrit Uitslag $link = array(); 980b625487dSandi $link['target'] = $conf['target']['extern']; 981b625487dSandi $link['style'] = ''; 982b625487dSandi $link['pre'] = ''; 983b625487dSandi $link['suf'] = ''; 9845e163278SAndreas Gohr $link['more'] = ''; 985b625487dSandi $link['class'] = $class; 986b625487dSandi $link['url'] = $url; 987914045f3SAndreas Gohr $link['rel'] = ''; 988e1c10e4dSchris 989b625487dSandi $link['name'] = $name; 990433bef32Sandi $link['title'] = $this->_xmlEntities($url); 991914045f3SAndreas Gohr if($conf['relnofollow']) $link['rel'] .= ' nofollow'; 992914045f3SAndreas Gohr if($conf['target']['extern']) $link['rel'] .= ' noopener'; 9930cecf9d5Sandi 994b625487dSandi //output formatted 995122f2d46SAndreas Böhler if($returnonly) { 996122f2d46SAndreas Böhler return $this->_formatLink($link); 997122f2d46SAndreas Böhler } else { 998a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9990cecf9d5Sandi } 1000122f2d46SAndreas Böhler } 10010cecf9d5Sandi 10020cecf9d5Sandi /** 10033dd5c225SAndreas Gohr * Render an interwiki link 10043dd5c225SAndreas Gohr * 10053dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 10063dd5c225SAndreas Gohr * 10073dd5c225SAndreas Gohr * @param string $match original link - probably not much use 10083dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 10093dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 10103dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 1011122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10120c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10130cecf9d5Sandi */ 1014122f2d46SAndreas Böhler function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) { 1015b625487dSandi global $conf; 10160cecf9d5Sandi 101797a3e4e3Sandi $link = array(); 101897a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 101997a3e4e3Sandi $link['pre'] = ''; 102097a3e4e3Sandi $link['suf'] = ''; 10215e163278SAndreas Gohr $link['more'] = ''; 1022433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 1023914045f3SAndreas Gohr $link['rel'] = ''; 10240cecf9d5Sandi 102597a3e4e3Sandi //get interwiki URL 10266496c33fSGerrit Uitslag $exists = null; 10276496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 10280cecf9d5Sandi 102997a3e4e3Sandi if(!$isImage) { 10309d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 10319d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 10321c2d1019SAndreas Gohr } else { 10331c2d1019SAndreas Gohr $link['class'] = 'media'; 103497a3e4e3Sandi } 10350cecf9d5Sandi 103697a3e4e3Sandi //do we stay at the same server? Use local target 10372345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 103897a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 103997a3e4e3Sandi } 10406496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 10416496c33fSGerrit Uitslag if($exists) { 10426496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 10436496c33fSGerrit Uitslag } else { 10446496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 1045914045f3SAndreas Gohr $link['rel'] .= ' nofollow'; 10466496c33fSGerrit Uitslag } 10476496c33fSGerrit Uitslag } 1048914045f3SAndreas Gohr if($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 10490cecf9d5Sandi 105097a3e4e3Sandi $link['url'] = $url; 105197a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 105297a3e4e3Sandi 105397a3e4e3Sandi //output formatted 1054122f2d46SAndreas Böhler if($returnonly) { 1055122f2d46SAndreas Böhler return $this->_formatLink($link); 1056122f2d46SAndreas Böhler } else { 1057a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10580cecf9d5Sandi } 1059122f2d46SAndreas Böhler } 10600cecf9d5Sandi 10610cecf9d5Sandi /** 10623dd5c225SAndreas Gohr * Link to windows share 10633dd5c225SAndreas Gohr * 10643dd5c225SAndreas Gohr * @param string $url the link 10653dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1066122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10670c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10680cecf9d5Sandi */ 1069122f2d46SAndreas Böhler function windowssharelink($url, $name = null, $returnonly = false) { 10701d47afe1Sandi global $conf; 10713dd5c225SAndreas Gohr 10721d47afe1Sandi //simple setup 107359bc3b48SGerrit Uitslag $link = array(); 10741d47afe1Sandi $link['target'] = $conf['target']['windows']; 10751d47afe1Sandi $link['pre'] = ''; 10761d47afe1Sandi $link['suf'] = ''; 10771d47afe1Sandi $link['style'] = ''; 10780cecf9d5Sandi 1079433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 10800cecf9d5Sandi if(!$isImage) { 10811d47afe1Sandi $link['class'] = 'windows'; 10820cecf9d5Sandi } else { 10831d47afe1Sandi $link['class'] = 'media'; 10840cecf9d5Sandi } 10850cecf9d5Sandi 1086433bef32Sandi $link['title'] = $this->_xmlEntities($url); 10871d47afe1Sandi $url = str_replace('\\', '/', $url); 10881d47afe1Sandi $url = 'file:///'.$url; 10891d47afe1Sandi $link['url'] = $url; 10900cecf9d5Sandi 10911d47afe1Sandi //output formatted 1092122f2d46SAndreas Böhler if($returnonly) { 1093122f2d46SAndreas Böhler return $this->_formatLink($link); 1094122f2d46SAndreas Böhler } else { 1095a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10960cecf9d5Sandi } 1097122f2d46SAndreas Böhler } 10980cecf9d5Sandi 10993dd5c225SAndreas Gohr /** 11003dd5c225SAndreas Gohr * Render a linked E-Mail Address 11013dd5c225SAndreas Gohr * 11023dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 11033dd5c225SAndreas Gohr * 11043dd5c225SAndreas Gohr * @param string $address Email-Address 11053dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1106122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 11070c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 11083dd5c225SAndreas Gohr */ 1109122f2d46SAndreas Böhler function emaillink($address, $name = null, $returnonly = false) { 111071352defSandi global $conf; 111171352defSandi //simple setup 111271352defSandi $link = array(); 111371352defSandi $link['target'] = ''; 111471352defSandi $link['pre'] = ''; 111571352defSandi $link['suf'] = ''; 111671352defSandi $link['style'] = ''; 111771352defSandi $link['more'] = ''; 11180cecf9d5Sandi 1119c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 11200cecf9d5Sandi if(!$isImage) { 1121be96545cSAnika Henke $link['class'] = 'mail'; 11220cecf9d5Sandi } else { 1123be96545cSAnika Henke $link['class'] = 'media'; 11240cecf9d5Sandi } 11250cecf9d5Sandi 112607738714SAndreas Gohr $address = $this->_xmlEntities($address); 112700a7b5adSEsther Brunner $address = obfuscate($address); 112800a7b5adSEsther Brunner $title = $address; 11298c128049SAndreas Gohr 113071352defSandi if(empty($name)) { 113100a7b5adSEsther Brunner $name = $address; 113271352defSandi } 11330cecf9d5Sandi 1134776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1135776b36ecSAndreas Gohr 1136776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 113771352defSandi $link['name'] = $name; 113871352defSandi $link['title'] = $title; 11390cecf9d5Sandi 114071352defSandi //output formatted 1141122f2d46SAndreas Böhler if($returnonly) { 1142122f2d46SAndreas Böhler return $this->_formatLink($link); 1143122f2d46SAndreas Böhler } else { 1144a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11450cecf9d5Sandi } 1146122f2d46SAndreas Böhler } 11470cecf9d5Sandi 11483dd5c225SAndreas Gohr /** 11493dd5c225SAndreas Gohr * Render an internal media file 11503dd5c225SAndreas Gohr * 11513dd5c225SAndreas Gohr * @param string $src media ID 11523dd5c225SAndreas Gohr * @param string $title descriptive text 11533dd5c225SAndreas Gohr * @param string $align left|center|right 11543dd5c225SAndreas Gohr * @param int $width width of media in pixel 11553dd5c225SAndreas Gohr * @param int $height height of media in pixel 11563dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11573dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 11583dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 11590c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11603dd5c225SAndreas Gohr */ 11610ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 11623dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 116337e34a5eSandi global $ID; 11648f34cf3dSMichael Große if (strpos($src, '#') !== false) { 116591df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 11668f34cf3dSMichael Große } 1167cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 11680cecf9d5Sandi 1169d98d4540SBen Coburn $noLink = false; 11708acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1171b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 11723685f775Sandi 11733dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1174b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 117552dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct')); 1176f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11772a2a2ba2SAnika Henke // don't link movies 117844881bd0Shenning.noren $noLink = true; 117955efc227SAndreas Gohr } else { 11802ca14335SEsther Brunner // add file icons 11819d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 11829d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 118352dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true); 118491328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 118555efc227SAndreas Gohr } 11863685f775Sandi 11878f34cf3dSMichael Große if (!empty($hash)) $link['url'] .= '#'.$hash; 118891df343aSAndreas Gohr 11896fe20453SGina Haeussge //markup non existing files 11904a24b459SKate Arzamastseva if(!$exists) { 11916fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 11924a24b459SKate Arzamastseva } 11936fe20453SGina Haeussge 11943685f775Sandi //output formatted 1195f50634f0SAnika Henke if($return) { 1196f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1197f50634f0SAnika Henke else return $this->_formatLink($link); 1198f50634f0SAnika Henke } else { 1199dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12002ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12010cecf9d5Sandi } 1202f50634f0SAnika Henke } 12030cecf9d5Sandi 12043dd5c225SAndreas Gohr /** 12053dd5c225SAndreas Gohr * Render an external media file 12063dd5c225SAndreas Gohr * 12073dd5c225SAndreas Gohr * @param string $src full media URL 12083dd5c225SAndreas Gohr * @param string $title descriptive text 12093dd5c225SAndreas Gohr * @param string $align left|center|right 12103dd5c225SAndreas Gohr * @param int $width width of media in pixel 12113dd5c225SAndreas Gohr * @param int $height height of media in pixel 12123dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 12133dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1214410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 12150c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 12163dd5c225SAndreas Gohr */ 12170ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 1218410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 12196efc45a2SDmitry Katsubo if(link_isinterwiki($src)){ 12206efc45a2SDmitry Katsubo list($shortcut, $reference) = explode('>', $src, 2); 12216efc45a2SDmitry Katsubo $exists = null; 12226efc45a2SDmitry Katsubo $src = $this->_resolveInterWiki($shortcut, $reference, $exists); 12236efc45a2SDmitry Katsubo } 122491df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1225d98d4540SBen Coburn $noLink = false; 12268acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1227b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1228b739ff0fSPierre Spring 1229b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 12303685f775Sandi 12313dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1232b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 12332ca14335SEsther Brunner // link only jpeg images 123444881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1235f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 12362a2a2ba2SAnika Henke // don't link movies 123744881bd0Shenning.noren $noLink = true; 12382ca14335SEsther Brunner } else { 12392ca14335SEsther Brunner // add file icons 124027bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 124127bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 12422ca14335SEsther Brunner } 12432ca14335SEsther Brunner 124491df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 124591df343aSAndreas Gohr 12463685f775Sandi //output formatted 1247410ee62aSAnika Henke if($return) { 1248410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1249410ee62aSAnika Henke else return $this->_formatLink($link); 1250410ee62aSAnika Henke } else { 1251dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12522ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12530cecf9d5Sandi } 1254410ee62aSAnika Henke } 12550cecf9d5Sandi 12564826ab45Sandi /** 12573db95becSAndreas Gohr * Renders an RSS feed 1258b625487dSandi * 12590c4c0281SGerrit Uitslag * @param string $url URL of the feed 12600c4c0281SGerrit Uitslag * @param array $params Finetuning of the output 12610c4c0281SGerrit Uitslag * 1262b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1263b625487dSandi */ 12643db95becSAndreas Gohr function rss($url, $params) { 1265b625487dSandi global $lang; 12663db95becSAndreas Gohr global $conf; 12673db95becSAndreas Gohr 12683db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 12693db95becSAndreas Gohr $feed = new FeedParser(); 127000077af8SAndreas Gohr $feed->set_feed_url($url); 1271b625487dSandi 1272b625487dSandi //disable warning while fetching 12733dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 12743dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 12753dd5c225SAndreas Gohr } 12763db95becSAndreas Gohr $rc = $feed->init(); 12773dd5c225SAndreas Gohr if(isset($elvl)) { 12783dd5c225SAndreas Gohr error_reporting($elvl); 12793dd5c225SAndreas Gohr } 1280b625487dSandi 128138c6f603SRobin H. Johnson if($params['nosort']) $feed->enable_order_by_date(false); 128238c6f603SRobin H. Johnson 12833db95becSAndreas Gohr //decide on start and end 12843db95becSAndreas Gohr if($params['reverse']) { 12853db95becSAndreas Gohr $mod = -1; 12863db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 12873db95becSAndreas Gohr $end = $start - ($params['max']); 1288b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 12893db95becSAndreas Gohr } else { 12903db95becSAndreas Gohr $mod = 1; 12913db95becSAndreas Gohr $start = 0; 12923db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1293d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 12943db95becSAndreas Gohr } 12953db95becSAndreas Gohr 1296a2d649c4Sandi $this->doc .= '<ul class="rss">'; 12973db95becSAndreas Gohr if($rc) { 12983db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 12991bde1582SAndreas Gohr $item = $feed->get_item($x); 13003db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1301d2ea3363SAndreas Gohr // support feeds without links 1302d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1303d2ea3363SAndreas Gohr if($lnkurl) { 1304793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1305793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 13063dd5c225SAndreas Gohr $this->externallink( 13073dd5c225SAndreas Gohr $item->get_permalink(), 13083dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 13093dd5c225SAndreas Gohr ); 1310d2ea3363SAndreas Gohr } else { 1311d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1312d2ea3363SAndreas Gohr } 13133db95becSAndreas Gohr if($params['author']) { 13141bde1582SAndreas Gohr $author = $item->get_author(0); 13151bde1582SAndreas Gohr if($author) { 13161bde1582SAndreas Gohr $name = $author->get_name(); 13171bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 1318163c2842SPhy if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name); 13191bde1582SAndreas Gohr } 13203db95becSAndreas Gohr } 13213db95becSAndreas Gohr if($params['date']) { 13222e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 13233db95becSAndreas Gohr } 13241bde1582SAndreas Gohr if($params['details']) { 13253db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1326173dccb7STom N Harris if($conf['htmlok']) { 13271bde1582SAndreas Gohr $this->doc .= $item->get_description(); 13283db95becSAndreas Gohr } else { 13291bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 13303db95becSAndreas Gohr } 13313db95becSAndreas Gohr $this->doc .= '</div>'; 13323db95becSAndreas Gohr } 13333db95becSAndreas Gohr 13343db95becSAndreas Gohr $this->doc .= '</div></li>'; 1335b625487dSandi } 1336b625487dSandi } else { 13373db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1338a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1339b625487dSandi $this->externallink($url); 134045e147ccSAndreas Gohr if($conf['allowdebug']) { 134145e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 134245e147ccSAndreas Gohr } 13433db95becSAndreas Gohr $this->doc .= '</div></li>'; 1344b625487dSandi } 1345a2d649c4Sandi $this->doc .= '</ul>'; 1346b625487dSandi } 1347b625487dSandi 13483dd5c225SAndreas Gohr /** 13493dd5c225SAndreas Gohr * Start a table 13503dd5c225SAndreas Gohr * 13513dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 13523dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 13533dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13547d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 13553dd5c225SAndreas Gohr */ 13560c4c0281SGerrit Uitslag function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 1357b5742cedSPierre Spring // initialize the row counter used for classes 1358b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1359619736fdSAdrian Lang $class = 'table'; 13600c4c0281SGerrit Uitslag if($classes !== null) { 13612e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 13620c4c0281SGerrit Uitslag $class .= ' ' . $classes; 13630c4c0281SGerrit Uitslag } 1364619736fdSAdrian Lang if($pos !== null) { 136506917fceSMichael Große $hid = $this->_headerToLink($class, true); 1366ec57f119SLarsDW223 $data = array(); 1367ec57f119SLarsDW223 $data['target'] = 'table'; 1368ec57f119SLarsDW223 $data['name'] = ''; 1369ec57f119SLarsDW223 $data['hid'] = $hid; 1370ec57f119SLarsDW223 $class .= ' '.$this->startSectionEdit($pos, $data); 1371619736fdSAdrian Lang } 1372619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1373619736fdSAdrian Lang DOKU_LF; 13740cecf9d5Sandi } 13750cecf9d5Sandi 13763dd5c225SAndreas Gohr /** 13773dd5c225SAndreas Gohr * Close a table 13783dd5c225SAndreas Gohr * 13793dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13803dd5c225SAndreas Gohr */ 1381619736fdSAdrian Lang function table_close($pos = null) { 1382a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1383619736fdSAdrian Lang if($pos !== null) { 138490df9a4dSAdrian Lang $this->finishSectionEdit($pos); 13850cecf9d5Sandi } 1386619736fdSAdrian Lang } 13870cecf9d5Sandi 13883dd5c225SAndreas Gohr /** 13893dd5c225SAndreas Gohr * Open a table header 13903dd5c225SAndreas Gohr */ 1391f05a1cc5SGerrit Uitslag function tablethead_open() { 1392f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1393f05a1cc5SGerrit Uitslag } 1394f05a1cc5SGerrit Uitslag 13953dd5c225SAndreas Gohr /** 13963dd5c225SAndreas Gohr * Close a table header 13973dd5c225SAndreas Gohr */ 1398f05a1cc5SGerrit Uitslag function tablethead_close() { 1399f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1400f05a1cc5SGerrit Uitslag } 1401f05a1cc5SGerrit Uitslag 14023dd5c225SAndreas Gohr /** 14035a93f869SAnika Henke * Open a table body 14045a93f869SAnika Henke */ 14055a93f869SAnika Henke function tabletbody_open() { 14065a93f869SAnika Henke $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF; 14075a93f869SAnika Henke } 14085a93f869SAnika Henke 14095a93f869SAnika Henke /** 14105a93f869SAnika Henke * Close a table body 14115a93f869SAnika Henke */ 14125a93f869SAnika Henke function tabletbody_close() { 14135a93f869SAnika Henke $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF; 14145a93f869SAnika Henke } 14155a93f869SAnika Henke 14165a93f869SAnika Henke /** 1417d2a99739SAndreas Gohr * Open a table footer 1418d2a99739SAndreas Gohr */ 1419d2a99739SAndreas Gohr function tabletfoot_open() { 142044f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF; 1421d2a99739SAndreas Gohr } 1422d2a99739SAndreas Gohr 1423d2a99739SAndreas Gohr /** 1424d2a99739SAndreas Gohr * Close a table footer 1425d2a99739SAndreas Gohr */ 1426d2a99739SAndreas Gohr function tabletfoot_close() { 142744f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF; 1428d2a99739SAndreas Gohr } 1429d2a99739SAndreas Gohr 1430d2a99739SAndreas Gohr /** 14313dd5c225SAndreas Gohr * Open a table row 14320c4c0281SGerrit Uitslag * 14337d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14343dd5c225SAndreas Gohr */ 14350c4c0281SGerrit Uitslag function tablerow_open($classes = null) { 1436b5742cedSPierre Spring // initialize the cell counter used for classes 1437b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1438b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 14390c4c0281SGerrit Uitslag if($classes !== null) { 14402e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14410c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14420c4c0281SGerrit Uitslag } 1443b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 14440cecf9d5Sandi } 14450cecf9d5Sandi 14463dd5c225SAndreas Gohr /** 14473dd5c225SAndreas Gohr * Close a table row 14483dd5c225SAndreas Gohr */ 14490cecf9d5Sandi function tablerow_close() { 1450a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 14510cecf9d5Sandi } 14520cecf9d5Sandi 14533dd5c225SAndreas Gohr /** 14543dd5c225SAndreas Gohr * Open a table header cell 14553dd5c225SAndreas Gohr * 14563dd5c225SAndreas Gohr * @param int $colspan 14573dd5c225SAndreas Gohr * @param string $align left|center|right 14583dd5c225SAndreas Gohr * @param int $rowspan 14597d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14603dd5c225SAndreas Gohr */ 14610c4c0281SGerrit Uitslag function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1462b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14630cecf9d5Sandi if(!is_null($align)) { 1464b5742cedSPierre Spring $class .= ' '.$align.'align'; 14650cecf9d5Sandi } 14660c4c0281SGerrit Uitslag if($classes !== null) { 14672e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14680c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14690c4c0281SGerrit Uitslag } 1470b5742cedSPierre Spring $class .= '"'; 1471b5742cedSPierre Spring $this->doc .= '<th '.$class; 14720cecf9d5Sandi if($colspan > 1) { 1473a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1474a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14750cecf9d5Sandi } 147625b97867Shakan.sandell if($rowspan > 1) { 147725b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 147825b97867Shakan.sandell } 1479a2d649c4Sandi $this->doc .= '>'; 14800cecf9d5Sandi } 14810cecf9d5Sandi 14823dd5c225SAndreas Gohr /** 14833dd5c225SAndreas Gohr * Close a table header cell 14843dd5c225SAndreas Gohr */ 14850cecf9d5Sandi function tableheader_close() { 1486a2d649c4Sandi $this->doc .= '</th>'; 14870cecf9d5Sandi } 14880cecf9d5Sandi 14893dd5c225SAndreas Gohr /** 14903dd5c225SAndreas Gohr * Open a table cell 14913dd5c225SAndreas Gohr * 14923dd5c225SAndreas Gohr * @param int $colspan 14933dd5c225SAndreas Gohr * @param string $align left|center|right 14943dd5c225SAndreas Gohr * @param int $rowspan 14957d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14963dd5c225SAndreas Gohr */ 14970c4c0281SGerrit Uitslag function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1498b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14990cecf9d5Sandi if(!is_null($align)) { 1500b5742cedSPierre Spring $class .= ' '.$align.'align'; 15010cecf9d5Sandi } 15020c4c0281SGerrit Uitslag if($classes !== null) { 15032e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 15040c4c0281SGerrit Uitslag $class .= ' ' . $classes; 15050c4c0281SGerrit Uitslag } 1506b5742cedSPierre Spring $class .= '"'; 1507b5742cedSPierre Spring $this->doc .= '<td '.$class; 15080cecf9d5Sandi if($colspan > 1) { 1509a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1510a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 15110cecf9d5Sandi } 151225b97867Shakan.sandell if($rowspan > 1) { 151325b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 151425b97867Shakan.sandell } 1515a2d649c4Sandi $this->doc .= '>'; 15160cecf9d5Sandi } 15170cecf9d5Sandi 15183dd5c225SAndreas Gohr /** 15193dd5c225SAndreas Gohr * Close a table cell 15203dd5c225SAndreas Gohr */ 15210cecf9d5Sandi function tablecell_close() { 1522a2d649c4Sandi $this->doc .= '</td>'; 15230cecf9d5Sandi } 15240cecf9d5Sandi 1525cea664bdSLarsDW223 /** 1526cea664bdSLarsDW223 * Returns the current header level. 1527cea664bdSLarsDW223 * (required e.g. by the filelist plugin) 1528cea664bdSLarsDW223 * 1529cea664bdSLarsDW223 * @return int The current header level 1530cea664bdSLarsDW223 */ 1531cea664bdSLarsDW223 function getLastlevel() { 1532cea664bdSLarsDW223 return $this->lastlevel; 1533cea664bdSLarsDW223 } 1534cea664bdSLarsDW223 15353dd5c225SAndreas Gohr #region Utility functions 15360cecf9d5Sandi 1537ba11bd29Sandi /** 15383fd0b676Sandi * Build a link 15393fd0b676Sandi * 15403fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1541ba11bd29Sandi * 15420c4c0281SGerrit Uitslag * @param array $link attributes of a link 15430c4c0281SGerrit Uitslag * @return string 15440c4c0281SGerrit Uitslag * 1545ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1546ba11bd29Sandi */ 1547433bef32Sandi function _formatLink($link) { 1548ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1549ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1550ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1551ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1552ba11bd29Sandi } 1553ba11bd29Sandi //remove double encodings in titles 1554ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1555ba11bd29Sandi 1556453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1557453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1558453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1559453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1560453493f2SAndreas Gohr 1561ba11bd29Sandi $ret = ''; 1562ba11bd29Sandi $ret .= $link['pre']; 1563ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1564bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1565bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1566bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1567bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 1568914045f3SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"'; 1569bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1570ba11bd29Sandi $ret .= '>'; 1571ba11bd29Sandi $ret .= $link['name']; 1572ba11bd29Sandi $ret .= '</a>'; 1573ba11bd29Sandi $ret .= $link['suf']; 1574ba11bd29Sandi return $ret; 1575ba11bd29Sandi } 1576ba11bd29Sandi 1577ba11bd29Sandi /** 15783fd0b676Sandi * Renders internal and external media 15793fd0b676Sandi * 15803fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 15813dd5c225SAndreas Gohr * @param string $src media ID 15823dd5c225SAndreas Gohr * @param string $title descriptive text 15833dd5c225SAndreas Gohr * @param string $align left|center|right 15843dd5c225SAndreas Gohr * @param int $width width of media in pixel 15853dd5c225SAndreas Gohr * @param int $height height of media in pixel 15863dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 15873dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 15883dd5c225SAndreas Gohr * @return string 15893fd0b676Sandi */ 15900ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 15910ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 15923fd0b676Sandi 15933fd0b676Sandi $ret = ''; 15943fd0b676Sandi 15953dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 15963fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1597b739ff0fSPierre Spring // first get the $title 1598b739ff0fSPierre Spring if(!is_null($title)) { 1599b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1600b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1601b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1602b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 160367f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1604b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 16053dd5c225SAndreas Gohr if(!empty($cap)) { 1606b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1607b739ff0fSPierre Spring } 1608b739ff0fSPierre Spring } 1609b739ff0fSPierre Spring if(!$render) { 1610b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1611b739ff0fSPierre Spring // return the title of the picture 1612b739ff0fSPierre Spring if(!$title) { 1613b739ff0fSPierre Spring // just show the sourcename 16143009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1615b739ff0fSPierre Spring } 1616b739ff0fSPierre Spring return $title; 1617b739ff0fSPierre Spring } 16183fd0b676Sandi //add image tag 161952dc5eadSlisps $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"'; 16203fd0b676Sandi $ret .= ' class="media'.$align.'"'; 16213fd0b676Sandi 1622b739ff0fSPierre Spring if($title) { 1623b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1624b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 16253fd0b676Sandi } else { 16263fd0b676Sandi $ret .= ' alt=""'; 16273fd0b676Sandi } 16283fd0b676Sandi 16293fd0b676Sandi if(!is_null($width)) 16303fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 16313fd0b676Sandi 16323fd0b676Sandi if(!is_null($height)) 16333fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 16343fd0b676Sandi 16353fd0b676Sandi $ret .= ' />'; 16363fd0b676Sandi 163717954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 16382a2a2ba2SAnika Henke // first get the $title 163917954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 16402a2a2ba2SAnika Henke if(!$render) { 164117954bb5SAnika Henke // if the file is not supposed to be rendered 164217954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 164317954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 16442a2a2ba2SAnika Henke } 16452a2a2ba2SAnika Henke 16462a2a2ba2SAnika Henke $att = array(); 16472a2a2ba2SAnika Henke $att['class'] = "media$align"; 164817954bb5SAnika Henke if($title) { 164917954bb5SAnika Henke $att['title'] = $title; 165017954bb5SAnika Henke } 16512a2a2ba2SAnika Henke 165217954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 165317954bb5SAnika Henke //add video 165479e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1655b44a5dceSAnika Henke } 165617954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1657b44a5dceSAnika Henke //add audio 1658b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 165917954bb5SAnika Henke } 1660b44a5dceSAnika Henke 16613fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 16621c882ba8SAndreas Gohr if(!$render) { 16631c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 16641c882ba8SAndreas Gohr // return the title of the flash 16651c882ba8SAndreas Gohr if(!$title) { 16661c882ba8SAndreas Gohr // just show the sourcename 16673009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 16681c882ba8SAndreas Gohr } 166907bf32b2SAndreas Gohr return $this->_xmlEntities($title); 16701c882ba8SAndreas Gohr } 16711c882ba8SAndreas Gohr 167207bf32b2SAndreas Gohr $att = array(); 167307bf32b2SAndreas Gohr $att['class'] = "media$align"; 167407bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 167507bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 16763dd5c225SAndreas Gohr $ret .= html_flashobject( 16773dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 167807bf32b2SAndreas Gohr array('quality' => 'high'), 167907bf32b2SAndreas Gohr null, 168007bf32b2SAndreas Gohr $att, 16813dd5c225SAndreas Gohr $this->_xmlEntities($title) 16823dd5c225SAndreas Gohr ); 16830f428d7dSAndreas Gohr } elseif($title) { 16843fd0b676Sandi // well at least we have a title to display 16853fd0b676Sandi $ret .= $this->_xmlEntities($title); 16863fd0b676Sandi } else { 16875291ca3aSAndreas Gohr // just show the sourcename 16883009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 16893fd0b676Sandi } 16903fd0b676Sandi 16913fd0b676Sandi return $ret; 16923fd0b676Sandi } 16933fd0b676Sandi 16943dd5c225SAndreas Gohr /** 16953dd5c225SAndreas Gohr * Escape string for output 16963dd5c225SAndreas Gohr * 16973dd5c225SAndreas Gohr * @param $string 16983dd5c225SAndreas Gohr * @return string 16993dd5c225SAndreas Gohr */ 1700433bef32Sandi function _xmlEntities($string) { 1701de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 17020cecf9d5Sandi } 17030cecf9d5Sandi 17048a831f2bSAndreas Gohr /** 17058a831f2bSAndreas Gohr * Creates a linkid from a headline 1706c5a8fd96SAndreas Gohr * 17073dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1708c5a8fd96SAndreas Gohr * @param string $title The headline title 1709c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 17103dd5c225SAndreas Gohr * @return string 17118a831f2bSAndreas Gohr */ 1712c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1713c5a8fd96SAndreas Gohr if($create) { 17144ceab83fSAndreas Gohr return sectionID($title, $this->headers); 17154ceab83fSAndreas Gohr } else { 1716443d207bSAndreas Gohr $check = false; 1717443d207bSAndreas Gohr return sectionID($title, $check); 1718c5a8fd96SAndreas Gohr } 17190cecf9d5Sandi } 17200cecf9d5Sandi 1721af587fa8Sandi /** 17223fd0b676Sandi * Construct a title and handle images in titles 17233fd0b676Sandi * 17240b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 17253dd5c225SAndreas Gohr * @param string|array $title either string title or media array 17263dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 17273dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 17283dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 17293dd5c225SAndreas Gohr * @param string $linktype content|navigation 17303dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 17313fd0b676Sandi */ 17320ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 173344881bd0Shenning.noren $isImage = false; 173429657f9eSAndreas Gohr if(is_array($title)) { 173529657f9eSAndreas Gohr $isImage = true; 173629657f9eSAndreas Gohr return $this->_imageTitle($title); 173729657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1738fe9ec250SChris Smith if(useHeading($linktype) && $id) { 173967c15eceSMichael Hamann $heading = p_get_first_heading($id); 1740f515db7fSAndreas Gohr if(!blank($heading)) { 1741433bef32Sandi return $this->_xmlEntities($heading); 1742bb0a59d4Sjan } 1743bb0a59d4Sjan } 1744433bef32Sandi return $this->_xmlEntities($default); 174568c26e6dSMichael Klier } else { 174668c26e6dSMichael Klier return $this->_xmlEntities($title); 17470cecf9d5Sandi } 17480cecf9d5Sandi } 17490cecf9d5Sandi 17500cecf9d5Sandi /** 17513dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 17523fd0b676Sandi * 17533fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1754e0c26282SGerrit Uitslag * @param array $img 17553dd5c225SAndreas Gohr * @return string HTML img tag or similar 17560cecf9d5Sandi */ 1757433bef32Sandi function _imageTitle($img) { 1758d9baf1a7SKazutaka Miyasaka global $ID; 1759d9baf1a7SKazutaka Miyasaka 1760d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1761d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 17623dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1763d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1764cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1765d9baf1a7SKazutaka Miyasaka } 1766d9baf1a7SKazutaka Miyasaka 17673dd5c225SAndreas Gohr return $this->_media( 17683dd5c225SAndreas Gohr $img['src'], 17694826ab45Sandi $img['title'], 17704826ab45Sandi $img['align'], 17714826ab45Sandi $img['width'], 17724826ab45Sandi $img['height'], 17733dd5c225SAndreas Gohr $img['cache'] 17743dd5c225SAndreas Gohr ); 17750cecf9d5Sandi } 1776b739ff0fSPierre Spring 1777b739ff0fSPierre Spring /** 17783dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 17793dd5c225SAndreas Gohr * 17803dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1781b739ff0fSPierre Spring * 1782b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 17833dd5c225SAndreas Gohr * @param string $src media ID 17843dd5c225SAndreas Gohr * @param string $title descriptive text 17853dd5c225SAndreas Gohr * @param string $align left|center|right 17863dd5c225SAndreas Gohr * @param int $width width of media in pixel 17873dd5c225SAndreas Gohr * @param int $height height of media in pixel 17883dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 17893dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 17903dd5c225SAndreas Gohr * @return array associative array with link config 1791b739ff0fSPierre Spring */ 1792d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1793b739ff0fSPierre Spring global $conf; 1794b739ff0fSPierre Spring 1795b739ff0fSPierre Spring $link = array(); 1796b739ff0fSPierre Spring $link['class'] = 'media'; 1797b739ff0fSPierre Spring $link['style'] = ''; 1798b739ff0fSPierre Spring $link['pre'] = ''; 1799b739ff0fSPierre Spring $link['suf'] = ''; 1800b739ff0fSPierre Spring $link['more'] = ''; 1801b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1802bc3d2252SAndreas Gohr if($conf['target']['media']) $link['rel'] = 'noopener'; 1803b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1804b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1805b739ff0fSPierre Spring 1806b739ff0fSPierre Spring return $link; 1807b739ff0fSPierre Spring } 180891459163SAnika Henke 18092a2a2ba2SAnika Henke /** 18102a2a2ba2SAnika Henke * Embed video(s) in HTML 18112a2a2ba2SAnika Henke * 18122a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 18130877a1f1SSchplurtz le Déboulonné * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> 18142a2a2ba2SAnika Henke * 18152a2a2ba2SAnika Henke * @param string $src - ID of video to embed 18162a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 18172a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 18182a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1819f50634f0SAnika Henke * @return string 18202a2a2ba2SAnika Henke */ 182179e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 18222a2a2ba2SAnika Henke // prepare width and height 18232a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 18242a2a2ba2SAnika Henke $atts['width'] = (int) $width; 18252a2a2ba2SAnika Henke $atts['height'] = (int) $height; 18262a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 18272a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 18282a2a2ba2SAnika Henke 1829410ee62aSAnika Henke $posterUrl = ''; 1830410ee62aSAnika Henke $files = array(); 18310877a1f1SSchplurtz le Déboulonné $tracks = array(); 1832410ee62aSAnika Henke $isExternal = media_isexternal($src); 1833410ee62aSAnika Henke 1834410ee62aSAnika Henke if ($isExternal) { 1835410ee62aSAnika Henke // take direct source for external files 1836702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1837410ee62aSAnika Henke $files[$srcMime] = $src; 1838410ee62aSAnika Henke } else { 18393d7a9e0aSAnika Henke // prepare alternative formats 18403d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1841410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 184259bc3b48SGerrit Uitslag $poster = media_alternativefiles($src, array('jpg', 'png')); 18430877a1f1SSchplurtz le Déboulonné $tracks = media_trackfiles($src); 184499f943f6SAnika Henke if(!empty($poster)) { 18452d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 184699f943f6SAnika Henke } 1847410ee62aSAnika Henke } 18482a2a2ba2SAnika Henke 1849f50634f0SAnika Henke $out = ''; 185079e53fe5SAnika Henke // open video tag 1851f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 18523641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1853f50634f0SAnika Henke $out .= '>'.NL; 18543641199aSAnika Henke $fallback = ''; 185579e53fe5SAnika Henke 185679e53fe5SAnika Henke // output source for each alternative video format 1857410ee62aSAnika Henke foreach($files as $mime => $file) { 1858410ee62aSAnika Henke if ($isExternal) { 1859410ee62aSAnika Henke $url = $file; 1860410ee62aSAnika Henke $linkType = 'externalmedia'; 1861410ee62aSAnika Henke } else { 18622d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1863410ee62aSAnika Henke $linkType = 'internalmedia'; 1864410ee62aSAnika Henke } 186517954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 18663d7a9e0aSAnika Henke 1867f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 186879e53fe5SAnika Henke // alternative content (just a link to the file) 1869410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 18703d7a9e0aSAnika Henke } 18712a2a2ba2SAnika Henke 18720877a1f1SSchplurtz le Déboulonné // output each track if any 18730877a1f1SSchplurtz le Déboulonné foreach( $tracks as $trackid => $info ) { 187423c61bbeSSchplurtz le Déboulonné list( $kind, $srclang ) = array_map( 'hsc', $info ); 187523c61bbeSSchplurtz le Déboulonné $out .= "<track kind=\"$kind\" srclang=\"$srclang\" "; 187623c61bbeSSchplurtz le Déboulonné $out .= "label=\"$srclang\" "; 18770877a1f1SSchplurtz le Déboulonné $out .= 'src="'.ml($trackid, '', true).'">'.NL; 18780877a1f1SSchplurtz le Déboulonné } 18790877a1f1SSchplurtz le Déboulonné 18802a2a2ba2SAnika Henke // finish 18813641199aSAnika Henke $out .= $fallback; 1882f50634f0SAnika Henke $out .= '</video>'.NL; 1883f50634f0SAnika Henke return $out; 18842a2a2ba2SAnika Henke } 18852a2a2ba2SAnika Henke 1886b44a5dceSAnika Henke /** 1887b44a5dceSAnika Henke * Embed audio in HTML 1888b44a5dceSAnika Henke * 1889b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1890b44a5dceSAnika Henke * 1891b44a5dceSAnika Henke * @param string $src - ID of audio to embed 18926d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1893f50634f0SAnika Henke * @return string 1894b44a5dceSAnika Henke */ 189559bc3b48SGerrit Uitslag function _audio($src, $atts = array()) { 1896702e97d3SAnika Henke $files = array(); 1897410ee62aSAnika Henke $isExternal = media_isexternal($src); 1898b44a5dceSAnika Henke 1899410ee62aSAnika Henke if ($isExternal) { 1900410ee62aSAnika Henke // take direct source for external files 1901702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1902410ee62aSAnika Henke $files[$srcMime] = $src; 1903410ee62aSAnika Henke } else { 1904b44a5dceSAnika Henke // prepare alternative formats 1905b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1906410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1907410ee62aSAnika Henke } 1908b44a5dceSAnika Henke 1909f50634f0SAnika Henke $out = ''; 1910b44a5dceSAnika Henke // open audio tag 1911f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 19123641199aSAnika Henke $fallback = ''; 1913b44a5dceSAnika Henke 1914b44a5dceSAnika Henke // output source for each alternative audio format 1915410ee62aSAnika Henke foreach($files as $mime => $file) { 1916410ee62aSAnika Henke if ($isExternal) { 1917410ee62aSAnika Henke $url = $file; 1918410ee62aSAnika Henke $linkType = 'externalmedia'; 1919410ee62aSAnika Henke } else { 19202d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1921410ee62aSAnika Henke $linkType = 'internalmedia'; 1922410ee62aSAnika Henke } 192317954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1924b44a5dceSAnika Henke 1925f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1926b44a5dceSAnika Henke // alternative content (just a link to the file) 1927410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1928b44a5dceSAnika Henke } 1929b44a5dceSAnika Henke 1930b44a5dceSAnika Henke // finish 19313641199aSAnika Henke $out .= $fallback; 1932f50634f0SAnika Henke $out .= '</audio>'.NL; 1933f50634f0SAnika Henke return $out; 1934b44a5dceSAnika Henke } 1935b44a5dceSAnika Henke 19365c2eed9aSlisps /** 193752dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 19385c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 19395c2eed9aSlisps * 19405c2eed9aSlisps * @author lisps 19415c2eed9aSlisps * @param string $media_id 19425c2eed9aSlisps * @access protected 19435c2eed9aSlisps * @return string revision ('' for current) 19445c2eed9aSlisps */ 194552dc5eadSlisps function _getLastMediaRevisionAt($media_id){ 194652dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 194778b874e6Slisps $pagelog = new MediaChangeLog($media_id); 194878b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 19495c2eed9aSlisps } 19505c2eed9aSlisps 19513dd5c225SAndreas Gohr #endregion 19520cecf9d5Sandi} 19530cecf9d5Sandi 1954e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1955