10cecf9d5Sandi<?php 2b625487dSandi/** 3b625487dSandi * Renderer for XHTML output 4b625487dSandi * 5b625487dSandi * @author Harry Fuecks <hfuecks@gmail.com> 6b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 7b625487dSandi */ 8fa8adffeSAndreas Gohrif(!defined('DOKU_INC')) die('meh.'); 90cecf9d5Sandi 100cecf9d5Sandiif(!defined('DOKU_LF')) { 110cecf9d5Sandi // Some whitespace to help View > Source 120cecf9d5Sandi define ('DOKU_LF', "\n"); 130cecf9d5Sandi} 140cecf9d5Sandi 150cecf9d5Sandiif(!defined('DOKU_TAB')) { 160cecf9d5Sandi // Some whitespace to help View > Source 170cecf9d5Sandi define ('DOKU_TAB', "\t"); 180cecf9d5Sandi} 190cecf9d5Sandi 200cecf9d5Sandi/** 213dd5c225SAndreas Gohr * The XHTML Renderer 223dd5c225SAndreas Gohr * 233dd5c225SAndreas Gohr * This is DokuWiki's main renderer used to display page content in the wiki 240cecf9d5Sandi */ 25ac83b9d8Sandiclass Doku_Renderer_xhtml extends Doku_Renderer { 263dd5c225SAndreas Gohr /** @var array store the table of contents */ 273dd5c225SAndreas Gohr public $toc = array(); 280cecf9d5Sandi 293dd5c225SAndreas Gohr /** @var array A stack of section edit data */ 303dd5c225SAndreas Gohr protected $sectionedits = array(); 314bde2196Slisps var $date_at = ''; // link pages and media against this revision 32c5a8fd96SAndreas Gohr 333dd5c225SAndreas Gohr /** @var int last section edit id, used by startSectionEdit */ 343dd5c225SAndreas Gohr protected $lastsecid = 0; 350cecf9d5Sandi 363dd5c225SAndreas Gohr /** @var array the list of headers used to create unique link ids */ 373dd5c225SAndreas Gohr protected $headers = array(); 383dd5c225SAndreas Gohr 3916ec3e37SAndreas Gohr /** @var array a list of footnotes, list starts at 1! */ 403dd5c225SAndreas Gohr protected $footnotes = array(); 417764a90aSandi 423dd5c225SAndreas Gohr /** @var int current section level */ 433dd5c225SAndreas Gohr protected $lastlevel = 0; 443dd5c225SAndreas Gohr /** @var array section node tracker */ 453dd5c225SAndreas Gohr protected $node = array(0, 0, 0, 0, 0); 463dd5c225SAndreas Gohr 473dd5c225SAndreas Gohr /** @var string temporary $doc store */ 483dd5c225SAndreas Gohr protected $store = ''; 493dd5c225SAndreas Gohr 503dd5c225SAndreas Gohr /** @var array global counter, for table classes etc. */ 513dd5c225SAndreas Gohr protected $_counter = array(); // 523dd5c225SAndreas Gohr 533dd5c225SAndreas Gohr /** @var int counts the code and file blocks, used to provide download links */ 543dd5c225SAndreas Gohr protected $_codeblock = 0; 553dd5c225SAndreas Gohr 563dd5c225SAndreas Gohr /** @var array list of allowed URL schemes */ 573dd5c225SAndreas Gohr protected $schemes = null; 58b5742cedSPierre Spring 5990df9a4dSAdrian Lang /** 6090df9a4dSAdrian Lang * Register a new edit section range 6190df9a4dSAdrian Lang * 6242ea7f44SGerrit Uitslag * @param int $start The byte position for the edit start 63ec57f119SLarsDW223 * @param array $data Associative array with section data: 64ec57f119SLarsDW223 * Key 'name': the section name/title 65ec57f119SLarsDW223 * Key 'target': the target for the section edit, 66ec57f119SLarsDW223 * e.g. 'section' or 'table' 67ec57f119SLarsDW223 * Key 'hid': header id 68ec57f119SLarsDW223 * Key 'codeblockOffset': actual code block index 69ec57f119SLarsDW223 * Key 'start': set in startSectionEdit(), 70ec57f119SLarsDW223 * do not set yourself 71ec57f119SLarsDW223 * Key 'range': calculated from 'start' and 72ec57f119SLarsDW223 * $key in finishSectionEdit(), 73ec57f119SLarsDW223 * do not set yourself 7490df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 7542ea7f44SGerrit Uitslag * 7690df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 7790df9a4dSAdrian Lang */ 78ec57f119SLarsDW223 public function startSectionEdit($start, $data) { 79ec57f119SLarsDW223 if (!is_array($data)) { 80ac025fdfSAndreas Gohr msg( 81ac025fdfSAndreas Gohr sprintf( 82ac025fdfSAndreas Gohr 'startSectionEdit: $data "%s" is NOT an array! One of your plugins needs an update.', 83ac025fdfSAndreas Gohr hsc((string) $data) 84ac025fdfSAndreas Gohr ), -1 85ac025fdfSAndreas Gohr ); 86ac025fdfSAndreas Gohr 87ac025fdfSAndreas Gohr // @deprecated 2018-04-14, backward compatibility 88ac025fdfSAndreas Gohr $args = func_get_args(); 89ac025fdfSAndreas Gohr $data = array(); 90ac025fdfSAndreas Gohr if(isset($args[1])) $data['target'] = $args[1]; 91ac025fdfSAndreas Gohr if(isset($args[2])) $data['name'] = $args[2]; 92ac025fdfSAndreas Gohr if(isset($args[3])) $data['hid'] = $args[3]; 93ec57f119SLarsDW223 } 94ec57f119SLarsDW223 $data['secid'] = ++$this->lastsecid; 95ec57f119SLarsDW223 $data['start'] = $start; 96ec57f119SLarsDW223 $this->sectionedits[] = $data; 97ec57f119SLarsDW223 return 'sectionedit'.$data['secid']; 9890df9a4dSAdrian Lang } 9990df9a4dSAdrian Lang 10090df9a4dSAdrian Lang /** 10190df9a4dSAdrian Lang * Finish an edit section range 10290df9a4dSAdrian Lang * 10342ea7f44SGerrit Uitslag * @param int $end The byte position for the edit end; null for the rest of the page 10442ea7f44SGerrit Uitslag * 10590df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 10690df9a4dSAdrian Lang */ 1072571786cSLarsDW223 public function finishSectionEdit($end = null, $hid = null) { 108ec57f119SLarsDW223 $data = array_pop($this->sectionedits); 109ec57f119SLarsDW223 if(!is_null($end) && $end <= $data['start']) { 11000c13053SAdrian Lang return; 11100c13053SAdrian Lang } 1122571786cSLarsDW223 if(!is_null($hid)) { 113ec57f119SLarsDW223 $data['hid'] .= $hid; 1142571786cSLarsDW223 } 115ec57f119SLarsDW223 $data['range'] = $data['start'].'-'.(is_null($end) ? '' : $end); 116ec57f119SLarsDW223 unset($data['start']); 117ada0d779SMichael Hamann $this->doc .= '<!-- EDIT'.hsc(json_encode ($data)).' -->'; 11890df9a4dSAdrian Lang } 11990df9a4dSAdrian Lang 1203dd5c225SAndreas Gohr /** 1213dd5c225SAndreas Gohr * Returns the format produced by this renderer. 1223dd5c225SAndreas Gohr * 1233dd5c225SAndreas Gohr * @return string always 'xhtml' 1243dd5c225SAndreas Gohr */ 1255f70445dSAndreas Gohr function getFormat() { 1265f70445dSAndreas Gohr return 'xhtml'; 1275f70445dSAndreas Gohr } 1285f70445dSAndreas Gohr 1293dd5c225SAndreas Gohr /** 1303dd5c225SAndreas Gohr * Initialize the document 1313dd5c225SAndreas Gohr */ 1320cecf9d5Sandi function document_start() { 133c5a8fd96SAndreas Gohr //reset some internals 134c5a8fd96SAndreas Gohr $this->toc = array(); 135c5a8fd96SAndreas Gohr $this->headers = array(); 1360cecf9d5Sandi } 1370cecf9d5Sandi 1383dd5c225SAndreas Gohr /** 1393dd5c225SAndreas Gohr * Finalize the document 1403dd5c225SAndreas Gohr */ 1410cecf9d5Sandi function document_end() { 14290df9a4dSAdrian Lang // Finish open section edits. 14390df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 144ec57f119SLarsDW223 if($this->sectionedits[count($this->sectionedits) - 1]['start'] <= 1) { 14590df9a4dSAdrian Lang // If there is only one section, do not write a section edit 14690df9a4dSAdrian Lang // marker. 14790df9a4dSAdrian Lang array_pop($this->sectionedits); 14890df9a4dSAdrian Lang } else { 149d9e36cbeSAdrian Lang $this->finishSectionEdit(); 15090df9a4dSAdrian Lang } 15190df9a4dSAdrian Lang } 15290df9a4dSAdrian Lang 1530cecf9d5Sandi if(count($this->footnotes) > 0) { 154a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 155d74aace9Schris 15616ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 157d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 158d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 159d74aace9Schris 160d74aace9Schris // open the footnote and set the anchor and backlink 161d74aace9Schris $this->doc .= '<div class="fn">'; 16216cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 16329bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 164d74aace9Schris 165d74aace9Schris // get any other footnotes that use the same markup 166d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 167d74aace9Schris 168d74aace9Schris if(count($alt)) { 169d74aace9Schris foreach($alt as $ref) { 170d74aace9Schris // set anchor and backlink for the other footnotes 17116ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 17216ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 173d74aace9Schris } 174d74aace9Schris } 175d74aace9Schris 176d74aace9Schris // add footnote markup and close this footnote 177694afa06SAnika Henke $this->doc .= '<div class="content">'.$footnote.'</div>'; 178d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 179d74aace9Schris } 1800cecf9d5Sandi } 181a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1820cecf9d5Sandi } 183c5a8fd96SAndreas Gohr 184b8595a66SAndreas Gohr // Prepare the TOC 185851f2e89SAnika Henke global $conf; 186851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 187b8595a66SAndreas Gohr global $TOC; 188b8595a66SAndreas Gohr $TOC = $this->toc; 1890cecf9d5Sandi } 1903e55d035SAndreas Gohr 1913e55d035SAndreas Gohr // make sure there are no empty paragraphs 19227918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 193e41c4da9SAndreas Gohr } 1940cecf9d5Sandi 1953dd5c225SAndreas Gohr /** 1963dd5c225SAndreas Gohr * Add an item to the TOC 1973dd5c225SAndreas Gohr * 1983dd5c225SAndreas Gohr * @param string $id the hash link 1993dd5c225SAndreas Gohr * @param string $text the text to display 2003dd5c225SAndreas Gohr * @param int $level the nesting level 2013dd5c225SAndreas Gohr */ 202e7856beaSchris function toc_additem($id, $text, $level) { 203af587fa8Sandi global $conf; 204af587fa8Sandi 205c5a8fd96SAndreas Gohr //handle TOC 206c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 2077d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 208c5a8fd96SAndreas Gohr } 209e7856beaSchris } 210e7856beaSchris 2113dd5c225SAndreas Gohr /** 2123dd5c225SAndreas Gohr * Render a heading 2133dd5c225SAndreas Gohr * 2143dd5c225SAndreas Gohr * @param string $text the text to display 2153dd5c225SAndreas Gohr * @param int $level header level 2163dd5c225SAndreas Gohr * @param int $pos byte position in the original source 2173dd5c225SAndreas Gohr */ 218e7856beaSchris function header($text, $level, $pos) { 21990df9a4dSAdrian Lang global $conf; 22090df9a4dSAdrian Lang 221f515db7fSAndreas Gohr if(blank($text)) return; //skip empty headlines 222e7856beaSchris 223e7856beaSchris $hid = $this->_headerToLink($text, true); 224e7856beaSchris 225e7856beaSchris //only add items within configured levels 226e7856beaSchris $this->toc_additem($hid, $text, $level); 227c5a8fd96SAndreas Gohr 22891459163SAnika Henke // adjust $node to reflect hierarchy of levels 22991459163SAnika Henke $this->node[$level - 1]++; 23091459163SAnika Henke if($level < $this->lastlevel) { 23191459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 23291459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 23391459163SAnika Henke } 23491459163SAnika Henke } 23591459163SAnika Henke $this->lastlevel = $level; 23691459163SAnika Henke 23790df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 23890df9a4dSAdrian Lang count($this->sectionedits) > 0 && 239ec57f119SLarsDW223 $this->sectionedits[count($this->sectionedits) - 1]['target'] === 'section' 2403dd5c225SAndreas Gohr ) { 2416c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 24290df9a4dSAdrian Lang } 24390df9a4dSAdrian Lang 244c5a8fd96SAndreas Gohr // write the header 24590df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 24690df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 247ec57f119SLarsDW223 $data = array(); 248ec57f119SLarsDW223 $data['target'] = 'section'; 249ec57f119SLarsDW223 $data['name'] = $text; 250ec57f119SLarsDW223 $data['hid'] = $hid; 251ec57f119SLarsDW223 $data['codeblockOffset'] = $this->_codeblock; 252ec57f119SLarsDW223 $this->doc .= ' class="'.$this->startSectionEdit($pos, $data).'"'; 25390df9a4dSAdrian Lang } 25416cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 255a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 25616cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2570cecf9d5Sandi } 2580cecf9d5Sandi 2593dd5c225SAndreas Gohr /** 2603dd5c225SAndreas Gohr * Open a new section 2613dd5c225SAndreas Gohr * 2623dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 2633dd5c225SAndreas Gohr */ 2640cecf9d5Sandi function section_open($level) { 2659864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2660cecf9d5Sandi } 2670cecf9d5Sandi 2683dd5c225SAndreas Gohr /** 2693dd5c225SAndreas Gohr * Close the current section 2703dd5c225SAndreas Gohr */ 2710cecf9d5Sandi function section_close() { 272a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2730cecf9d5Sandi } 2740cecf9d5Sandi 2753dd5c225SAndreas Gohr /** 2763dd5c225SAndreas Gohr * Render plain text data 2773dd5c225SAndreas Gohr * 2783dd5c225SAndreas Gohr * @param $text 2793dd5c225SAndreas Gohr */ 2800cecf9d5Sandi function cdata($text) { 281a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2820cecf9d5Sandi } 2830cecf9d5Sandi 2843dd5c225SAndreas Gohr /** 2853dd5c225SAndreas Gohr * Open a paragraph 2863dd5c225SAndreas Gohr */ 2870cecf9d5Sandi function p_open() { 28859869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2890cecf9d5Sandi } 2900cecf9d5Sandi 2913dd5c225SAndreas Gohr /** 2923dd5c225SAndreas Gohr * Close a paragraph 2933dd5c225SAndreas Gohr */ 2940cecf9d5Sandi function p_close() { 29559869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2960cecf9d5Sandi } 2970cecf9d5Sandi 2983dd5c225SAndreas Gohr /** 2993dd5c225SAndreas Gohr * Create a line break 3003dd5c225SAndreas Gohr */ 3010cecf9d5Sandi function linebreak() { 302a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 3030cecf9d5Sandi } 3040cecf9d5Sandi 3053dd5c225SAndreas Gohr /** 3063dd5c225SAndreas Gohr * Create a horizontal line 3073dd5c225SAndreas Gohr */ 3080cecf9d5Sandi function hr() { 3094beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 3100cecf9d5Sandi } 3110cecf9d5Sandi 3123dd5c225SAndreas Gohr /** 3133dd5c225SAndreas Gohr * Start strong (bold) formatting 3143dd5c225SAndreas Gohr */ 3150cecf9d5Sandi function strong_open() { 316a2d649c4Sandi $this->doc .= '<strong>'; 3170cecf9d5Sandi } 3180cecf9d5Sandi 3193dd5c225SAndreas Gohr /** 3203dd5c225SAndreas Gohr * Stop strong (bold) formatting 3213dd5c225SAndreas Gohr */ 3220cecf9d5Sandi function strong_close() { 323a2d649c4Sandi $this->doc .= '</strong>'; 3240cecf9d5Sandi } 3250cecf9d5Sandi 3263dd5c225SAndreas Gohr /** 3273dd5c225SAndreas Gohr * Start emphasis (italics) formatting 3283dd5c225SAndreas Gohr */ 3290cecf9d5Sandi function emphasis_open() { 330a2d649c4Sandi $this->doc .= '<em>'; 3310cecf9d5Sandi } 3320cecf9d5Sandi 3333dd5c225SAndreas Gohr /** 3343dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 3353dd5c225SAndreas Gohr */ 3360cecf9d5Sandi function emphasis_close() { 337a2d649c4Sandi $this->doc .= '</em>'; 3380cecf9d5Sandi } 3390cecf9d5Sandi 3403dd5c225SAndreas Gohr /** 3413dd5c225SAndreas Gohr * Start underline formatting 3423dd5c225SAndreas Gohr */ 3430cecf9d5Sandi function underline_open() { 34402e51121SAnika Henke $this->doc .= '<em class="u">'; 3450cecf9d5Sandi } 3460cecf9d5Sandi 3473dd5c225SAndreas Gohr /** 3483dd5c225SAndreas Gohr * Stop underline formatting 3493dd5c225SAndreas Gohr */ 3500cecf9d5Sandi function underline_close() { 35102e51121SAnika Henke $this->doc .= '</em>'; 3520cecf9d5Sandi } 3530cecf9d5Sandi 3543dd5c225SAndreas Gohr /** 3553dd5c225SAndreas Gohr * Start monospace formatting 3563dd5c225SAndreas Gohr */ 3570cecf9d5Sandi function monospace_open() { 358a2d649c4Sandi $this->doc .= '<code>'; 3590cecf9d5Sandi } 3600cecf9d5Sandi 3613dd5c225SAndreas Gohr /** 3623dd5c225SAndreas Gohr * Stop monospace formatting 3633dd5c225SAndreas Gohr */ 3640cecf9d5Sandi function monospace_close() { 365a2d649c4Sandi $this->doc .= '</code>'; 3660cecf9d5Sandi } 3670cecf9d5Sandi 3683dd5c225SAndreas Gohr /** 3693dd5c225SAndreas Gohr * Start a subscript 3703dd5c225SAndreas Gohr */ 3710cecf9d5Sandi function subscript_open() { 372a2d649c4Sandi $this->doc .= '<sub>'; 3730cecf9d5Sandi } 3740cecf9d5Sandi 3753dd5c225SAndreas Gohr /** 3763dd5c225SAndreas Gohr * Stop a subscript 3773dd5c225SAndreas Gohr */ 3780cecf9d5Sandi function subscript_close() { 379a2d649c4Sandi $this->doc .= '</sub>'; 3800cecf9d5Sandi } 3810cecf9d5Sandi 3823dd5c225SAndreas Gohr /** 3833dd5c225SAndreas Gohr * Start a superscript 3843dd5c225SAndreas Gohr */ 3850cecf9d5Sandi function superscript_open() { 386a2d649c4Sandi $this->doc .= '<sup>'; 3870cecf9d5Sandi } 3880cecf9d5Sandi 3893dd5c225SAndreas Gohr /** 3903dd5c225SAndreas Gohr * Stop a superscript 3913dd5c225SAndreas Gohr */ 3920cecf9d5Sandi function superscript_close() { 393a2d649c4Sandi $this->doc .= '</sup>'; 3940cecf9d5Sandi } 3950cecf9d5Sandi 3963dd5c225SAndreas Gohr /** 3973dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 3983dd5c225SAndreas Gohr */ 3990cecf9d5Sandi function deleted_open() { 400a2d649c4Sandi $this->doc .= '<del>'; 4010cecf9d5Sandi } 4020cecf9d5Sandi 4033dd5c225SAndreas Gohr /** 4043dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 4053dd5c225SAndreas Gohr */ 4060cecf9d5Sandi function deleted_close() { 407a2d649c4Sandi $this->doc .= '</del>'; 4080cecf9d5Sandi } 4090cecf9d5Sandi 4103fd0b676Sandi /** 4113fd0b676Sandi * Callback for footnote start syntax 4123fd0b676Sandi * 4133fd0b676Sandi * All following content will go to the footnote instead of 414d74aace9Schris * the document. To achieve this the previous rendered content 4153fd0b676Sandi * is moved to $store and $doc is cleared 4163fd0b676Sandi * 4173fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 4183fd0b676Sandi */ 4190cecf9d5Sandi function footnote_open() { 4207764a90aSandi 4217764a90aSandi // move current content to store and record footnote 4227764a90aSandi $this->store = $this->doc; 4237764a90aSandi $this->doc = ''; 4240cecf9d5Sandi } 4250cecf9d5Sandi 4263fd0b676Sandi /** 4273fd0b676Sandi * Callback for footnote end syntax 4283fd0b676Sandi * 4293fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 4303fd0b676Sandi * content is restored from $store again 4313fd0b676Sandi * 4323fd0b676Sandi * @author Andreas Gohr 4333fd0b676Sandi */ 4340cecf9d5Sandi function footnote_close() { 43516ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 43616ec3e37SAndreas Gohr static $fnid = 0; 43716ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 43816ec3e37SAndreas Gohr $fnid++; 4397764a90aSandi 440d74aace9Schris // recover footnote into the stack and restore old content 441d74aace9Schris $footnote = $this->doc; 4427764a90aSandi $this->doc = $this->store; 4437764a90aSandi $this->store = ''; 444d74aace9Schris 445d74aace9Schris // check to see if this footnote has been seen before 446d74aace9Schris $i = array_search($footnote, $this->footnotes); 447d74aace9Schris 448d74aace9Schris if($i === false) { 449d74aace9Schris // its a new footnote, add it to the $footnotes array 45016ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 451d74aace9Schris } else { 45216ec3e37SAndreas Gohr // seen this one before, save a placeholder 45316ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 454d74aace9Schris } 455d74aace9Schris 4566b379cbfSAndreas Gohr // output the footnote reference and link 45716ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4580cecf9d5Sandi } 4590cecf9d5Sandi 4603dd5c225SAndreas Gohr /** 4613dd5c225SAndreas Gohr * Open an unordered list 4620c4c0281SGerrit Uitslag * 4637d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4643dd5c225SAndreas Gohr */ 4650c4c0281SGerrit Uitslag function listu_open($classes = null) { 4660c4c0281SGerrit Uitslag $class = ''; 4670c4c0281SGerrit Uitslag if($classes !== null) { 4682e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4690c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4700c4c0281SGerrit Uitslag } 4710c4c0281SGerrit Uitslag $this->doc .= "<ul$class>".DOKU_LF; 4720cecf9d5Sandi } 4730cecf9d5Sandi 4743dd5c225SAndreas Gohr /** 4753dd5c225SAndreas Gohr * Close an unordered list 4763dd5c225SAndreas Gohr */ 4770cecf9d5Sandi function listu_close() { 478a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4790cecf9d5Sandi } 4800cecf9d5Sandi 4813dd5c225SAndreas Gohr /** 4823dd5c225SAndreas Gohr * Open an ordered list 4830c4c0281SGerrit Uitslag * 4847d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 4853dd5c225SAndreas Gohr */ 4860c4c0281SGerrit Uitslag function listo_open($classes = null) { 4870c4c0281SGerrit Uitslag $class = ''; 4880c4c0281SGerrit Uitslag if($classes !== null) { 4892e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 4900c4c0281SGerrit Uitslag $class = " class=\"$classes\""; 4910c4c0281SGerrit Uitslag } 4920c4c0281SGerrit Uitslag $this->doc .= "<ol$class>".DOKU_LF; 4930cecf9d5Sandi } 4940cecf9d5Sandi 4953dd5c225SAndreas Gohr /** 4963dd5c225SAndreas Gohr * Close an ordered list 4973dd5c225SAndreas Gohr */ 4980cecf9d5Sandi function listo_close() { 499a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 5000cecf9d5Sandi } 5010cecf9d5Sandi 5023dd5c225SAndreas Gohr /** 5033dd5c225SAndreas Gohr * Open a list item 5043dd5c225SAndreas Gohr * 5053dd5c225SAndreas Gohr * @param int $level the nesting level 506e3a24861SChristopher Smith * @param bool $node true when a node; false when a leaf 5073dd5c225SAndreas Gohr */ 508e3a24861SChristopher Smith function listitem_open($level, $node=false) { 509e3a24861SChristopher Smith $branching = $node ? ' node' : ''; 510e3a24861SChristopher Smith $this->doc .= '<li class="level'.$level.$branching.'">'; 5110cecf9d5Sandi } 5120cecf9d5Sandi 5133dd5c225SAndreas Gohr /** 5143dd5c225SAndreas Gohr * Close a list item 5153dd5c225SAndreas Gohr */ 5160cecf9d5Sandi function listitem_close() { 517a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 5180cecf9d5Sandi } 5190cecf9d5Sandi 5203dd5c225SAndreas Gohr /** 5213dd5c225SAndreas Gohr * Start the content of a list item 5223dd5c225SAndreas Gohr */ 5230cecf9d5Sandi function listcontent_open() { 52490db23d7Schris $this->doc .= '<div class="li">'; 5250cecf9d5Sandi } 5260cecf9d5Sandi 5273dd5c225SAndreas Gohr /** 5283dd5c225SAndreas Gohr * Stop the content of a list item 5293dd5c225SAndreas Gohr */ 5300cecf9d5Sandi function listcontent_close() { 53159869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 5320cecf9d5Sandi } 5330cecf9d5Sandi 5343dd5c225SAndreas Gohr /** 5353dd5c225SAndreas Gohr * Output unformatted $text 5363dd5c225SAndreas Gohr * 5373dd5c225SAndreas Gohr * Defaults to $this->cdata() 5383dd5c225SAndreas Gohr * 5393dd5c225SAndreas Gohr * @param string $text 5403dd5c225SAndreas Gohr */ 5410cecf9d5Sandi function unformatted($text) { 542a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 5430cecf9d5Sandi } 5440cecf9d5Sandi 5450cecf9d5Sandi /** 5463fd0b676Sandi * Execute PHP code if allowed 5473fd0b676Sandi * 548d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 5495d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5505d568b99SChris Smith * 5513fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5520cecf9d5Sandi */ 5535d568b99SChris Smith function php($text, $wrapper = 'code') { 55435a56260SChris Smith global $conf; 55535a56260SChris Smith 556d86d5af0SChris Smith if($conf['phpok']) { 557bad0b545Sandi ob_start(); 5584de671bcSandi eval($text); 5593fd0b676Sandi $this->doc .= ob_get_contents(); 560bad0b545Sandi ob_end_clean(); 561d86d5af0SChris Smith } else { 5625d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 563d86d5af0SChris Smith } 5640cecf9d5Sandi } 5650cecf9d5Sandi 5663dd5c225SAndreas Gohr /** 5673dd5c225SAndreas Gohr * Output block level PHP code 5683dd5c225SAndreas Gohr * 5693dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 5703dd5c225SAndreas Gohr * to $doc 5713dd5c225SAndreas Gohr * 5723dd5c225SAndreas Gohr * @param string $text The PHP code 5733dd5c225SAndreas Gohr */ 57407f89c3cSAnika Henke function phpblock($text) { 5755d568b99SChris Smith $this->php($text, 'pre'); 57607f89c3cSAnika Henke } 57707f89c3cSAnika Henke 5780cecf9d5Sandi /** 5793fd0b676Sandi * Insert HTML if allowed 5803fd0b676Sandi * 581d9764001SMichael Hamann * @param string $text html text 5825d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5835d568b99SChris Smith * 5843fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5850cecf9d5Sandi */ 5865d568b99SChris Smith function html($text, $wrapper = 'code') { 58735a56260SChris Smith global $conf; 58835a56260SChris Smith 589d86d5af0SChris Smith if($conf['htmlok']) { 590a2d649c4Sandi $this->doc .= $text; 591d86d5af0SChris Smith } else { 5925d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 593d86d5af0SChris Smith } 5944de671bcSandi } 5950cecf9d5Sandi 5963dd5c225SAndreas Gohr /** 5973dd5c225SAndreas Gohr * Output raw block-level HTML 5983dd5c225SAndreas Gohr * 5993dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 6003dd5c225SAndreas Gohr * 6013dd5c225SAndreas Gohr * @param string $text The HTML 6023dd5c225SAndreas Gohr */ 60307f89c3cSAnika Henke function htmlblock($text) { 6045d568b99SChris Smith $this->html($text, 'pre'); 60507f89c3cSAnika Henke } 60607f89c3cSAnika Henke 6073dd5c225SAndreas Gohr /** 6083dd5c225SAndreas Gohr * Start a block quote 6093dd5c225SAndreas Gohr */ 6100cecf9d5Sandi function quote_open() { 61196331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 6120cecf9d5Sandi } 6130cecf9d5Sandi 6143dd5c225SAndreas Gohr /** 6153dd5c225SAndreas Gohr * Stop a block quote 6163dd5c225SAndreas Gohr */ 6170cecf9d5Sandi function quote_close() { 61896331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 6190cecf9d5Sandi } 6200cecf9d5Sandi 6213dd5c225SAndreas Gohr /** 6223dd5c225SAndreas Gohr * Output preformatted text 6233dd5c225SAndreas Gohr * 6243dd5c225SAndreas Gohr * @param string $text 6253dd5c225SAndreas Gohr */ 6263d491f75SAndreas Gohr function preformatted($text) { 627c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 6283d491f75SAndreas Gohr } 6293d491f75SAndreas Gohr 6303dd5c225SAndreas Gohr /** 6313dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 6323dd5c225SAndreas Gohr * 6333dd5c225SAndreas Gohr * @param string $text text to show 6343dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6353dd5c225SAndreas Gohr * @param string $filename file path label 636e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6373dd5c225SAndreas Gohr */ 638e2d88156SLarsDW223 function file($text, $language = null, $filename = null, $options=null) { 639e2d88156SLarsDW223 $this->_highlight('file', $text, $language, $filename, $options); 6403d491f75SAndreas Gohr } 6413d491f75SAndreas Gohr 6423dd5c225SAndreas Gohr /** 6433dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 6443dd5c225SAndreas Gohr * 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 6493dd5c225SAndreas Gohr */ 650e2d88156SLarsDW223 function code($text, $language = null, $filename = null, $options=null) { 651e2d88156SLarsDW223 $this->_highlight('code', $text, $language, $filename, $options); 6523d491f75SAndreas Gohr } 6533d491f75SAndreas Gohr 6540cecf9d5Sandi /** 6553d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6563fd0b676Sandi * 6573fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 6583dd5c225SAndreas Gohr * @param string $type code|file 6593dd5c225SAndreas Gohr * @param string $text text to show 6603dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 6613dd5c225SAndreas Gohr * @param string $filename file path label 662e2d88156SLarsDW223 * @param array $options assoziative array with additional geshi options 6630cecf9d5Sandi */ 664e2d88156SLarsDW223 function _highlight($type, $text, $language = null, $filename = null, $options = null) { 6653d491f75SAndreas Gohr global $ID; 6663d491f75SAndreas Gohr global $lang; 667ec57f119SLarsDW223 global $INPUT; 6683d491f75SAndreas Gohr 66956bd9509SPhy $language = preg_replace(PREG_PATTERN_VALID_LANGUAGE, '', $language); 67056bd9509SPhy 6713d491f75SAndreas Gohr if($filename) { 672190c56e8SAndreas Gohr // add icon 67327bf7924STom N Harris list($ext) = mimetype($filename, false); 674190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 675190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 676190c56e8SAndreas Gohr 677ec57f119SLarsDW223 $offset = 0; 678ec57f119SLarsDW223 if ($INPUT->has('codeblockOffset')) { 679ec57f119SLarsDW223 $offset = $INPUT->str('codeblockOffset'); 680ec57f119SLarsDW223 } 6813d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 682ec57f119SLarsDW223 $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $offset+$this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6833d491f75SAndreas Gohr $this->doc .= hsc($filename); 6843d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6853d491f75SAndreas Gohr } 6860cecf9d5Sandi 687d43aac1cSGina Haeussge if($text{0} == "\n") { 688d43aac1cSGina Haeussge $text = substr($text, 1); 689d43aac1cSGina Haeussge } 690d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 691d43aac1cSGina Haeussge $text = substr($text, 0, -1); 692d43aac1cSGina Haeussge } 693d43aac1cSGina Haeussge 694a056e285SPhy if(empty($language)) { // empty is faster than is_null and can prevent '' string 6953d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6960cecf9d5Sandi } else { 6973d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6983d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6993d491f75SAndreas Gohr 700e2d88156SLarsDW223 $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '', $options).'</pre>'.DOKU_LF; 7010cecf9d5Sandi } 7023d491f75SAndreas Gohr 7033d491f75SAndreas Gohr if($filename) { 7043d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 7053d491f75SAndreas Gohr } 7063d491f75SAndreas Gohr 7073d491f75SAndreas Gohr $this->_codeblock++; 7080cecf9d5Sandi } 7090cecf9d5Sandi 7103dd5c225SAndreas Gohr /** 7113dd5c225SAndreas Gohr * Format an acronym 7123dd5c225SAndreas Gohr * 7133dd5c225SAndreas Gohr * Uses $this->acronyms 7143dd5c225SAndreas Gohr * 7153dd5c225SAndreas Gohr * @param string $acronym 7163dd5c225SAndreas Gohr */ 7170cecf9d5Sandi function acronym($acronym) { 7180cecf9d5Sandi 7190cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 7200cecf9d5Sandi 721433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 7220cecf9d5Sandi 723940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 724940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 7250cecf9d5Sandi 7260cecf9d5Sandi } else { 727a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 7280cecf9d5Sandi } 7290cecf9d5Sandi } 7300cecf9d5Sandi 7313dd5c225SAndreas Gohr /** 7323dd5c225SAndreas Gohr * Format a smiley 7333dd5c225SAndreas Gohr * 7343dd5c225SAndreas Gohr * Uses $this->smiley 7353dd5c225SAndreas Gohr * 7363dd5c225SAndreas Gohr * @param string $smiley 7373dd5c225SAndreas Gohr */ 7380cecf9d5Sandi function smiley($smiley) { 7390cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 740f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 7418e38227fSAnika Henke '" class="icon" alt="'. 742433bef32Sandi $this->_xmlEntities($smiley).'" />'; 7430cecf9d5Sandi } else { 744a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 7450cecf9d5Sandi } 7460cecf9d5Sandi } 7470cecf9d5Sandi 7483dd5c225SAndreas Gohr /** 7493dd5c225SAndreas Gohr * Format an entity 7503dd5c225SAndreas Gohr * 7513dd5c225SAndreas Gohr * Entities are basically small text replacements 7523dd5c225SAndreas Gohr * 7533dd5c225SAndreas Gohr * Uses $this->entities 7543dd5c225SAndreas Gohr * 7553dd5c225SAndreas Gohr * @param string $entity 7564de671bcSandi */ 7570cecf9d5Sandi function entity($entity) { 7580cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 759a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7600cecf9d5Sandi } else { 761a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7620cecf9d5Sandi } 7630cecf9d5Sandi } 7640cecf9d5Sandi 7653dd5c225SAndreas Gohr /** 7663dd5c225SAndreas Gohr * Typographically format a multiply sign 7673dd5c225SAndreas Gohr * 7683dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 7693dd5c225SAndreas Gohr * 7703dd5c225SAndreas Gohr * @param string|int $x first value 7713dd5c225SAndreas Gohr * @param string|int $y second value 7723dd5c225SAndreas Gohr */ 7730cecf9d5Sandi function multiplyentity($x, $y) { 774a2d649c4Sandi $this->doc .= "$x×$y"; 7750cecf9d5Sandi } 7760cecf9d5Sandi 7773dd5c225SAndreas Gohr /** 7783dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 7793dd5c225SAndreas Gohr */ 7800cecf9d5Sandi function singlequoteopening() { 78171b40da2SAnika Henke global $lang; 78271b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7830cecf9d5Sandi } 7840cecf9d5Sandi 7853dd5c225SAndreas Gohr /** 7863dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 7873dd5c225SAndreas Gohr */ 7880cecf9d5Sandi function singlequoteclosing() { 78971b40da2SAnika Henke global $lang; 79071b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7910cecf9d5Sandi } 7920cecf9d5Sandi 7933dd5c225SAndreas Gohr /** 7943dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 7953dd5c225SAndreas Gohr */ 79657d757d1SAndreas Gohr function apostrophe() { 79757d757d1SAndreas Gohr global $lang; 798a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 79957d757d1SAndreas Gohr } 80057d757d1SAndreas Gohr 8013dd5c225SAndreas Gohr /** 8023dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 8033dd5c225SAndreas Gohr */ 8040cecf9d5Sandi function doublequoteopening() { 80571b40da2SAnika Henke global $lang; 80671b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 8070cecf9d5Sandi } 8080cecf9d5Sandi 8093dd5c225SAndreas Gohr /** 8103dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 8113dd5c225SAndreas Gohr */ 8120cecf9d5Sandi function doublequoteclosing() { 81371b40da2SAnika Henke global $lang; 81471b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 8150cecf9d5Sandi } 8160cecf9d5Sandi 8170cecf9d5Sandi /** 8183dd5c225SAndreas Gohr * Render a CamelCase link 8193dd5c225SAndreas Gohr * 8203dd5c225SAndreas Gohr * @param string $link The link name 821122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8220c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8230c4c0281SGerrit Uitslag * 8243dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 8250cecf9d5Sandi */ 826122f2d46SAndreas Böhler function camelcaselink($link, $returnonly = false) { 827122f2d46SAndreas Böhler if($returnonly) { 828122f2d46SAndreas Böhler return $this->internallink($link, $link, null, true); 829122f2d46SAndreas Böhler } else { 83011d0aa47Sandi $this->internallink($link, $link); 8310cecf9d5Sandi } 832122f2d46SAndreas Böhler } 8330cecf9d5Sandi 8343dd5c225SAndreas Gohr /** 8353dd5c225SAndreas Gohr * Render a page local link 8363dd5c225SAndreas Gohr * 8373dd5c225SAndreas Gohr * @param string $hash hash link identifier 8383dd5c225SAndreas Gohr * @param string $name name for the link 839122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 8400c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 8413dd5c225SAndreas Gohr */ 842122f2d46SAndreas Böhler function locallink($hash, $name = null, $returnonly = false) { 8430b7c14c2Sandi global $ID; 8440b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 8450b7c14c2Sandi $hash = $this->_headerToLink($hash); 846e260f93bSAnika Henke $title = $ID.' ↵'; 847122f2d46SAndreas Böhler 848122f2d46SAndreas Böhler $doc = '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 849122f2d46SAndreas Böhler $doc .= $name; 850122f2d46SAndreas Böhler $doc .= '</a>'; 851122f2d46SAndreas Böhler 852122f2d46SAndreas Böhler if($returnonly) { 853122f2d46SAndreas Böhler return $doc; 854122f2d46SAndreas Böhler } else { 855122f2d46SAndreas Böhler $this->doc .= $doc; 856122f2d46SAndreas Böhler } 8570b7c14c2Sandi } 8580b7c14c2Sandi 859cffcc403Sandi /** 8603fd0b676Sandi * Render an internal Wiki Link 8613fd0b676Sandi * 862fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 863cffcc403Sandi * elsewhere - no need to implement them in other renderers 8643fd0b676Sandi * 8653dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 866f23eef27SGerrit Uitslag * @param string $id pageid 867f23eef27SGerrit Uitslag * @param string|null $name link name 868f23eef27SGerrit Uitslag * @param string|null $search adds search url param 869f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 870f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 871f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 872cffcc403Sandi */ 8730ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 874ba11bd29Sandi global $conf; 87537e34a5eSandi global $ID; 876c4dda6afSAnika Henke global $INFO; 87744653a53SAdrian Lang 8783d5e07d9SAdrian Lang $params = ''; 8793d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8803d5e07d9SAdrian Lang if(count($parts) === 2) { 8813d5e07d9SAdrian Lang $id = $parts[0]; 8823d5e07d9SAdrian Lang $params = $parts[1]; 88344653a53SAdrian Lang } 88444653a53SAdrian Lang 885fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 886fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 887fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 888fda14ffcSIzidor Matušov // (some things could be lost) 889fda14ffcSIzidor Matušov if($id === '') { 890fda14ffcSIzidor Matušov $id = $ID; 891fda14ffcSIzidor Matušov } 892fda14ffcSIzidor Matušov 8930339c872Sjan // default name is based on $id as given 8940339c872Sjan $default = $this->_simpleTitle($id); 895ad32e47eSAndreas Gohr 8960339c872Sjan // now first resolve and clean up the $id 89790bee600Slisps resolve_pageid(getNS($ID), $id, $exists, $this->date_at, true); 898fda14ffcSIzidor Matušov 89959bc3b48SGerrit Uitslag $link = array(); 900fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 9010e1c636eSandi if(!$isImage) { 9020e1c636eSandi if($exists) { 903ba11bd29Sandi $class = 'wikilink1'; 9040cecf9d5Sandi } else { 905ba11bd29Sandi $class = 'wikilink2'; 90644a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 9070cecf9d5Sandi } 9080cecf9d5Sandi } else { 909ba11bd29Sandi $class = 'media'; 9100cecf9d5Sandi } 9110cecf9d5Sandi 912a1685bedSandi //keep hash anchor 9136d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 914943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 915a1685bedSandi 916ba11bd29Sandi //prepare for formating 917ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 918ba11bd29Sandi $link['style'] = ''; 919ba11bd29Sandi $link['pre'] = ''; 920ba11bd29Sandi $link['suf'] = ''; 92140eb54bbSjan // highlight link to current page 922c4dda6afSAnika Henke if($id == $INFO['id']) { 92392795d04Sandi $link['pre'] = '<span class="curid">'; 92492795d04Sandi $link['suf'] = '</span>'; 92540eb54bbSjan } 9265e163278SAndreas Gohr $link['more'] = ''; 927ba11bd29Sandi $link['class'] = $class; 9285c2eed9aSlisps if($this->date_at) { 929912a6d48SPhy $params = $params.'&at='.rawurlencode($this->date_at); 9305c2eed9aSlisps } 93144653a53SAdrian Lang $link['url'] = wl($id, $params); 932ba11bd29Sandi $link['name'] = $name; 933ba11bd29Sandi $link['title'] = $id; 934723d78dbSandi //add search string 935723d78dbSandi if($search) { 936546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 937546d3a99SAndreas Gohr if(is_array($search)) { 938546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 939546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 940546d3a99SAndreas Gohr } else { 941546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 942546d3a99SAndreas Gohr } 943723d78dbSandi } 944723d78dbSandi 945a1685bedSandi //keep hash 946a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 947a1685bedSandi 948ba11bd29Sandi //output formatted 949cffcc403Sandi if($returnonly) { 950cffcc403Sandi return $this->_formatLink($link); 951cffcc403Sandi } else { 952a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9530cecf9d5Sandi } 954cffcc403Sandi } 9550cecf9d5Sandi 9563dd5c225SAndreas Gohr /** 9573dd5c225SAndreas Gohr * Render an external link 9583dd5c225SAndreas Gohr * 9593dd5c225SAndreas Gohr * @param string $url full URL with scheme 9603dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 961122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 9620c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 9633dd5c225SAndreas Gohr */ 964122f2d46SAndreas Böhler function externallink($url, $name = null, $returnonly = false) { 965b625487dSandi global $conf; 9660cecf9d5Sandi 967433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 9686f0c5dbfSandi 969b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 970b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 971b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 972b52b1596SAndreas Gohr $scheme = strtolower($scheme); 973b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 974b52b1596SAndreas Gohr 975b52b1596SAndreas Gohr // is there still an URL? 976b52b1596SAndreas Gohr if(!$url) { 97749cef4fdSAndreas Böhler if($returnonly) { 97849cef4fdSAndreas Böhler return $name; 97949cef4fdSAndreas Böhler } else { 980b52b1596SAndreas Gohr $this->doc .= $name; 98149cef4fdSAndreas Böhler } 982b52b1596SAndreas Gohr return; 983b52b1596SAndreas Gohr } 984b52b1596SAndreas Gohr 985b52b1596SAndreas Gohr // set class 9860cecf9d5Sandi if(!$isImage) { 987b625487dSandi $class = 'urlextern'; 9880cecf9d5Sandi } else { 989b625487dSandi $class = 'media'; 9900cecf9d5Sandi } 9910cecf9d5Sandi 992b625487dSandi //prepare for formating 99359bc3b48SGerrit Uitslag $link = array(); 994b625487dSandi $link['target'] = $conf['target']['extern']; 995b625487dSandi $link['style'] = ''; 996b625487dSandi $link['pre'] = ''; 997b625487dSandi $link['suf'] = ''; 9985e163278SAndreas Gohr $link['more'] = ''; 999b625487dSandi $link['class'] = $class; 1000b625487dSandi $link['url'] = $url; 1001914045f3SAndreas Gohr $link['rel'] = ''; 1002e1c10e4dSchris 1003b625487dSandi $link['name'] = $name; 1004433bef32Sandi $link['title'] = $this->_xmlEntities($url); 1005*5ddd0bbbSStarArmy if($conf['relnofollow']) $link['rel'] .= ' ugc nofollow'; 1006914045f3SAndreas Gohr if($conf['target']['extern']) $link['rel'] .= ' noopener'; 10070cecf9d5Sandi 1008b625487dSandi //output formatted 1009122f2d46SAndreas Böhler if($returnonly) { 1010122f2d46SAndreas Böhler return $this->_formatLink($link); 1011122f2d46SAndreas Böhler } else { 1012a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10130cecf9d5Sandi } 1014122f2d46SAndreas Böhler } 10150cecf9d5Sandi 10160cecf9d5Sandi /** 10173dd5c225SAndreas Gohr * Render an interwiki link 10183dd5c225SAndreas Gohr * 10193dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 10203dd5c225SAndreas Gohr * 10213dd5c225SAndreas Gohr * @param string $match original link - probably not much use 10223dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 10233dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 10243dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 1025122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10260c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10270cecf9d5Sandi */ 1028122f2d46SAndreas Böhler function interwikilink($match, $name = null, $wikiName, $wikiUri, $returnonly = false) { 1029b625487dSandi global $conf; 10300cecf9d5Sandi 103197a3e4e3Sandi $link = array(); 103297a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 103397a3e4e3Sandi $link['pre'] = ''; 103497a3e4e3Sandi $link['suf'] = ''; 10355e163278SAndreas Gohr $link['more'] = ''; 1036433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 1037914045f3SAndreas Gohr $link['rel'] = ''; 10380cecf9d5Sandi 103997a3e4e3Sandi //get interwiki URL 10406496c33fSGerrit Uitslag $exists = null; 10416496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 10420cecf9d5Sandi 104397a3e4e3Sandi if(!$isImage) { 10449d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 10459d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 10461c2d1019SAndreas Gohr } else { 10471c2d1019SAndreas Gohr $link['class'] = 'media'; 104897a3e4e3Sandi } 10490cecf9d5Sandi 105097a3e4e3Sandi //do we stay at the same server? Use local target 10512345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 105297a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 105397a3e4e3Sandi } 10546496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 10556496c33fSGerrit Uitslag if($exists) { 10566496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 10576496c33fSGerrit Uitslag } else { 10586496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 1059914045f3SAndreas Gohr $link['rel'] .= ' nofollow'; 10606496c33fSGerrit Uitslag } 10616496c33fSGerrit Uitslag } 1062914045f3SAndreas Gohr if($conf['target']['interwiki']) $link['rel'] .= ' noopener'; 10630cecf9d5Sandi 106497a3e4e3Sandi $link['url'] = $url; 106597a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 106697a3e4e3Sandi 106797a3e4e3Sandi //output formatted 1068122f2d46SAndreas Böhler if($returnonly) { 1069122f2d46SAndreas Böhler return $this->_formatLink($link); 1070122f2d46SAndreas Böhler } else { 1071a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10720cecf9d5Sandi } 1073122f2d46SAndreas Böhler } 10740cecf9d5Sandi 10750cecf9d5Sandi /** 10763dd5c225SAndreas Gohr * Link to windows share 10773dd5c225SAndreas Gohr * 10783dd5c225SAndreas Gohr * @param string $url the link 10793dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1080122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 10810c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 10820cecf9d5Sandi */ 1083122f2d46SAndreas Böhler function windowssharelink($url, $name = null, $returnonly = false) { 10841d47afe1Sandi global $conf; 10853dd5c225SAndreas Gohr 10861d47afe1Sandi //simple setup 108759bc3b48SGerrit Uitslag $link = array(); 10881d47afe1Sandi $link['target'] = $conf['target']['windows']; 10891d47afe1Sandi $link['pre'] = ''; 10901d47afe1Sandi $link['suf'] = ''; 10911d47afe1Sandi $link['style'] = ''; 10920cecf9d5Sandi 1093433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 10940cecf9d5Sandi if(!$isImage) { 10951d47afe1Sandi $link['class'] = 'windows'; 10960cecf9d5Sandi } else { 10971d47afe1Sandi $link['class'] = 'media'; 10980cecf9d5Sandi } 10990cecf9d5Sandi 1100433bef32Sandi $link['title'] = $this->_xmlEntities($url); 11011d47afe1Sandi $url = str_replace('\\', '/', $url); 11021d47afe1Sandi $url = 'file:///'.$url; 11031d47afe1Sandi $link['url'] = $url; 11040cecf9d5Sandi 11051d47afe1Sandi //output formatted 1106122f2d46SAndreas Böhler if($returnonly) { 1107122f2d46SAndreas Böhler return $this->_formatLink($link); 1108122f2d46SAndreas Böhler } else { 1109a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11100cecf9d5Sandi } 1111122f2d46SAndreas Böhler } 11120cecf9d5Sandi 11133dd5c225SAndreas Gohr /** 11143dd5c225SAndreas Gohr * Render a linked E-Mail Address 11153dd5c225SAndreas Gohr * 11163dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 11173dd5c225SAndreas Gohr * 11183dd5c225SAndreas Gohr * @param string $address Email-Address 11193dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1120122f2d46SAndreas Böhler * @param bool $returnonly whether to return html or write to doc attribute 11210c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 11223dd5c225SAndreas Gohr */ 1123122f2d46SAndreas Böhler function emaillink($address, $name = null, $returnonly = false) { 112471352defSandi global $conf; 112571352defSandi //simple setup 112671352defSandi $link = array(); 112771352defSandi $link['target'] = ''; 112871352defSandi $link['pre'] = ''; 112971352defSandi $link['suf'] = ''; 113071352defSandi $link['style'] = ''; 113171352defSandi $link['more'] = ''; 11320cecf9d5Sandi 1133c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 11340cecf9d5Sandi if(!$isImage) { 1135be96545cSAnika Henke $link['class'] = 'mail'; 11360cecf9d5Sandi } else { 1137be96545cSAnika Henke $link['class'] = 'media'; 11380cecf9d5Sandi } 11390cecf9d5Sandi 114007738714SAndreas Gohr $address = $this->_xmlEntities($address); 114100a7b5adSEsther Brunner $address = obfuscate($address); 114200a7b5adSEsther Brunner $title = $address; 11438c128049SAndreas Gohr 114471352defSandi if(empty($name)) { 114500a7b5adSEsther Brunner $name = $address; 114671352defSandi } 11470cecf9d5Sandi 1148776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1149776b36ecSAndreas Gohr 1150776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 115171352defSandi $link['name'] = $name; 115271352defSandi $link['title'] = $title; 11530cecf9d5Sandi 115471352defSandi //output formatted 1155122f2d46SAndreas Böhler if($returnonly) { 1156122f2d46SAndreas Böhler return $this->_formatLink($link); 1157122f2d46SAndreas Böhler } else { 1158a2d649c4Sandi $this->doc .= $this->_formatLink($link); 11590cecf9d5Sandi } 1160122f2d46SAndreas Böhler } 11610cecf9d5Sandi 11623dd5c225SAndreas Gohr /** 11633dd5c225SAndreas Gohr * Render an internal media file 11643dd5c225SAndreas Gohr * 11653dd5c225SAndreas Gohr * @param string $src media ID 11663dd5c225SAndreas Gohr * @param string $title descriptive text 11673dd5c225SAndreas Gohr * @param string $align left|center|right 11683dd5c225SAndreas Gohr * @param int $width width of media in pixel 11693dd5c225SAndreas Gohr * @param int $height height of media in pixel 11703dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 11713dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 11723dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 11730c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 11743dd5c225SAndreas Gohr */ 11750ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 11763dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 117737e34a5eSandi global $ID; 11788f34cf3dSMichael Große if (strpos($src, '#') !== false) { 117991df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 11808f34cf3dSMichael Große } 1181cdb5e961Slisps resolve_mediaid(getNS($ID), $src, $exists, $this->date_at, true); 11820cecf9d5Sandi 1183d98d4540SBen Coburn $noLink = false; 11848acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1185b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 11863685f775Sandi 11873dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1188b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 118952dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src)), ($linking == 'direct')); 1190f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11912a2a2ba2SAnika Henke // don't link movies 119244881bd0Shenning.noren $noLink = true; 119355efc227SAndreas Gohr } else { 11942ca14335SEsther Brunner // add file icons 11959d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 11969d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 119752dc5eadSlisps $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache , 'rev'=>$this->_getLastMediaRevisionAt($src)), true); 119891328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 119955efc227SAndreas Gohr } 12003685f775Sandi 12018f34cf3dSMichael Große if (!empty($hash)) $link['url'] .= '#'.$hash; 120291df343aSAndreas Gohr 12036fe20453SGina Haeussge //markup non existing files 12044a24b459SKate Arzamastseva if(!$exists) { 12056fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 12064a24b459SKate Arzamastseva } 12076fe20453SGina Haeussge 12083685f775Sandi //output formatted 1209f50634f0SAnika Henke if($return) { 1210f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1211f50634f0SAnika Henke else return $this->_formatLink($link); 1212f50634f0SAnika Henke } else { 1213dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12142ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12150cecf9d5Sandi } 1216f50634f0SAnika Henke } 12170cecf9d5Sandi 12183dd5c225SAndreas Gohr /** 12193dd5c225SAndreas Gohr * Render an external media file 12203dd5c225SAndreas Gohr * 12213dd5c225SAndreas Gohr * @param string $src full media URL 12223dd5c225SAndreas Gohr * @param string $title descriptive text 12233dd5c225SAndreas Gohr * @param string $align left|center|right 12243dd5c225SAndreas Gohr * @param int $width width of media in pixel 12253dd5c225SAndreas Gohr * @param int $height height of media in pixel 12263dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 12273dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1228410ee62aSAnika Henke * @param bool $return return HTML instead of adding to $doc 12290c4c0281SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $return 12303dd5c225SAndreas Gohr */ 12310ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 1232410ee62aSAnika Henke $height = null, $cache = null, $linking = null, $return = false) { 12336efc45a2SDmitry Katsubo if(link_isinterwiki($src)){ 12346efc45a2SDmitry Katsubo list($shortcut, $reference) = explode('>', $src, 2); 12356efc45a2SDmitry Katsubo $exists = null; 12366efc45a2SDmitry Katsubo $src = $this->_resolveInterWiki($shortcut, $reference, $exists); 12376efc45a2SDmitry Katsubo } 123891df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1239d98d4540SBen Coburn $noLink = false; 12408acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1241b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1242b739ff0fSPierre Spring 1243b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 12443685f775Sandi 12453dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1246b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 12472ca14335SEsther Brunner // link only jpeg images 124844881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1249f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 12502a2a2ba2SAnika Henke // don't link movies 125144881bd0Shenning.noren $noLink = true; 12522ca14335SEsther Brunner } else { 12532ca14335SEsther Brunner // add file icons 125427bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 125527bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 12562ca14335SEsther Brunner } 12572ca14335SEsther Brunner 125891df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 125991df343aSAndreas Gohr 12603685f775Sandi //output formatted 1261410ee62aSAnika Henke if($return) { 1262410ee62aSAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1263410ee62aSAnika Henke else return $this->_formatLink($link); 1264410ee62aSAnika Henke } else { 1265dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 12662ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 12670cecf9d5Sandi } 1268410ee62aSAnika Henke } 12690cecf9d5Sandi 12704826ab45Sandi /** 12713db95becSAndreas Gohr * Renders an RSS feed 1272b625487dSandi * 12730c4c0281SGerrit Uitslag * @param string $url URL of the feed 12740c4c0281SGerrit Uitslag * @param array $params Finetuning of the output 12750c4c0281SGerrit Uitslag * 1276b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1277b625487dSandi */ 12783db95becSAndreas Gohr function rss($url, $params) { 1279b625487dSandi global $lang; 12803db95becSAndreas Gohr global $conf; 12813db95becSAndreas Gohr 12823db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 12833db95becSAndreas Gohr $feed = new FeedParser(); 128400077af8SAndreas Gohr $feed->set_feed_url($url); 1285b625487dSandi 1286b625487dSandi //disable warning while fetching 12873dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 12883dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 12893dd5c225SAndreas Gohr } 12903db95becSAndreas Gohr $rc = $feed->init(); 12913dd5c225SAndreas Gohr if(isset($elvl)) { 12923dd5c225SAndreas Gohr error_reporting($elvl); 12933dd5c225SAndreas Gohr } 1294b625487dSandi 129538c6f603SRobin H. Johnson if($params['nosort']) $feed->enable_order_by_date(false); 129638c6f603SRobin H. Johnson 12973db95becSAndreas Gohr //decide on start and end 12983db95becSAndreas Gohr if($params['reverse']) { 12993db95becSAndreas Gohr $mod = -1; 13003db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 13013db95becSAndreas Gohr $end = $start - ($params['max']); 1302b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 13033db95becSAndreas Gohr } else { 13043db95becSAndreas Gohr $mod = 1; 13053db95becSAndreas Gohr $start = 0; 13063db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1307d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 13083db95becSAndreas Gohr } 13093db95becSAndreas Gohr 1310a2d649c4Sandi $this->doc .= '<ul class="rss">'; 13113db95becSAndreas Gohr if($rc) { 13123db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 13131bde1582SAndreas Gohr $item = $feed->get_item($x); 13143db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1315d2ea3363SAndreas Gohr // support feeds without links 1316d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1317d2ea3363SAndreas Gohr if($lnkurl) { 1318793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1319793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 13203dd5c225SAndreas Gohr $this->externallink( 13213dd5c225SAndreas Gohr $item->get_permalink(), 13223dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 13233dd5c225SAndreas Gohr ); 1324d2ea3363SAndreas Gohr } else { 1325d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1326d2ea3363SAndreas Gohr } 13273db95becSAndreas Gohr if($params['author']) { 13281bde1582SAndreas Gohr $author = $item->get_author(0); 13291bde1582SAndreas Gohr if($author) { 13301bde1582SAndreas Gohr $name = $author->get_name(); 13311bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 1332163c2842SPhy if($name) $this->doc .= ' '.$lang['by'].' '.hsc($name); 13331bde1582SAndreas Gohr } 13343db95becSAndreas Gohr } 13353db95becSAndreas Gohr if($params['date']) { 13362e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 13373db95becSAndreas Gohr } 13381bde1582SAndreas Gohr if($params['details']) { 13393db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1340173dccb7STom N Harris if($conf['htmlok']) { 13411bde1582SAndreas Gohr $this->doc .= $item->get_description(); 13423db95becSAndreas Gohr } else { 13431bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 13443db95becSAndreas Gohr } 13453db95becSAndreas Gohr $this->doc .= '</div>'; 13463db95becSAndreas Gohr } 13473db95becSAndreas Gohr 13483db95becSAndreas Gohr $this->doc .= '</div></li>'; 1349b625487dSandi } 1350b625487dSandi } else { 13513db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1352a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1353b625487dSandi $this->externallink($url); 135445e147ccSAndreas Gohr if($conf['allowdebug']) { 135545e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 135645e147ccSAndreas Gohr } 13573db95becSAndreas Gohr $this->doc .= '</div></li>'; 1358b625487dSandi } 1359a2d649c4Sandi $this->doc .= '</ul>'; 1360b625487dSandi } 1361b625487dSandi 13623dd5c225SAndreas Gohr /** 13633dd5c225SAndreas Gohr * Start a table 13643dd5c225SAndreas Gohr * 13653dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 13663dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 13673dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13687d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 13693dd5c225SAndreas Gohr */ 13700c4c0281SGerrit Uitslag function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) { 1371b5742cedSPierre Spring // initialize the row counter used for classes 1372b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1373619736fdSAdrian Lang $class = 'table'; 13740c4c0281SGerrit Uitslag if($classes !== null) { 13752e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 13760c4c0281SGerrit Uitslag $class .= ' ' . $classes; 13770c4c0281SGerrit Uitslag } 1378619736fdSAdrian Lang if($pos !== null) { 137906917fceSMichael Große $hid = $this->_headerToLink($class, true); 1380ec57f119SLarsDW223 $data = array(); 1381ec57f119SLarsDW223 $data['target'] = 'table'; 1382ec57f119SLarsDW223 $data['name'] = ''; 1383ec57f119SLarsDW223 $data['hid'] = $hid; 1384ec57f119SLarsDW223 $class .= ' '.$this->startSectionEdit($pos, $data); 1385619736fdSAdrian Lang } 1386619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1387619736fdSAdrian Lang DOKU_LF; 13880cecf9d5Sandi } 13890cecf9d5Sandi 13903dd5c225SAndreas Gohr /** 13913dd5c225SAndreas Gohr * Close a table 13923dd5c225SAndreas Gohr * 13933dd5c225SAndreas Gohr * @param int $pos byte position in the original source 13943dd5c225SAndreas Gohr */ 1395619736fdSAdrian Lang function table_close($pos = null) { 1396a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1397619736fdSAdrian Lang if($pos !== null) { 139890df9a4dSAdrian Lang $this->finishSectionEdit($pos); 13990cecf9d5Sandi } 1400619736fdSAdrian Lang } 14010cecf9d5Sandi 14023dd5c225SAndreas Gohr /** 14033dd5c225SAndreas Gohr * Open a table header 14043dd5c225SAndreas Gohr */ 1405f05a1cc5SGerrit Uitslag function tablethead_open() { 1406f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1407f05a1cc5SGerrit Uitslag } 1408f05a1cc5SGerrit Uitslag 14093dd5c225SAndreas Gohr /** 14103dd5c225SAndreas Gohr * Close a table header 14113dd5c225SAndreas Gohr */ 1412f05a1cc5SGerrit Uitslag function tablethead_close() { 1413f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1414f05a1cc5SGerrit Uitslag } 1415f05a1cc5SGerrit Uitslag 14163dd5c225SAndreas Gohr /** 14175a93f869SAnika Henke * Open a table body 14185a93f869SAnika Henke */ 14195a93f869SAnika Henke function tabletbody_open() { 14205a93f869SAnika Henke $this->doc .= DOKU_TAB.'<tbody>'.DOKU_LF; 14215a93f869SAnika Henke } 14225a93f869SAnika Henke 14235a93f869SAnika Henke /** 14245a93f869SAnika Henke * Close a table body 14255a93f869SAnika Henke */ 14265a93f869SAnika Henke function tabletbody_close() { 14275a93f869SAnika Henke $this->doc .= DOKU_TAB.'</tbody>'.DOKU_LF; 14285a93f869SAnika Henke } 14295a93f869SAnika Henke 14305a93f869SAnika Henke /** 1431d2a99739SAndreas Gohr * Open a table footer 1432d2a99739SAndreas Gohr */ 1433d2a99739SAndreas Gohr function tabletfoot_open() { 143444f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'<tfoot>'.DOKU_LF; 1435d2a99739SAndreas Gohr } 1436d2a99739SAndreas Gohr 1437d2a99739SAndreas Gohr /** 1438d2a99739SAndreas Gohr * Close a table footer 1439d2a99739SAndreas Gohr */ 1440d2a99739SAndreas Gohr function tabletfoot_close() { 144144f5d1c1SAndreas Gohr $this->doc .= DOKU_TAB.'</tfoot>'.DOKU_LF; 1442d2a99739SAndreas Gohr } 1443d2a99739SAndreas Gohr 1444d2a99739SAndreas Gohr /** 14453dd5c225SAndreas Gohr * Open a table row 14460c4c0281SGerrit Uitslag * 14477d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14483dd5c225SAndreas Gohr */ 14490c4c0281SGerrit Uitslag function tablerow_open($classes = null) { 1450b5742cedSPierre Spring // initialize the cell counter used for classes 1451b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1452b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 14530c4c0281SGerrit Uitslag if($classes !== null) { 14542e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14550c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14560c4c0281SGerrit Uitslag } 1457b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 14580cecf9d5Sandi } 14590cecf9d5Sandi 14603dd5c225SAndreas Gohr /** 14613dd5c225SAndreas Gohr * Close a table row 14623dd5c225SAndreas Gohr */ 14630cecf9d5Sandi function tablerow_close() { 1464a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 14650cecf9d5Sandi } 14660cecf9d5Sandi 14673dd5c225SAndreas Gohr /** 14683dd5c225SAndreas Gohr * Open a table header cell 14693dd5c225SAndreas Gohr * 14703dd5c225SAndreas Gohr * @param int $colspan 14713dd5c225SAndreas Gohr * @param string $align left|center|right 14723dd5c225SAndreas Gohr * @param int $rowspan 14737d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 14743dd5c225SAndreas Gohr */ 14750c4c0281SGerrit Uitslag function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1476b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 14770cecf9d5Sandi if(!is_null($align)) { 1478b5742cedSPierre Spring $class .= ' '.$align.'align'; 14790cecf9d5Sandi } 14800c4c0281SGerrit Uitslag if($classes !== null) { 14812e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 14820c4c0281SGerrit Uitslag $class .= ' ' . $classes; 14830c4c0281SGerrit Uitslag } 1484b5742cedSPierre Spring $class .= '"'; 1485b5742cedSPierre Spring $this->doc .= '<th '.$class; 14860cecf9d5Sandi if($colspan > 1) { 1487a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1488a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 14890cecf9d5Sandi } 149025b97867Shakan.sandell if($rowspan > 1) { 149125b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 149225b97867Shakan.sandell } 1493a2d649c4Sandi $this->doc .= '>'; 14940cecf9d5Sandi } 14950cecf9d5Sandi 14963dd5c225SAndreas Gohr /** 14973dd5c225SAndreas Gohr * Close a table header cell 14983dd5c225SAndreas Gohr */ 14990cecf9d5Sandi function tableheader_close() { 1500a2d649c4Sandi $this->doc .= '</th>'; 15010cecf9d5Sandi } 15020cecf9d5Sandi 15033dd5c225SAndreas Gohr /** 15043dd5c225SAndreas Gohr * Open a table cell 15053dd5c225SAndreas Gohr * 15063dd5c225SAndreas Gohr * @param int $colspan 15073dd5c225SAndreas Gohr * @param string $align left|center|right 15083dd5c225SAndreas Gohr * @param int $rowspan 15097d769f75SAndreas Gohr * @param string|string[] $classes css classes - have to be valid, do not pass unfiltered user input 15103dd5c225SAndreas Gohr */ 15110c4c0281SGerrit Uitslag function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) { 1512b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 15130cecf9d5Sandi if(!is_null($align)) { 1514b5742cedSPierre Spring $class .= ' '.$align.'align'; 15150cecf9d5Sandi } 15160c4c0281SGerrit Uitslag if($classes !== null) { 15172e0ebe60SAndreas Gohr if(is_array($classes)) $classes = join(' ', $classes); 15180c4c0281SGerrit Uitslag $class .= ' ' . $classes; 15190c4c0281SGerrit Uitslag } 1520b5742cedSPierre Spring $class .= '"'; 1521b5742cedSPierre Spring $this->doc .= '<td '.$class; 15220cecf9d5Sandi if($colspan > 1) { 1523a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1524a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 15250cecf9d5Sandi } 152625b97867Shakan.sandell if($rowspan > 1) { 152725b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 152825b97867Shakan.sandell } 1529a2d649c4Sandi $this->doc .= '>'; 15300cecf9d5Sandi } 15310cecf9d5Sandi 15323dd5c225SAndreas Gohr /** 15333dd5c225SAndreas Gohr * Close a table cell 15343dd5c225SAndreas Gohr */ 15350cecf9d5Sandi function tablecell_close() { 1536a2d649c4Sandi $this->doc .= '</td>'; 15370cecf9d5Sandi } 15380cecf9d5Sandi 1539cea664bdSLarsDW223 /** 1540cea664bdSLarsDW223 * Returns the current header level. 1541cea664bdSLarsDW223 * (required e.g. by the filelist plugin) 1542cea664bdSLarsDW223 * 1543cea664bdSLarsDW223 * @return int The current header level 1544cea664bdSLarsDW223 */ 1545cea664bdSLarsDW223 function getLastlevel() { 1546cea664bdSLarsDW223 return $this->lastlevel; 1547cea664bdSLarsDW223 } 1548cea664bdSLarsDW223 15493dd5c225SAndreas Gohr #region Utility functions 15500cecf9d5Sandi 1551ba11bd29Sandi /** 15523fd0b676Sandi * Build a link 15533fd0b676Sandi * 15543fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1555ba11bd29Sandi * 15560c4c0281SGerrit Uitslag * @param array $link attributes of a link 15570c4c0281SGerrit Uitslag * @return string 15580c4c0281SGerrit Uitslag * 1559ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1560ba11bd29Sandi */ 1561433bef32Sandi function _formatLink($link) { 1562ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1563ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1564ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1565ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1566ba11bd29Sandi } 1567ba11bd29Sandi //remove double encodings in titles 1568ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1569ba11bd29Sandi 1570453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1571453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1572453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1573453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1574453493f2SAndreas Gohr 1575ba11bd29Sandi $ret = ''; 1576ba11bd29Sandi $ret .= $link['pre']; 1577ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1578bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1579bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1580bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1581bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 1582914045f3SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.trim($link['rel']).'"'; 1583bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1584ba11bd29Sandi $ret .= '>'; 1585ba11bd29Sandi $ret .= $link['name']; 1586ba11bd29Sandi $ret .= '</a>'; 1587ba11bd29Sandi $ret .= $link['suf']; 1588ba11bd29Sandi return $ret; 1589ba11bd29Sandi } 1590ba11bd29Sandi 1591ba11bd29Sandi /** 15923fd0b676Sandi * Renders internal and external media 15933fd0b676Sandi * 15943fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 15953dd5c225SAndreas Gohr * @param string $src media ID 15963dd5c225SAndreas Gohr * @param string $title descriptive text 15973dd5c225SAndreas Gohr * @param string $align left|center|right 15983dd5c225SAndreas Gohr * @param int $width width of media in pixel 15993dd5c225SAndreas Gohr * @param int $height height of media in pixel 16003dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 16013dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 16023dd5c225SAndreas Gohr * @return string 16033fd0b676Sandi */ 16040ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 16050ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 16063fd0b676Sandi 16073fd0b676Sandi $ret = ''; 16083fd0b676Sandi 16093dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 16103fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1611b739ff0fSPierre Spring // first get the $title 1612b739ff0fSPierre Spring if(!is_null($title)) { 1613b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1614b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1615b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1616b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 161767f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1618b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 16193dd5c225SAndreas Gohr if(!empty($cap)) { 1620b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1621b739ff0fSPierre Spring } 1622b739ff0fSPierre Spring } 1623b739ff0fSPierre Spring if(!$render) { 1624b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1625b739ff0fSPierre Spring // return the title of the picture 1626b739ff0fSPierre Spring if(!$title) { 1627b739ff0fSPierre Spring // just show the sourcename 16283009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1629b739ff0fSPierre Spring } 1630b739ff0fSPierre Spring return $title; 1631b739ff0fSPierre Spring } 16323fd0b676Sandi //add image tag 163352dc5eadSlisps $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache, 'rev'=>$this->_getLastMediaRevisionAt($src))).'"'; 16343fd0b676Sandi $ret .= ' class="media'.$align.'"'; 16353fd0b676Sandi 1636b739ff0fSPierre Spring if($title) { 1637b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1638b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 16393fd0b676Sandi } else { 16403fd0b676Sandi $ret .= ' alt=""'; 16413fd0b676Sandi } 16423fd0b676Sandi 16433fd0b676Sandi if(!is_null($width)) 16443fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 16453fd0b676Sandi 16463fd0b676Sandi if(!is_null($height)) 16473fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 16483fd0b676Sandi 16493fd0b676Sandi $ret .= ' />'; 16503fd0b676Sandi 165117954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 16522a2a2ba2SAnika Henke // first get the $title 165317954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 16542a2a2ba2SAnika Henke if(!$render) { 165517954bb5SAnika Henke // if the file is not supposed to be rendered 165617954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 165717954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 16582a2a2ba2SAnika Henke } 16592a2a2ba2SAnika Henke 16602a2a2ba2SAnika Henke $att = array(); 16612a2a2ba2SAnika Henke $att['class'] = "media$align"; 166217954bb5SAnika Henke if($title) { 166317954bb5SAnika Henke $att['title'] = $title; 166417954bb5SAnika Henke } 16652a2a2ba2SAnika Henke 166617954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 166717954bb5SAnika Henke //add video 166879e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1669b44a5dceSAnika Henke } 167017954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1671b44a5dceSAnika Henke //add audio 1672b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 167317954bb5SAnika Henke } 1674b44a5dceSAnika Henke 16753fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 16761c882ba8SAndreas Gohr if(!$render) { 16771c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 16781c882ba8SAndreas Gohr // return the title of the flash 16791c882ba8SAndreas Gohr if(!$title) { 16801c882ba8SAndreas Gohr // just show the sourcename 16813009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 16821c882ba8SAndreas Gohr } 168307bf32b2SAndreas Gohr return $this->_xmlEntities($title); 16841c882ba8SAndreas Gohr } 16851c882ba8SAndreas Gohr 168607bf32b2SAndreas Gohr $att = array(); 168707bf32b2SAndreas Gohr $att['class'] = "media$align"; 168807bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 168907bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 16903dd5c225SAndreas Gohr $ret .= html_flashobject( 16913dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 169207bf32b2SAndreas Gohr array('quality' => 'high'), 169307bf32b2SAndreas Gohr null, 169407bf32b2SAndreas Gohr $att, 16953dd5c225SAndreas Gohr $this->_xmlEntities($title) 16963dd5c225SAndreas Gohr ); 16970f428d7dSAndreas Gohr } elseif($title) { 16983fd0b676Sandi // well at least we have a title to display 16993fd0b676Sandi $ret .= $this->_xmlEntities($title); 17003fd0b676Sandi } else { 17015291ca3aSAndreas Gohr // just show the sourcename 17023009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 17033fd0b676Sandi } 17043fd0b676Sandi 17053fd0b676Sandi return $ret; 17063fd0b676Sandi } 17073fd0b676Sandi 17083dd5c225SAndreas Gohr /** 17093dd5c225SAndreas Gohr * Escape string for output 17103dd5c225SAndreas Gohr * 17113dd5c225SAndreas Gohr * @param $string 17123dd5c225SAndreas Gohr * @return string 17133dd5c225SAndreas Gohr */ 1714433bef32Sandi function _xmlEntities($string) { 1715de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 17160cecf9d5Sandi } 17170cecf9d5Sandi 17188a831f2bSAndreas Gohr /** 17198a831f2bSAndreas Gohr * Creates a linkid from a headline 1720c5a8fd96SAndreas Gohr * 17213dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1722c5a8fd96SAndreas Gohr * @param string $title The headline title 1723c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 17243dd5c225SAndreas Gohr * @return string 17258a831f2bSAndreas Gohr */ 1726c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1727c5a8fd96SAndreas Gohr if($create) { 17284ceab83fSAndreas Gohr return sectionID($title, $this->headers); 17294ceab83fSAndreas Gohr } else { 1730443d207bSAndreas Gohr $check = false; 1731443d207bSAndreas Gohr return sectionID($title, $check); 1732c5a8fd96SAndreas Gohr } 17330cecf9d5Sandi } 17340cecf9d5Sandi 1735af587fa8Sandi /** 17363fd0b676Sandi * Construct a title and handle images in titles 17373fd0b676Sandi * 17380b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 17393dd5c225SAndreas Gohr * @param string|array $title either string title or media array 17403dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 17413dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 17423dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 17433dd5c225SAndreas Gohr * @param string $linktype content|navigation 17443dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 17453fd0b676Sandi */ 17460ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 174744881bd0Shenning.noren $isImage = false; 174829657f9eSAndreas Gohr if(is_array($title)) { 174929657f9eSAndreas Gohr $isImage = true; 175029657f9eSAndreas Gohr return $this->_imageTitle($title); 175129657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1752fe9ec250SChris Smith if(useHeading($linktype) && $id) { 175367c15eceSMichael Hamann $heading = p_get_first_heading($id); 1754f515db7fSAndreas Gohr if(!blank($heading)) { 1755433bef32Sandi return $this->_xmlEntities($heading); 1756bb0a59d4Sjan } 1757bb0a59d4Sjan } 1758433bef32Sandi return $this->_xmlEntities($default); 175968c26e6dSMichael Klier } else { 176068c26e6dSMichael Klier return $this->_xmlEntities($title); 17610cecf9d5Sandi } 17620cecf9d5Sandi } 17630cecf9d5Sandi 17640cecf9d5Sandi /** 17653dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 17663fd0b676Sandi * 17673fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1768e0c26282SGerrit Uitslag * @param array $img 17693dd5c225SAndreas Gohr * @return string HTML img tag or similar 17700cecf9d5Sandi */ 1771433bef32Sandi function _imageTitle($img) { 1772d9baf1a7SKazutaka Miyasaka global $ID; 1773d9baf1a7SKazutaka Miyasaka 1774d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1775d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 17763dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1777d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1778cdb5e961Slisps resolve_mediaid(getNS($ID), $img['src'], $exists ,$this->date_at, true); 1779d9baf1a7SKazutaka Miyasaka } 1780d9baf1a7SKazutaka Miyasaka 17813dd5c225SAndreas Gohr return $this->_media( 17823dd5c225SAndreas Gohr $img['src'], 17834826ab45Sandi $img['title'], 17844826ab45Sandi $img['align'], 17854826ab45Sandi $img['width'], 17864826ab45Sandi $img['height'], 17873dd5c225SAndreas Gohr $img['cache'] 17883dd5c225SAndreas Gohr ); 17890cecf9d5Sandi } 1790b739ff0fSPierre Spring 1791b739ff0fSPierre Spring /** 17923dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 17933dd5c225SAndreas Gohr * 17943dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1795b739ff0fSPierre Spring * 1796b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 17973dd5c225SAndreas Gohr * @param string $src media ID 17983dd5c225SAndreas Gohr * @param string $title descriptive text 17993dd5c225SAndreas Gohr * @param string $align left|center|right 18003dd5c225SAndreas Gohr * @param int $width width of media in pixel 18013dd5c225SAndreas Gohr * @param int $height height of media in pixel 18023dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 18033dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 18043dd5c225SAndreas Gohr * @return array associative array with link config 1805b739ff0fSPierre Spring */ 1806d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1807b739ff0fSPierre Spring global $conf; 1808b739ff0fSPierre Spring 1809b739ff0fSPierre Spring $link = array(); 1810b739ff0fSPierre Spring $link['class'] = 'media'; 1811b739ff0fSPierre Spring $link['style'] = ''; 1812b739ff0fSPierre Spring $link['pre'] = ''; 1813b739ff0fSPierre Spring $link['suf'] = ''; 1814b739ff0fSPierre Spring $link['more'] = ''; 1815b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1816bc3d2252SAndreas Gohr if($conf['target']['media']) $link['rel'] = 'noopener'; 1817b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1818b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1819b739ff0fSPierre Spring 1820b739ff0fSPierre Spring return $link; 1821b739ff0fSPierre Spring } 182291459163SAnika Henke 18232a2a2ba2SAnika Henke /** 18242a2a2ba2SAnika Henke * Embed video(s) in HTML 18252a2a2ba2SAnika Henke * 18262a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 18270877a1f1SSchplurtz le Déboulonné * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> 18282a2a2ba2SAnika Henke * 18292a2a2ba2SAnika Henke * @param string $src - ID of video to embed 18302a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 18312a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 18322a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1833f50634f0SAnika Henke * @return string 18342a2a2ba2SAnika Henke */ 183579e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 18362a2a2ba2SAnika Henke // prepare width and height 18372a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 18382a2a2ba2SAnika Henke $atts['width'] = (int) $width; 18392a2a2ba2SAnika Henke $atts['height'] = (int) $height; 18402a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 18412a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 18422a2a2ba2SAnika Henke 1843410ee62aSAnika Henke $posterUrl = ''; 1844410ee62aSAnika Henke $files = array(); 18450877a1f1SSchplurtz le Déboulonné $tracks = array(); 1846410ee62aSAnika Henke $isExternal = media_isexternal($src); 1847410ee62aSAnika Henke 1848410ee62aSAnika Henke if ($isExternal) { 1849410ee62aSAnika Henke // take direct source for external files 1850702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1851410ee62aSAnika Henke $files[$srcMime] = $src; 1852410ee62aSAnika Henke } else { 18533d7a9e0aSAnika Henke // prepare alternative formats 18543d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 1855410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 185659bc3b48SGerrit Uitslag $poster = media_alternativefiles($src, array('jpg', 'png')); 18570877a1f1SSchplurtz le Déboulonné $tracks = media_trackfiles($src); 185899f943f6SAnika Henke if(!empty($poster)) { 18592d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 186099f943f6SAnika Henke } 1861410ee62aSAnika Henke } 18622a2a2ba2SAnika Henke 1863f50634f0SAnika Henke $out = ''; 186479e53fe5SAnika Henke // open video tag 1865f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 18663641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1867f50634f0SAnika Henke $out .= '>'.NL; 18683641199aSAnika Henke $fallback = ''; 186979e53fe5SAnika Henke 187079e53fe5SAnika Henke // output source for each alternative video format 1871410ee62aSAnika Henke foreach($files as $mime => $file) { 1872410ee62aSAnika Henke if ($isExternal) { 1873410ee62aSAnika Henke $url = $file; 1874410ee62aSAnika Henke $linkType = 'externalmedia'; 1875410ee62aSAnika Henke } else { 18762d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1877410ee62aSAnika Henke $linkType = 'internalmedia'; 1878410ee62aSAnika Henke } 187917954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 18803d7a9e0aSAnika Henke 1881f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 188279e53fe5SAnika Henke // alternative content (just a link to the file) 1883410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 18843d7a9e0aSAnika Henke } 18852a2a2ba2SAnika Henke 18860877a1f1SSchplurtz le Déboulonné // output each track if any 18870877a1f1SSchplurtz le Déboulonné foreach( $tracks as $trackid => $info ) { 188823c61bbeSSchplurtz le Déboulonné list( $kind, $srclang ) = array_map( 'hsc', $info ); 188923c61bbeSSchplurtz le Déboulonné $out .= "<track kind=\"$kind\" srclang=\"$srclang\" "; 189023c61bbeSSchplurtz le Déboulonné $out .= "label=\"$srclang\" "; 18910877a1f1SSchplurtz le Déboulonné $out .= 'src="'.ml($trackid, '', true).'">'.NL; 18920877a1f1SSchplurtz le Déboulonné } 18930877a1f1SSchplurtz le Déboulonné 18942a2a2ba2SAnika Henke // finish 18953641199aSAnika Henke $out .= $fallback; 1896f50634f0SAnika Henke $out .= '</video>'.NL; 1897f50634f0SAnika Henke return $out; 18982a2a2ba2SAnika Henke } 18992a2a2ba2SAnika Henke 1900b44a5dceSAnika Henke /** 1901b44a5dceSAnika Henke * Embed audio in HTML 1902b44a5dceSAnika Henke * 1903b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1904b44a5dceSAnika Henke * 1905b44a5dceSAnika Henke * @param string $src - ID of audio to embed 19066d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1907f50634f0SAnika Henke * @return string 1908b44a5dceSAnika Henke */ 190959bc3b48SGerrit Uitslag function _audio($src, $atts = array()) { 1910702e97d3SAnika Henke $files = array(); 1911410ee62aSAnika Henke $isExternal = media_isexternal($src); 1912b44a5dceSAnika Henke 1913410ee62aSAnika Henke if ($isExternal) { 1914410ee62aSAnika Henke // take direct source for external files 1915702e97d3SAnika Henke list(/*ext*/, $srcMime) = mimetype($src); 1916410ee62aSAnika Henke $files[$srcMime] = $src; 1917410ee62aSAnika Henke } else { 1918b44a5dceSAnika Henke // prepare alternative formats 1919b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1920410ee62aSAnika Henke $files = media_alternativefiles($src, $extensions); 1921410ee62aSAnika Henke } 1922b44a5dceSAnika Henke 1923f50634f0SAnika Henke $out = ''; 1924b44a5dceSAnika Henke // open audio tag 1925f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 19263641199aSAnika Henke $fallback = ''; 1927b44a5dceSAnika Henke 1928b44a5dceSAnika Henke // output source for each alternative audio format 1929410ee62aSAnika Henke foreach($files as $mime => $file) { 1930410ee62aSAnika Henke if ($isExternal) { 1931410ee62aSAnika Henke $url = $file; 1932410ee62aSAnika Henke $linkType = 'externalmedia'; 1933410ee62aSAnika Henke } else { 19342d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 1935410ee62aSAnika Henke $linkType = 'internalmedia'; 1936410ee62aSAnika Henke } 193717954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1938b44a5dceSAnika Henke 1939f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1940b44a5dceSAnika Henke // alternative content (just a link to the file) 1941410ee62aSAnika Henke $fallback .= $this->$linkType($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1942b44a5dceSAnika Henke } 1943b44a5dceSAnika Henke 1944b44a5dceSAnika Henke // finish 19453641199aSAnika Henke $out .= $fallback; 1946f50634f0SAnika Henke $out .= '</audio>'.NL; 1947f50634f0SAnika Henke return $out; 1948b44a5dceSAnika Henke } 1949b44a5dceSAnika Henke 19505c2eed9aSlisps /** 195152dc5eadSlisps * _getLastMediaRevisionAt is a helperfunction to internalmedia() and _media() 19525c2eed9aSlisps * which returns an existing media revision less or equal to rev or date_at 19535c2eed9aSlisps * 19545c2eed9aSlisps * @author lisps 19555c2eed9aSlisps * @param string $media_id 19565c2eed9aSlisps * @access protected 19575c2eed9aSlisps * @return string revision ('' for current) 19585c2eed9aSlisps */ 195952dc5eadSlisps function _getLastMediaRevisionAt($media_id){ 196052dc5eadSlisps if(!$this->date_at || media_isexternal($media_id)) return ''; 196178b874e6Slisps $pagelog = new MediaChangeLog($media_id); 196278b874e6Slisps return $pagelog->getLastRevisionAt($this->date_at); 19635c2eed9aSlisps } 19645c2eed9aSlisps 19653dd5c225SAndreas Gohr #endregion 19660cecf9d5Sandi} 19670cecf9d5Sandi 1968e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1969