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/** 21*3dd5c225SAndreas Gohr * The XHTML Renderer 22*3dd5c225SAndreas Gohr * 23*3dd5c225SAndreas Gohr * This is DokuWiki's main renderer used to display page content in the wiki 240cecf9d5Sandi */ 25ac83b9d8Sandiclass Doku_Renderer_xhtml extends Doku_Renderer { 26*3dd5c225SAndreas Gohr /** @var array store the table of contents */ 27*3dd5c225SAndreas Gohr public $toc = array(); 280cecf9d5Sandi 29*3dd5c225SAndreas Gohr /** @var array A stack of section edit data */ 30*3dd5c225SAndreas Gohr protected $sectionedits = array(); 31c5a8fd96SAndreas Gohr 32*3dd5c225SAndreas Gohr /** @var int last section edit id, used by startSectionEdit */ 33*3dd5c225SAndreas Gohr protected $lastsecid = 0; 340cecf9d5Sandi 35*3dd5c225SAndreas Gohr /** @var array the list of headers used to create unique link ids */ 36*3dd5c225SAndreas Gohr protected $headers = array(); 37*3dd5c225SAndreas Gohr 3816ec3e37SAndreas Gohr /** @var array a list of footnotes, list starts at 1! */ 39*3dd5c225SAndreas Gohr protected $footnotes = array(); 407764a90aSandi 41*3dd5c225SAndreas Gohr /** @var int current section level */ 42*3dd5c225SAndreas Gohr protected $lastlevel = 0; 43*3dd5c225SAndreas Gohr /** @var array section node tracker */ 44*3dd5c225SAndreas Gohr protected $node = array(0, 0, 0, 0, 0); 45*3dd5c225SAndreas Gohr 46*3dd5c225SAndreas Gohr /** @var string temporary $doc store */ 47*3dd5c225SAndreas Gohr protected $store = ''; 48*3dd5c225SAndreas Gohr 49*3dd5c225SAndreas Gohr /** @var array global counter, for table classes etc. */ 50*3dd5c225SAndreas Gohr protected $_counter = array(); // 51*3dd5c225SAndreas Gohr 52*3dd5c225SAndreas Gohr /** @var int counts the code and file blocks, used to provide download links */ 53*3dd5c225SAndreas Gohr protected $_codeblock = 0; 54*3dd5c225SAndreas Gohr 55*3dd5c225SAndreas Gohr /** @var array list of allowed URL schemes */ 56*3dd5c225SAndreas Gohr protected $schemes = null; 57b5742cedSPierre Spring 5890df9a4dSAdrian Lang /** 5990df9a4dSAdrian Lang * Register a new edit section range 6090df9a4dSAdrian Lang * 6190df9a4dSAdrian Lang * @param $type string The section type identifier 6290df9a4dSAdrian Lang * @param $title string The section title 6390df9a4dSAdrian Lang * @param $start int The byte position for the edit start 6490df9a4dSAdrian Lang * @return string A marker class for the starting HTML element 6590df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 6690df9a4dSAdrian Lang */ 673f9e3215SAdrian Lang public function startSectionEdit($start, $type, $title = null) { 68b04a190dSMichael Hamann $this->sectionedits[] = array(++$this->lastsecid, $start, $type, $title); 69b04a190dSMichael Hamann return 'sectionedit'.$this->lastsecid; 7090df9a4dSAdrian Lang } 7190df9a4dSAdrian Lang 7290df9a4dSAdrian Lang /** 7390df9a4dSAdrian Lang * Finish an edit section range 7490df9a4dSAdrian Lang * 75d9e36cbeSAdrian Lang * @param $end int The byte position for the edit end; null for the rest of 76c404cb3bSMatt Perry * the page 7790df9a4dSAdrian Lang * @author Adrian Lang <lang@cosmocode.de> 7890df9a4dSAdrian Lang */ 793f9e3215SAdrian Lang public function finishSectionEdit($end = null) { 8090df9a4dSAdrian Lang list($id, $start, $type, $title) = array_pop($this->sectionedits); 81d9e36cbeSAdrian Lang if(!is_null($end) && $end <= $start) { 8200c13053SAdrian Lang return; 8300c13053SAdrian Lang } 8440868f2fSAdrian Lang $this->doc .= "<!-- EDIT$id ".strtoupper($type).' '; 8540868f2fSAdrian Lang if(!is_null($title)) { 8640868f2fSAdrian Lang $this->doc .= '"'.str_replace('"', '', $title).'" '; 8740868f2fSAdrian Lang } 88d9e36cbeSAdrian Lang $this->doc .= "[$start-".(is_null($end) ? '' : $end).'] -->'; 8990df9a4dSAdrian Lang } 9090df9a4dSAdrian Lang 91*3dd5c225SAndreas Gohr /** 92*3dd5c225SAndreas Gohr * Returns the format produced by this renderer. 93*3dd5c225SAndreas Gohr * 94*3dd5c225SAndreas Gohr * @return string always 'xhtml' 95*3dd5c225SAndreas Gohr */ 965f70445dSAndreas Gohr function getFormat() { 975f70445dSAndreas Gohr return 'xhtml'; 985f70445dSAndreas Gohr } 995f70445dSAndreas Gohr 100*3dd5c225SAndreas Gohr /** 101*3dd5c225SAndreas Gohr * Initialize the document 102*3dd5c225SAndreas Gohr */ 1030cecf9d5Sandi function document_start() { 104c5a8fd96SAndreas Gohr //reset some internals 105c5a8fd96SAndreas Gohr $this->toc = array(); 106c5a8fd96SAndreas Gohr $this->headers = array(); 1070cecf9d5Sandi } 1080cecf9d5Sandi 109*3dd5c225SAndreas Gohr /** 110*3dd5c225SAndreas Gohr * Finalize the document 111*3dd5c225SAndreas Gohr */ 1120cecf9d5Sandi function document_end() { 11390df9a4dSAdrian Lang // Finish open section edits. 11490df9a4dSAdrian Lang while(count($this->sectionedits) > 0) { 11590df9a4dSAdrian Lang if($this->sectionedits[count($this->sectionedits) - 1][1] <= 1) { 11690df9a4dSAdrian Lang // If there is only one section, do not write a section edit 11790df9a4dSAdrian Lang // marker. 11890df9a4dSAdrian Lang array_pop($this->sectionedits); 11990df9a4dSAdrian Lang } else { 120d9e36cbeSAdrian Lang $this->finishSectionEdit(); 12190df9a4dSAdrian Lang } 12290df9a4dSAdrian Lang } 12390df9a4dSAdrian Lang 1240cecf9d5Sandi if(count($this->footnotes) > 0) { 125a2d649c4Sandi $this->doc .= '<div class="footnotes">'.DOKU_LF; 126d74aace9Schris 12716ec3e37SAndreas Gohr foreach($this->footnotes as $id => $footnote) { 128d74aace9Schris // check its not a placeholder that indicates actual footnote text is elsewhere 129d74aace9Schris if(substr($footnote, 0, 5) != "@@FNT") { 130d74aace9Schris 131d74aace9Schris // open the footnote and set the anchor and backlink 132d74aace9Schris $this->doc .= '<div class="fn">'; 13316cc7ed7SAnika Henke $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" class="fn_bot">'; 13429bfcd16SAndreas Gohr $this->doc .= $id.')</a></sup> '.DOKU_LF; 135d74aace9Schris 136d74aace9Schris // get any other footnotes that use the same markup 137d74aace9Schris $alt = array_keys($this->footnotes, "@@FNT$id"); 138d74aace9Schris 139d74aace9Schris if(count($alt)) { 140d74aace9Schris foreach($alt as $ref) { 141d74aace9Schris // set anchor and backlink for the other footnotes 14216ec3e37SAndreas Gohr $this->doc .= ', <sup><a href="#fnt__'.($ref).'" id="fn__'.($ref).'" class="fn_bot">'; 14316ec3e37SAndreas Gohr $this->doc .= ($ref).')</a></sup> '.DOKU_LF; 144d74aace9Schris } 145d74aace9Schris } 146d74aace9Schris 147d74aace9Schris // add footnote markup and close this footnote 148a2d649c4Sandi $this->doc .= $footnote; 149d74aace9Schris $this->doc .= '</div>'.DOKU_LF; 150d74aace9Schris } 1510cecf9d5Sandi } 152a2d649c4Sandi $this->doc .= '</div>'.DOKU_LF; 1530cecf9d5Sandi } 154c5a8fd96SAndreas Gohr 155b8595a66SAndreas Gohr // Prepare the TOC 156851f2e89SAnika Henke global $conf; 157851f2e89SAnika Henke if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) { 158b8595a66SAndreas Gohr global $TOC; 159b8595a66SAndreas Gohr $TOC = $this->toc; 1600cecf9d5Sandi } 1613e55d035SAndreas Gohr 1623e55d035SAndreas Gohr // make sure there are no empty paragraphs 16327918226Schris $this->doc = preg_replace('#<p>\s*</p>#', '', $this->doc); 164e41c4da9SAndreas Gohr } 1650cecf9d5Sandi 166*3dd5c225SAndreas Gohr /** 167*3dd5c225SAndreas Gohr * Add an item to the TOC 168*3dd5c225SAndreas Gohr * 169*3dd5c225SAndreas Gohr * @param string $id the hash link 170*3dd5c225SAndreas Gohr * @param string $text the text to display 171*3dd5c225SAndreas Gohr * @param int $level the nesting level 172*3dd5c225SAndreas Gohr */ 173e7856beaSchris function toc_additem($id, $text, $level) { 174af587fa8Sandi global $conf; 175af587fa8Sandi 176c5a8fd96SAndreas Gohr //handle TOC 177c5a8fd96SAndreas Gohr if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']) { 1787d91652aSAndreas Gohr $this->toc[] = html_mktocitem($id, $text, $level - $conf['toptoclevel'] + 1); 179c5a8fd96SAndreas Gohr } 180e7856beaSchris } 181e7856beaSchris 182*3dd5c225SAndreas Gohr /** 183*3dd5c225SAndreas Gohr * Render a heading 184*3dd5c225SAndreas Gohr * 185*3dd5c225SAndreas Gohr * @param string $text the text to display 186*3dd5c225SAndreas Gohr * @param int $level header level 187*3dd5c225SAndreas Gohr * @param int $pos byte position in the original source 188*3dd5c225SAndreas Gohr */ 189e7856beaSchris function header($text, $level, $pos) { 19090df9a4dSAdrian Lang global $conf; 19190df9a4dSAdrian Lang 192bdd8111bSAndreas Gohr if(!$text) return; //skip empty headlines 193e7856beaSchris 194e7856beaSchris $hid = $this->_headerToLink($text, true); 195e7856beaSchris 196e7856beaSchris //only add items within configured levels 197e7856beaSchris $this->toc_additem($hid, $text, $level); 198c5a8fd96SAndreas Gohr 19991459163SAnika Henke // adjust $node to reflect hierarchy of levels 20091459163SAnika Henke $this->node[$level - 1]++; 20191459163SAnika Henke if($level < $this->lastlevel) { 20291459163SAnika Henke for($i = 0; $i < $this->lastlevel - $level; $i++) { 20391459163SAnika Henke $this->node[$this->lastlevel - $i - 1] = 0; 20491459163SAnika Henke } 20591459163SAnika Henke } 20691459163SAnika Henke $this->lastlevel = $level; 20791459163SAnika Henke 20890df9a4dSAdrian Lang if($level <= $conf['maxseclevel'] && 20990df9a4dSAdrian Lang count($this->sectionedits) > 0 && 210*3dd5c225SAndreas Gohr $this->sectionedits[count($this->sectionedits) - 1][2] === 'section' 211*3dd5c225SAndreas Gohr ) { 2126c1f778cSAdrian Lang $this->finishSectionEdit($pos - 1); 21390df9a4dSAdrian Lang } 21490df9a4dSAdrian Lang 215c5a8fd96SAndreas Gohr // write the header 21690df9a4dSAdrian Lang $this->doc .= DOKU_LF.'<h'.$level; 21790df9a4dSAdrian Lang if($level <= $conf['maxseclevel']) { 21890df9a4dSAdrian Lang $this->doc .= ' class="'.$this->startSectionEdit($pos, 'section', $text).'"'; 21990df9a4dSAdrian Lang } 22016cc7ed7SAnika Henke $this->doc .= ' id="'.$hid.'">'; 221a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 22216cc7ed7SAnika Henke $this->doc .= "</h$level>".DOKU_LF; 2230cecf9d5Sandi } 2240cecf9d5Sandi 225*3dd5c225SAndreas Gohr /** 226*3dd5c225SAndreas Gohr * Open a new section 227*3dd5c225SAndreas Gohr * 228*3dd5c225SAndreas Gohr * @param int $level section level (as determined by the previous header) 229*3dd5c225SAndreas Gohr */ 2300cecf9d5Sandi function section_open($level) { 2319864e7b1SAdrian Lang $this->doc .= '<div class="level'.$level.'">'.DOKU_LF; 2320cecf9d5Sandi } 2330cecf9d5Sandi 234*3dd5c225SAndreas Gohr /** 235*3dd5c225SAndreas Gohr * Close the current section 236*3dd5c225SAndreas Gohr */ 2370cecf9d5Sandi function section_close() { 238a2d649c4Sandi $this->doc .= DOKU_LF.'</div>'.DOKU_LF; 2390cecf9d5Sandi } 2400cecf9d5Sandi 241*3dd5c225SAndreas Gohr /** 242*3dd5c225SAndreas Gohr * Render plain text data 243*3dd5c225SAndreas Gohr * 244*3dd5c225SAndreas Gohr * @param $text 245*3dd5c225SAndreas Gohr */ 2460cecf9d5Sandi function cdata($text) { 247a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 2480cecf9d5Sandi } 2490cecf9d5Sandi 250*3dd5c225SAndreas Gohr /** 251*3dd5c225SAndreas Gohr * Open a paragraph 252*3dd5c225SAndreas Gohr */ 2530cecf9d5Sandi function p_open() { 25459869a4bSAnika Henke $this->doc .= DOKU_LF.'<p>'.DOKU_LF; 2550cecf9d5Sandi } 2560cecf9d5Sandi 257*3dd5c225SAndreas Gohr /** 258*3dd5c225SAndreas Gohr * Close a paragraph 259*3dd5c225SAndreas Gohr */ 2600cecf9d5Sandi function p_close() { 26159869a4bSAnika Henke $this->doc .= DOKU_LF.'</p>'.DOKU_LF; 2620cecf9d5Sandi } 2630cecf9d5Sandi 264*3dd5c225SAndreas Gohr /** 265*3dd5c225SAndreas Gohr * Create a line break 266*3dd5c225SAndreas Gohr */ 2670cecf9d5Sandi function linebreak() { 268a2d649c4Sandi $this->doc .= '<br/>'.DOKU_LF; 2690cecf9d5Sandi } 2700cecf9d5Sandi 271*3dd5c225SAndreas Gohr /** 272*3dd5c225SAndreas Gohr * Create a horizontal line 273*3dd5c225SAndreas Gohr */ 2740cecf9d5Sandi function hr() { 2754beabca9SAnika Henke $this->doc .= '<hr />'.DOKU_LF; 2760cecf9d5Sandi } 2770cecf9d5Sandi 278*3dd5c225SAndreas Gohr /** 279*3dd5c225SAndreas Gohr * Start strong (bold) formatting 280*3dd5c225SAndreas Gohr */ 2810cecf9d5Sandi function strong_open() { 282a2d649c4Sandi $this->doc .= '<strong>'; 2830cecf9d5Sandi } 2840cecf9d5Sandi 285*3dd5c225SAndreas Gohr /** 286*3dd5c225SAndreas Gohr * Stop strong (bold) formatting 287*3dd5c225SAndreas Gohr */ 2880cecf9d5Sandi function strong_close() { 289a2d649c4Sandi $this->doc .= '</strong>'; 2900cecf9d5Sandi } 2910cecf9d5Sandi 292*3dd5c225SAndreas Gohr /** 293*3dd5c225SAndreas Gohr * Start emphasis (italics) formatting 294*3dd5c225SAndreas Gohr */ 2950cecf9d5Sandi function emphasis_open() { 296a2d649c4Sandi $this->doc .= '<em>'; 2970cecf9d5Sandi } 2980cecf9d5Sandi 299*3dd5c225SAndreas Gohr /** 300*3dd5c225SAndreas Gohr * Stop emphasis (italics) formatting 301*3dd5c225SAndreas Gohr */ 3020cecf9d5Sandi function emphasis_close() { 303a2d649c4Sandi $this->doc .= '</em>'; 3040cecf9d5Sandi } 3050cecf9d5Sandi 306*3dd5c225SAndreas Gohr /** 307*3dd5c225SAndreas Gohr * Start underline formatting 308*3dd5c225SAndreas Gohr */ 3090cecf9d5Sandi function underline_open() { 31002e51121SAnika Henke $this->doc .= '<em class="u">'; 3110cecf9d5Sandi } 3120cecf9d5Sandi 313*3dd5c225SAndreas Gohr /** 314*3dd5c225SAndreas Gohr * Stop underline formatting 315*3dd5c225SAndreas Gohr */ 3160cecf9d5Sandi function underline_close() { 31702e51121SAnika Henke $this->doc .= '</em>'; 3180cecf9d5Sandi } 3190cecf9d5Sandi 320*3dd5c225SAndreas Gohr /** 321*3dd5c225SAndreas Gohr * Start monospace formatting 322*3dd5c225SAndreas Gohr */ 3230cecf9d5Sandi function monospace_open() { 324a2d649c4Sandi $this->doc .= '<code>'; 3250cecf9d5Sandi } 3260cecf9d5Sandi 327*3dd5c225SAndreas Gohr /** 328*3dd5c225SAndreas Gohr * Stop monospace formatting 329*3dd5c225SAndreas Gohr */ 3300cecf9d5Sandi function monospace_close() { 331a2d649c4Sandi $this->doc .= '</code>'; 3320cecf9d5Sandi } 3330cecf9d5Sandi 334*3dd5c225SAndreas Gohr /** 335*3dd5c225SAndreas Gohr * Start a subscript 336*3dd5c225SAndreas Gohr */ 3370cecf9d5Sandi function subscript_open() { 338a2d649c4Sandi $this->doc .= '<sub>'; 3390cecf9d5Sandi } 3400cecf9d5Sandi 341*3dd5c225SAndreas Gohr /** 342*3dd5c225SAndreas Gohr * Stop a subscript 343*3dd5c225SAndreas Gohr */ 3440cecf9d5Sandi function subscript_close() { 345a2d649c4Sandi $this->doc .= '</sub>'; 3460cecf9d5Sandi } 3470cecf9d5Sandi 348*3dd5c225SAndreas Gohr /** 349*3dd5c225SAndreas Gohr * Start a superscript 350*3dd5c225SAndreas Gohr */ 3510cecf9d5Sandi function superscript_open() { 352a2d649c4Sandi $this->doc .= '<sup>'; 3530cecf9d5Sandi } 3540cecf9d5Sandi 355*3dd5c225SAndreas Gohr /** 356*3dd5c225SAndreas Gohr * Stop a superscript 357*3dd5c225SAndreas Gohr */ 3580cecf9d5Sandi function superscript_close() { 359a2d649c4Sandi $this->doc .= '</sup>'; 3600cecf9d5Sandi } 3610cecf9d5Sandi 362*3dd5c225SAndreas Gohr /** 363*3dd5c225SAndreas Gohr * Start deleted (strike-through) formatting 364*3dd5c225SAndreas Gohr */ 3650cecf9d5Sandi function deleted_open() { 366a2d649c4Sandi $this->doc .= '<del>'; 3670cecf9d5Sandi } 3680cecf9d5Sandi 369*3dd5c225SAndreas Gohr /** 370*3dd5c225SAndreas Gohr * Stop deleted (strike-through) formatting 371*3dd5c225SAndreas Gohr */ 3720cecf9d5Sandi function deleted_close() { 373a2d649c4Sandi $this->doc .= '</del>'; 3740cecf9d5Sandi } 3750cecf9d5Sandi 3763fd0b676Sandi /** 3773fd0b676Sandi * Callback for footnote start syntax 3783fd0b676Sandi * 3793fd0b676Sandi * All following content will go to the footnote instead of 380d74aace9Schris * the document. To achieve this the previous rendered content 3813fd0b676Sandi * is moved to $store and $doc is cleared 3823fd0b676Sandi * 3833fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 3843fd0b676Sandi */ 3850cecf9d5Sandi function footnote_open() { 3867764a90aSandi 3877764a90aSandi // move current content to store and record footnote 3887764a90aSandi $this->store = $this->doc; 3897764a90aSandi $this->doc = ''; 3900cecf9d5Sandi } 3910cecf9d5Sandi 3923fd0b676Sandi /** 3933fd0b676Sandi * Callback for footnote end syntax 3943fd0b676Sandi * 3953fd0b676Sandi * All rendered content is moved to the $footnotes array and the old 3963fd0b676Sandi * content is restored from $store again 3973fd0b676Sandi * 3983fd0b676Sandi * @author Andreas Gohr 3993fd0b676Sandi */ 4000cecf9d5Sandi function footnote_close() { 40116ec3e37SAndreas Gohr /** @var $fnid int takes track of seen footnotes, assures they are unique even across multiple docs FS#2841 */ 40216ec3e37SAndreas Gohr static $fnid = 0; 40316ec3e37SAndreas Gohr // assign new footnote id (we start at 1) 40416ec3e37SAndreas Gohr $fnid++; 4057764a90aSandi 406d74aace9Schris // recover footnote into the stack and restore old content 407d74aace9Schris $footnote = $this->doc; 4087764a90aSandi $this->doc = $this->store; 4097764a90aSandi $this->store = ''; 410d74aace9Schris 411d74aace9Schris // check to see if this footnote has been seen before 412d74aace9Schris $i = array_search($footnote, $this->footnotes); 413d74aace9Schris 414d74aace9Schris if($i === false) { 415d74aace9Schris // its a new footnote, add it to the $footnotes array 41616ec3e37SAndreas Gohr $this->footnotes[$fnid] = $footnote; 417d74aace9Schris } else { 41816ec3e37SAndreas Gohr // seen this one before, save a placeholder 41916ec3e37SAndreas Gohr $this->footnotes[$fnid] = "@@FNT".($i); 420d74aace9Schris } 421d74aace9Schris 4226b379cbfSAndreas Gohr // output the footnote reference and link 42316ec3e37SAndreas Gohr $this->doc .= '<sup><a href="#fn__'.$fnid.'" id="fnt__'.$fnid.'" class="fn_top">'.$fnid.')</a></sup>'; 4240cecf9d5Sandi } 4250cecf9d5Sandi 426*3dd5c225SAndreas Gohr /** 427*3dd5c225SAndreas Gohr * Open an unordered list 428*3dd5c225SAndreas Gohr */ 4290cecf9d5Sandi function listu_open() { 430a2d649c4Sandi $this->doc .= '<ul>'.DOKU_LF; 4310cecf9d5Sandi } 4320cecf9d5Sandi 433*3dd5c225SAndreas Gohr /** 434*3dd5c225SAndreas Gohr * Close an unordered list 435*3dd5c225SAndreas Gohr */ 4360cecf9d5Sandi function listu_close() { 437a2d649c4Sandi $this->doc .= '</ul>'.DOKU_LF; 4380cecf9d5Sandi } 4390cecf9d5Sandi 440*3dd5c225SAndreas Gohr /** 441*3dd5c225SAndreas Gohr * Open an ordered list 442*3dd5c225SAndreas Gohr */ 4430cecf9d5Sandi function listo_open() { 444a2d649c4Sandi $this->doc .= '<ol>'.DOKU_LF; 4450cecf9d5Sandi } 4460cecf9d5Sandi 447*3dd5c225SAndreas Gohr /** 448*3dd5c225SAndreas Gohr * Close an ordered list 449*3dd5c225SAndreas Gohr */ 4500cecf9d5Sandi function listo_close() { 451a2d649c4Sandi $this->doc .= '</ol>'.DOKU_LF; 4520cecf9d5Sandi } 4530cecf9d5Sandi 454*3dd5c225SAndreas Gohr /** 455*3dd5c225SAndreas Gohr * Open a list item 456*3dd5c225SAndreas Gohr * 457*3dd5c225SAndreas Gohr * @param int $level the nesting level 458*3dd5c225SAndreas Gohr */ 4590cecf9d5Sandi function listitem_open($level) { 46059869a4bSAnika Henke $this->doc .= '<li class="level'.$level.'">'; 4610cecf9d5Sandi } 4620cecf9d5Sandi 463*3dd5c225SAndreas Gohr /** 464*3dd5c225SAndreas Gohr * Close a list item 465*3dd5c225SAndreas Gohr */ 4660cecf9d5Sandi function listitem_close() { 467a2d649c4Sandi $this->doc .= '</li>'.DOKU_LF; 4680cecf9d5Sandi } 4690cecf9d5Sandi 470*3dd5c225SAndreas Gohr /** 471*3dd5c225SAndreas Gohr * Start the content of a list item 472*3dd5c225SAndreas Gohr */ 4730cecf9d5Sandi function listcontent_open() { 47490db23d7Schris $this->doc .= '<div class="li">'; 4750cecf9d5Sandi } 4760cecf9d5Sandi 477*3dd5c225SAndreas Gohr /** 478*3dd5c225SAndreas Gohr * Stop the content of a list item 479*3dd5c225SAndreas Gohr */ 4800cecf9d5Sandi function listcontent_close() { 48159869a4bSAnika Henke $this->doc .= '</div>'.DOKU_LF; 4820cecf9d5Sandi } 4830cecf9d5Sandi 484*3dd5c225SAndreas Gohr /** 485*3dd5c225SAndreas Gohr * Output unformatted $text 486*3dd5c225SAndreas Gohr * 487*3dd5c225SAndreas Gohr * Defaults to $this->cdata() 488*3dd5c225SAndreas Gohr * 489*3dd5c225SAndreas Gohr * @param string $text 490*3dd5c225SAndreas Gohr */ 4910cecf9d5Sandi function unformatted($text) { 492a2d649c4Sandi $this->doc .= $this->_xmlEntities($text); 4930cecf9d5Sandi } 4940cecf9d5Sandi 4950cecf9d5Sandi /** 4963fd0b676Sandi * Execute PHP code if allowed 4973fd0b676Sandi * 498d9764001SMichael Hamann * @param string $text PHP code that is either executed or printed 4995d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['phpok'] is okff 5005d568b99SChris Smith * 5013fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5020cecf9d5Sandi */ 5035d568b99SChris Smith function php($text, $wrapper = 'code') { 50435a56260SChris Smith global $conf; 50535a56260SChris Smith 506d86d5af0SChris Smith if($conf['phpok']) { 507bad0b545Sandi ob_start(); 5084de671bcSandi eval($text); 5093fd0b676Sandi $this->doc .= ob_get_contents(); 510bad0b545Sandi ob_end_clean(); 511d86d5af0SChris Smith } else { 5125d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper); 513d86d5af0SChris Smith } 5140cecf9d5Sandi } 5150cecf9d5Sandi 516*3dd5c225SAndreas Gohr /** 517*3dd5c225SAndreas Gohr * Output block level PHP code 518*3dd5c225SAndreas Gohr * 519*3dd5c225SAndreas Gohr * If $conf['phpok'] is true this should evaluate the given code and append the result 520*3dd5c225SAndreas Gohr * to $doc 521*3dd5c225SAndreas Gohr * 522*3dd5c225SAndreas Gohr * @param string $text The PHP code 523*3dd5c225SAndreas Gohr */ 52407f89c3cSAnika Henke function phpblock($text) { 5255d568b99SChris Smith $this->php($text, 'pre'); 52607f89c3cSAnika Henke } 52707f89c3cSAnika Henke 5280cecf9d5Sandi /** 5293fd0b676Sandi * Insert HTML if allowed 5303fd0b676Sandi * 531d9764001SMichael Hamann * @param string $text html text 5325d568b99SChris Smith * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff 5335d568b99SChris Smith * 5343fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 5350cecf9d5Sandi */ 5365d568b99SChris Smith function html($text, $wrapper = 'code') { 53735a56260SChris Smith global $conf; 53835a56260SChris Smith 539d86d5af0SChris Smith if($conf['htmlok']) { 540a2d649c4Sandi $this->doc .= $text; 541d86d5af0SChris Smith } else { 5425d568b99SChris Smith $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper); 543d86d5af0SChris Smith } 5444de671bcSandi } 5450cecf9d5Sandi 546*3dd5c225SAndreas Gohr /** 547*3dd5c225SAndreas Gohr * Output raw block-level HTML 548*3dd5c225SAndreas Gohr * 549*3dd5c225SAndreas Gohr * If $conf['htmlok'] is true this should add the code as is to $doc 550*3dd5c225SAndreas Gohr * 551*3dd5c225SAndreas Gohr * @param string $text The HTML 552*3dd5c225SAndreas Gohr */ 55307f89c3cSAnika Henke function htmlblock($text) { 5545d568b99SChris Smith $this->html($text, 'pre'); 55507f89c3cSAnika Henke } 55607f89c3cSAnika Henke 557*3dd5c225SAndreas Gohr /** 558*3dd5c225SAndreas Gohr * Start a block quote 559*3dd5c225SAndreas Gohr */ 5600cecf9d5Sandi function quote_open() { 56196331712SAnika Henke $this->doc .= '<blockquote><div class="no">'.DOKU_LF; 5620cecf9d5Sandi } 5630cecf9d5Sandi 564*3dd5c225SAndreas Gohr /** 565*3dd5c225SAndreas Gohr * Stop a block quote 566*3dd5c225SAndreas Gohr */ 5670cecf9d5Sandi function quote_close() { 56896331712SAnika Henke $this->doc .= '</div></blockquote>'.DOKU_LF; 5690cecf9d5Sandi } 5700cecf9d5Sandi 571*3dd5c225SAndreas Gohr /** 572*3dd5c225SAndreas Gohr * Output preformatted text 573*3dd5c225SAndreas Gohr * 574*3dd5c225SAndreas Gohr * @param string $text 575*3dd5c225SAndreas Gohr */ 5763d491f75SAndreas Gohr function preformatted($text) { 577c9250713SAnika Henke $this->doc .= '<pre class="code">'.trim($this->_xmlEntities($text), "\n\r").'</pre>'.DOKU_LF; 5783d491f75SAndreas Gohr } 5793d491f75SAndreas Gohr 580*3dd5c225SAndreas Gohr /** 581*3dd5c225SAndreas Gohr * Display text as file content, optionally syntax highlighted 582*3dd5c225SAndreas Gohr * 583*3dd5c225SAndreas Gohr * @param string $text text to show 584*3dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 585*3dd5c225SAndreas Gohr * @param string $filename file path label 586*3dd5c225SAndreas Gohr */ 5873d491f75SAndreas Gohr function file($text, $language = null, $filename = null) { 5883d491f75SAndreas Gohr $this->_highlight('file', $text, $language, $filename); 5893d491f75SAndreas Gohr } 5903d491f75SAndreas Gohr 591*3dd5c225SAndreas Gohr /** 592*3dd5c225SAndreas Gohr * Display text as code content, optionally syntax highlighted 593*3dd5c225SAndreas Gohr * 594*3dd5c225SAndreas Gohr * @param string $text text to show 595*3dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 596*3dd5c225SAndreas Gohr * @param string $filename file path label 597*3dd5c225SAndreas Gohr */ 5983d491f75SAndreas Gohr function code($text, $language = null, $filename = null) { 5993d491f75SAndreas Gohr $this->_highlight('code', $text, $language, $filename); 6003d491f75SAndreas Gohr } 6013d491f75SAndreas Gohr 6020cecf9d5Sandi /** 6033d491f75SAndreas Gohr * Use GeSHi to highlight language syntax in code and file blocks 6043fd0b676Sandi * 6053fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 606*3dd5c225SAndreas Gohr * @param string $type code|file 607*3dd5c225SAndreas Gohr * @param string $text text to show 608*3dd5c225SAndreas Gohr * @param string $language programming language to use for syntax highlighting 609*3dd5c225SAndreas Gohr * @param string $filename file path label 6100cecf9d5Sandi */ 6113d491f75SAndreas Gohr function _highlight($type, $text, $language = null, $filename = null) { 6123d491f75SAndreas Gohr global $ID; 6133d491f75SAndreas Gohr global $lang; 6143d491f75SAndreas Gohr 6153d491f75SAndreas Gohr if($filename) { 616190c56e8SAndreas Gohr // add icon 61727bf7924STom N Harris list($ext) = mimetype($filename, false); 618190c56e8SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 619190c56e8SAndreas Gohr $class = 'mediafile mf_'.$class; 620190c56e8SAndreas Gohr 6213d491f75SAndreas Gohr $this->doc .= '<dl class="'.$type.'">'.DOKU_LF; 622190c56e8SAndreas Gohr $this->doc .= '<dt><a href="'.exportlink($ID, 'code', array('codeblock' => $this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">'; 6233d491f75SAndreas Gohr $this->doc .= hsc($filename); 6243d491f75SAndreas Gohr $this->doc .= '</a></dt>'.DOKU_LF.'<dd>'; 6253d491f75SAndreas Gohr } 6260cecf9d5Sandi 627d43aac1cSGina Haeussge if($text{0} == "\n") { 628d43aac1cSGina Haeussge $text = substr($text, 1); 629d43aac1cSGina Haeussge } 630d43aac1cSGina Haeussge if(substr($text, -1) == "\n") { 631d43aac1cSGina Haeussge $text = substr($text, 0, -1); 632d43aac1cSGina Haeussge } 633d43aac1cSGina Haeussge 6340cecf9d5Sandi if(is_null($language)) { 6353d491f75SAndreas Gohr $this->doc .= '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; 6360cecf9d5Sandi } else { 6373d491f75SAndreas Gohr $class = 'code'; //we always need the code class to make the syntax highlighting apply 6383d491f75SAndreas Gohr if($type != 'code') $class .= ' '.$type; 6393d491f75SAndreas Gohr 6403d491f75SAndreas Gohr $this->doc .= "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '').'</pre>'.DOKU_LF; 6410cecf9d5Sandi } 6423d491f75SAndreas Gohr 6433d491f75SAndreas Gohr if($filename) { 6443d491f75SAndreas Gohr $this->doc .= '</dd></dl>'.DOKU_LF; 6453d491f75SAndreas Gohr } 6463d491f75SAndreas Gohr 6473d491f75SAndreas Gohr $this->_codeblock++; 6480cecf9d5Sandi } 6490cecf9d5Sandi 650*3dd5c225SAndreas Gohr /** 651*3dd5c225SAndreas Gohr * Format an acronym 652*3dd5c225SAndreas Gohr * 653*3dd5c225SAndreas Gohr * Uses $this->acronyms 654*3dd5c225SAndreas Gohr * 655*3dd5c225SAndreas Gohr * @param string $acronym 656*3dd5c225SAndreas Gohr */ 6570cecf9d5Sandi function acronym($acronym) { 6580cecf9d5Sandi 6590cecf9d5Sandi if(array_key_exists($acronym, $this->acronyms)) { 6600cecf9d5Sandi 661433bef32Sandi $title = $this->_xmlEntities($this->acronyms[$acronym]); 6620cecf9d5Sandi 663940db3a3SAnika Henke $this->doc .= '<abbr title="'.$title 664940db3a3SAnika Henke .'">'.$this->_xmlEntities($acronym).'</abbr>'; 6650cecf9d5Sandi 6660cecf9d5Sandi } else { 667a2d649c4Sandi $this->doc .= $this->_xmlEntities($acronym); 6680cecf9d5Sandi } 6690cecf9d5Sandi } 6700cecf9d5Sandi 671*3dd5c225SAndreas Gohr /** 672*3dd5c225SAndreas Gohr * Format a smiley 673*3dd5c225SAndreas Gohr * 674*3dd5c225SAndreas Gohr * Uses $this->smiley 675*3dd5c225SAndreas Gohr * 676*3dd5c225SAndreas Gohr * @param string $smiley 677*3dd5c225SAndreas Gohr */ 6780cecf9d5Sandi function smiley($smiley) { 6790cecf9d5Sandi if(array_key_exists($smiley, $this->smileys)) { 680f62ea8a1Sandi $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley]. 6818e38227fSAnika Henke '" class="icon" alt="'. 682433bef32Sandi $this->_xmlEntities($smiley).'" />'; 6830cecf9d5Sandi } else { 684a2d649c4Sandi $this->doc .= $this->_xmlEntities($smiley); 6850cecf9d5Sandi } 6860cecf9d5Sandi } 6870cecf9d5Sandi 688*3dd5c225SAndreas Gohr /** 689*3dd5c225SAndreas Gohr * Format an entity 690*3dd5c225SAndreas Gohr * 691*3dd5c225SAndreas Gohr * Entities are basically small text replacements 692*3dd5c225SAndreas Gohr * 693*3dd5c225SAndreas Gohr * Uses $this->entities 694*3dd5c225SAndreas Gohr * 695*3dd5c225SAndreas Gohr * @param string $entity 6964de671bcSandi */ 6970cecf9d5Sandi function entity($entity) { 6980cecf9d5Sandi if(array_key_exists($entity, $this->entities)) { 699a2d649c4Sandi $this->doc .= $this->entities[$entity]; 7000cecf9d5Sandi } else { 701a2d649c4Sandi $this->doc .= $this->_xmlEntities($entity); 7020cecf9d5Sandi } 7030cecf9d5Sandi } 7040cecf9d5Sandi 705*3dd5c225SAndreas Gohr /** 706*3dd5c225SAndreas Gohr * Typographically format a multiply sign 707*3dd5c225SAndreas Gohr * 708*3dd5c225SAndreas Gohr * Example: ($x=640, $y=480) should result in "640×480" 709*3dd5c225SAndreas Gohr * 710*3dd5c225SAndreas Gohr * @param string|int $x first value 711*3dd5c225SAndreas Gohr * @param string|int $y second value 712*3dd5c225SAndreas Gohr */ 7130cecf9d5Sandi function multiplyentity($x, $y) { 714a2d649c4Sandi $this->doc .= "$x×$y"; 7150cecf9d5Sandi } 7160cecf9d5Sandi 717*3dd5c225SAndreas Gohr /** 718*3dd5c225SAndreas Gohr * Render an opening single quote char (language specific) 719*3dd5c225SAndreas Gohr */ 7200cecf9d5Sandi function singlequoteopening() { 72171b40da2SAnika Henke global $lang; 72271b40da2SAnika Henke $this->doc .= $lang['singlequoteopening']; 7230cecf9d5Sandi } 7240cecf9d5Sandi 725*3dd5c225SAndreas Gohr /** 726*3dd5c225SAndreas Gohr * Render a closing single quote char (language specific) 727*3dd5c225SAndreas Gohr */ 7280cecf9d5Sandi function singlequoteclosing() { 72971b40da2SAnika Henke global $lang; 73071b40da2SAnika Henke $this->doc .= $lang['singlequoteclosing']; 7310cecf9d5Sandi } 7320cecf9d5Sandi 733*3dd5c225SAndreas Gohr /** 734*3dd5c225SAndreas Gohr * Render an apostrophe char (language specific) 735*3dd5c225SAndreas Gohr */ 73657d757d1SAndreas Gohr function apostrophe() { 73757d757d1SAndreas Gohr global $lang; 738a8bd192aSAndreas Gohr $this->doc .= $lang['apostrophe']; 73957d757d1SAndreas Gohr } 74057d757d1SAndreas Gohr 741*3dd5c225SAndreas Gohr /** 742*3dd5c225SAndreas Gohr * Render an opening double quote char (language specific) 743*3dd5c225SAndreas Gohr */ 7440cecf9d5Sandi function doublequoteopening() { 74571b40da2SAnika Henke global $lang; 74671b40da2SAnika Henke $this->doc .= $lang['doublequoteopening']; 7470cecf9d5Sandi } 7480cecf9d5Sandi 749*3dd5c225SAndreas Gohr /** 750*3dd5c225SAndreas Gohr * Render an closinging double quote char (language specific) 751*3dd5c225SAndreas Gohr */ 7520cecf9d5Sandi function doublequoteclosing() { 75371b40da2SAnika Henke global $lang; 75471b40da2SAnika Henke $this->doc .= $lang['doublequoteclosing']; 7550cecf9d5Sandi } 7560cecf9d5Sandi 7570cecf9d5Sandi /** 758*3dd5c225SAndreas Gohr * Render a CamelCase link 759*3dd5c225SAndreas Gohr * 760*3dd5c225SAndreas Gohr * @param string $link The link name 761*3dd5c225SAndreas Gohr * @see http://en.wikipedia.org/wiki/CamelCase 7620cecf9d5Sandi */ 7630cecf9d5Sandi function camelcaselink($link) { 76411d0aa47Sandi $this->internallink($link, $link); 7650cecf9d5Sandi } 7660cecf9d5Sandi 767*3dd5c225SAndreas Gohr /** 768*3dd5c225SAndreas Gohr * Render a page local link 769*3dd5c225SAndreas Gohr * 770*3dd5c225SAndreas Gohr * @param string $hash hash link identifier 771*3dd5c225SAndreas Gohr * @param string $name name for the link 772*3dd5c225SAndreas Gohr */ 7730ea51e63SMatt Perry function locallink($hash, $name = null) { 7740b7c14c2Sandi global $ID; 7750b7c14c2Sandi $name = $this->_getLinkTitle($name, $hash, $isImage); 7760b7c14c2Sandi $hash = $this->_headerToLink($hash); 777e260f93bSAnika Henke $title = $ID.' ↵'; 7780b7c14c2Sandi $this->doc .= '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">'; 7790b7c14c2Sandi $this->doc .= $name; 7800b7c14c2Sandi $this->doc .= '</a>'; 7810b7c14c2Sandi } 7820b7c14c2Sandi 783cffcc403Sandi /** 7843fd0b676Sandi * Render an internal Wiki Link 7853fd0b676Sandi * 786fe9ec250SChris Smith * $search,$returnonly & $linktype are not for the renderer but are used 787cffcc403Sandi * elsewhere - no need to implement them in other renderers 7883fd0b676Sandi * 789*3dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 790f23eef27SGerrit Uitslag * @param string $id pageid 791f23eef27SGerrit Uitslag * @param string|null $name link name 792f23eef27SGerrit Uitslag * @param string|null $search adds search url param 793f23eef27SGerrit Uitslag * @param bool $returnonly whether to return html or write to doc attribute 794f23eef27SGerrit Uitslag * @param string $linktype type to set use of headings 795f23eef27SGerrit Uitslag * @return void|string writes to doc attribute or returns html depends on $returnonly 796cffcc403Sandi */ 7970ea51e63SMatt Perry function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') { 798ba11bd29Sandi global $conf; 79937e34a5eSandi global $ID; 800c4dda6afSAnika Henke global $INFO; 80144653a53SAdrian Lang 8023d5e07d9SAdrian Lang $params = ''; 8033d5e07d9SAdrian Lang $parts = explode('?', $id, 2); 8043d5e07d9SAdrian Lang if(count($parts) === 2) { 8053d5e07d9SAdrian Lang $id = $parts[0]; 8063d5e07d9SAdrian Lang $params = $parts[1]; 80744653a53SAdrian Lang } 80844653a53SAdrian Lang 809fda14ffcSIzidor Matušov // For empty $id we need to know the current $ID 810fda14ffcSIzidor Matušov // We need this check because _simpleTitle needs 811fda14ffcSIzidor Matušov // correct $id and resolve_pageid() use cleanID($id) 812fda14ffcSIzidor Matušov // (some things could be lost) 813fda14ffcSIzidor Matušov if($id === '') { 814fda14ffcSIzidor Matušov $id = $ID; 815fda14ffcSIzidor Matušov } 816fda14ffcSIzidor Matušov 8170339c872Sjan // default name is based on $id as given 8180339c872Sjan $default = $this->_simpleTitle($id); 819ad32e47eSAndreas Gohr 8200339c872Sjan // now first resolve and clean up the $id 82137e34a5eSandi resolve_pageid(getNS($ID), $id, $exists); 822fda14ffcSIzidor Matušov 823fe9ec250SChris Smith $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype); 8240e1c636eSandi if(!$isImage) { 8250e1c636eSandi if($exists) { 826ba11bd29Sandi $class = 'wikilink1'; 8270cecf9d5Sandi } else { 828ba11bd29Sandi $class = 'wikilink2'; 82944a6b4c7SAndreas Gohr $link['rel'] = 'nofollow'; 8300cecf9d5Sandi } 8310cecf9d5Sandi } else { 832ba11bd29Sandi $class = 'media'; 8330cecf9d5Sandi } 8340cecf9d5Sandi 835a1685bedSandi //keep hash anchor 8366d2af55dSChristopher Smith @list($id, $hash) = explode('#', $id, 2); 837943dedc6SAndreas Gohr if(!empty($hash)) $hash = $this->_headerToLink($hash); 838a1685bedSandi 839ba11bd29Sandi //prepare for formating 840ba11bd29Sandi $link['target'] = $conf['target']['wiki']; 841ba11bd29Sandi $link['style'] = ''; 842ba11bd29Sandi $link['pre'] = ''; 843ba11bd29Sandi $link['suf'] = ''; 84440eb54bbSjan // highlight link to current page 845c4dda6afSAnika Henke if($id == $INFO['id']) { 84692795d04Sandi $link['pre'] = '<span class="curid">'; 84792795d04Sandi $link['suf'] = '</span>'; 84840eb54bbSjan } 8495e163278SAndreas Gohr $link['more'] = ''; 850ba11bd29Sandi $link['class'] = $class; 85144653a53SAdrian Lang $link['url'] = wl($id, $params); 852ba11bd29Sandi $link['name'] = $name; 853ba11bd29Sandi $link['title'] = $id; 854723d78dbSandi //add search string 855723d78dbSandi if($search) { 856546d3a99SAndreas Gohr ($conf['userewrite']) ? $link['url'] .= '?' : $link['url'] .= '&'; 857546d3a99SAndreas Gohr if(is_array($search)) { 858546d3a99SAndreas Gohr $search = array_map('rawurlencode', $search); 859546d3a99SAndreas Gohr $link['url'] .= 's[]='.join('&s[]=', $search); 860546d3a99SAndreas Gohr } else { 861546d3a99SAndreas Gohr $link['url'] .= 's='.rawurlencode($search); 862546d3a99SAndreas Gohr } 863723d78dbSandi } 864723d78dbSandi 865a1685bedSandi //keep hash 866a1685bedSandi if($hash) $link['url'] .= '#'.$hash; 867a1685bedSandi 868ba11bd29Sandi //output formatted 869cffcc403Sandi if($returnonly) { 870cffcc403Sandi return $this->_formatLink($link); 871cffcc403Sandi } else { 872a2d649c4Sandi $this->doc .= $this->_formatLink($link); 8730cecf9d5Sandi } 874cffcc403Sandi } 8750cecf9d5Sandi 876*3dd5c225SAndreas Gohr /** 877*3dd5c225SAndreas Gohr * Render an external link 878*3dd5c225SAndreas Gohr * 879*3dd5c225SAndreas Gohr * @param string $url full URL with scheme 880*3dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 881*3dd5c225SAndreas Gohr */ 8820ea51e63SMatt Perry function externallink($url, $name = null) { 883b625487dSandi global $conf; 8840cecf9d5Sandi 885433bef32Sandi $name = $this->_getLinkTitle($name, $url, $isImage); 8866f0c5dbfSandi 887b52b1596SAndreas Gohr // url might be an attack vector, only allow registered protocols 888b52b1596SAndreas Gohr if(is_null($this->schemes)) $this->schemes = getSchemes(); 889b52b1596SAndreas Gohr list($scheme) = explode('://', $url); 890b52b1596SAndreas Gohr $scheme = strtolower($scheme); 891b52b1596SAndreas Gohr if(!in_array($scheme, $this->schemes)) $url = ''; 892b52b1596SAndreas Gohr 893b52b1596SAndreas Gohr // is there still an URL? 894b52b1596SAndreas Gohr if(!$url) { 895b52b1596SAndreas Gohr $this->doc .= $name; 896b52b1596SAndreas Gohr return; 897b52b1596SAndreas Gohr } 898b52b1596SAndreas Gohr 899b52b1596SAndreas Gohr // set class 9000cecf9d5Sandi if(!$isImage) { 901b625487dSandi $class = 'urlextern'; 9020cecf9d5Sandi } else { 903b625487dSandi $class = 'media'; 9040cecf9d5Sandi } 9050cecf9d5Sandi 906b625487dSandi //prepare for formating 907b625487dSandi $link['target'] = $conf['target']['extern']; 908b625487dSandi $link['style'] = ''; 909b625487dSandi $link['pre'] = ''; 910b625487dSandi $link['suf'] = ''; 9115e163278SAndreas Gohr $link['more'] = ''; 912b625487dSandi $link['class'] = $class; 913b625487dSandi $link['url'] = $url; 914e1c10e4dSchris 915b625487dSandi $link['name'] = $name; 916433bef32Sandi $link['title'] = $this->_xmlEntities($url); 917b625487dSandi if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"'; 9180cecf9d5Sandi 919b625487dSandi //output formatted 920a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9210cecf9d5Sandi } 9220cecf9d5Sandi 9230cecf9d5Sandi /** 924*3dd5c225SAndreas Gohr * Render an interwiki link 925*3dd5c225SAndreas Gohr * 926*3dd5c225SAndreas Gohr * You may want to use $this->_resolveInterWiki() here 927*3dd5c225SAndreas Gohr * 928*3dd5c225SAndreas Gohr * @param string $match original link - probably not much use 929*3dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 930*3dd5c225SAndreas Gohr * @param string $wikiName indentifier (shortcut) for the remote wiki 931*3dd5c225SAndreas Gohr * @param string $wikiUri the fragment parsed from the original link 9320cecf9d5Sandi */ 9330ea51e63SMatt Perry function interwikilink($match, $name = null, $wikiName, $wikiUri) { 934b625487dSandi global $conf; 9350cecf9d5Sandi 93697a3e4e3Sandi $link = array(); 93797a3e4e3Sandi $link['target'] = $conf['target']['interwiki']; 93897a3e4e3Sandi $link['pre'] = ''; 93997a3e4e3Sandi $link['suf'] = ''; 9405e163278SAndreas Gohr $link['more'] = ''; 941433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage); 9420cecf9d5Sandi 94397a3e4e3Sandi //get interwiki URL 9446496c33fSGerrit Uitslag $exists = null; 9456496c33fSGerrit Uitslag $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists); 9460cecf9d5Sandi 94797a3e4e3Sandi if(!$isImage) { 9489d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $wikiName); 9499d2ddea4SAndreas Gohr $link['class'] = "interwiki iw_$class"; 9501c2d1019SAndreas Gohr } else { 9511c2d1019SAndreas Gohr $link['class'] = 'media'; 95297a3e4e3Sandi } 9530cecf9d5Sandi 95497a3e4e3Sandi //do we stay at the same server? Use local target 9552345e871SGerrit Uitslag if(strpos($url, DOKU_URL) === 0 OR strpos($url, DOKU_BASE) === 0) { 95697a3e4e3Sandi $link['target'] = $conf['target']['wiki']; 95797a3e4e3Sandi } 9586496c33fSGerrit Uitslag if($exists !== null && !$isImage) { 9596496c33fSGerrit Uitslag if($exists) { 9606496c33fSGerrit Uitslag $link['class'] .= ' wikilink1'; 9616496c33fSGerrit Uitslag } else { 9626496c33fSGerrit Uitslag $link['class'] .= ' wikilink2'; 9636496c33fSGerrit Uitslag $link['rel'] = 'nofollow'; 9646496c33fSGerrit Uitslag } 9656496c33fSGerrit Uitslag } 9660cecf9d5Sandi 96797a3e4e3Sandi $link['url'] = $url; 96897a3e4e3Sandi $link['title'] = htmlspecialchars($link['url']); 96997a3e4e3Sandi 97097a3e4e3Sandi //output formatted 971a2d649c4Sandi $this->doc .= $this->_formatLink($link); 9720cecf9d5Sandi } 9730cecf9d5Sandi 9740cecf9d5Sandi /** 975*3dd5c225SAndreas Gohr * Link to windows share 976*3dd5c225SAndreas Gohr * 977*3dd5c225SAndreas Gohr * @param string $url the link 978*3dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 9790cecf9d5Sandi */ 9800ea51e63SMatt Perry function windowssharelink($url, $name = null) { 9811d47afe1Sandi global $conf; 982*3dd5c225SAndreas Gohr 9831d47afe1Sandi //simple setup 9841d47afe1Sandi $link['target'] = $conf['target']['windows']; 9851d47afe1Sandi $link['pre'] = ''; 9861d47afe1Sandi $link['suf'] = ''; 9871d47afe1Sandi $link['style'] = ''; 9880cecf9d5Sandi 989433bef32Sandi $link['name'] = $this->_getLinkTitle($name, $url, $isImage); 9900cecf9d5Sandi if(!$isImage) { 9911d47afe1Sandi $link['class'] = 'windows'; 9920cecf9d5Sandi } else { 9931d47afe1Sandi $link['class'] = 'media'; 9940cecf9d5Sandi } 9950cecf9d5Sandi 996433bef32Sandi $link['title'] = $this->_xmlEntities($url); 9971d47afe1Sandi $url = str_replace('\\', '/', $url); 9981d47afe1Sandi $url = 'file:///'.$url; 9991d47afe1Sandi $link['url'] = $url; 10000cecf9d5Sandi 10011d47afe1Sandi //output formatted 1002a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10030cecf9d5Sandi } 10040cecf9d5Sandi 1005*3dd5c225SAndreas Gohr /** 1006*3dd5c225SAndreas Gohr * Render a linked E-Mail Address 1007*3dd5c225SAndreas Gohr * 1008*3dd5c225SAndreas Gohr * Honors $conf['mailguard'] setting 1009*3dd5c225SAndreas Gohr * 1010*3dd5c225SAndreas Gohr * @param string $address Email-Address 1011*3dd5c225SAndreas Gohr * @param string|array $name name for the link, array for media file 1012*3dd5c225SAndreas Gohr */ 10130ea51e63SMatt Perry function emaillink($address, $name = null) { 101471352defSandi global $conf; 101571352defSandi //simple setup 101671352defSandi $link = array(); 101771352defSandi $link['target'] = ''; 101871352defSandi $link['pre'] = ''; 101971352defSandi $link['suf'] = ''; 102071352defSandi $link['style'] = ''; 102171352defSandi $link['more'] = ''; 10220cecf9d5Sandi 1023c078fc55SAndreas Gohr $name = $this->_getLinkTitle($name, '', $isImage); 10240cecf9d5Sandi if(!$isImage) { 1025be96545cSAnika Henke $link['class'] = 'mail'; 10260cecf9d5Sandi } else { 1027be96545cSAnika Henke $link['class'] = 'media'; 10280cecf9d5Sandi } 10290cecf9d5Sandi 103007738714SAndreas Gohr $address = $this->_xmlEntities($address); 103100a7b5adSEsther Brunner $address = obfuscate($address); 103200a7b5adSEsther Brunner $title = $address; 10338c128049SAndreas Gohr 103471352defSandi if(empty($name)) { 103500a7b5adSEsther Brunner $name = $address; 103671352defSandi } 10370cecf9d5Sandi 1038776b36ecSAndreas Gohr if($conf['mailguard'] == 'visible') $address = rawurlencode($address); 1039776b36ecSAndreas Gohr 1040776b36ecSAndreas Gohr $link['url'] = 'mailto:'.$address; 104171352defSandi $link['name'] = $name; 104271352defSandi $link['title'] = $title; 10430cecf9d5Sandi 104471352defSandi //output formatted 1045a2d649c4Sandi $this->doc .= $this->_formatLink($link); 10460cecf9d5Sandi } 10470cecf9d5Sandi 1048*3dd5c225SAndreas Gohr /** 1049*3dd5c225SAndreas Gohr * Render an internal media file 1050*3dd5c225SAndreas Gohr * 1051*3dd5c225SAndreas Gohr * @param string $src media ID 1052*3dd5c225SAndreas Gohr * @param string $title descriptive text 1053*3dd5c225SAndreas Gohr * @param string $align left|center|right 1054*3dd5c225SAndreas Gohr * @param int $width width of media in pixel 1055*3dd5c225SAndreas Gohr * @param int $height height of media in pixel 1056*3dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 1057*3dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1058*3dd5c225SAndreas Gohr * @param bool $return return HTML instead of adding to $doc 1059*3dd5c225SAndreas Gohr * @return void|string 1060*3dd5c225SAndreas Gohr */ 10610ea51e63SMatt Perry function internalmedia($src, $title = null, $align = null, $width = null, 1062*3dd5c225SAndreas Gohr $height = null, $cache = null, $linking = null, $return = false) { 106337e34a5eSandi global $ID; 106491df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 106537e34a5eSandi resolve_mediaid(getNS($ID), $src, $exists); 10660cecf9d5Sandi 1067d98d4540SBen Coburn $noLink = false; 10688acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1069b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 10703685f775Sandi 1071*3dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1072b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 1073dc673a5bSjoe.lapp $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache), ($linking == 'direct')); 1074f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 10752a2a2ba2SAnika Henke // don't link movies 107644881bd0Shenning.noren $noLink = true; 107755efc227SAndreas Gohr } else { 10782ca14335SEsther Brunner // add file icons 10799d2ddea4SAndreas Gohr $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 10809d2ddea4SAndreas Gohr $link['class'] .= ' mediafile mf_'.$class; 10816de3759aSAndreas Gohr $link['url'] = ml($src, array('id' => $ID, 'cache' => $cache), true); 108291328684SMichael Hamann if($exists) $link['title'] .= ' ('.filesize_h(filesize(mediaFN($src))).')'; 108355efc227SAndreas Gohr } 10843685f775Sandi 108591df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 108691df343aSAndreas Gohr 10876fe20453SGina Haeussge //markup non existing files 10884a24b459SKate Arzamastseva if(!$exists) { 10896fe20453SGina Haeussge $link['class'] .= ' wikilink2'; 10904a24b459SKate Arzamastseva } 10916fe20453SGina Haeussge 10923685f775Sandi //output formatted 1093f50634f0SAnika Henke if($return) { 1094f50634f0SAnika Henke if($linking == 'nolink' || $noLink) return $link['name']; 1095f50634f0SAnika Henke else return $this->_formatLink($link); 1096f50634f0SAnika Henke } else { 1097dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 10982ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 10990cecf9d5Sandi } 1100f50634f0SAnika Henke } 11010cecf9d5Sandi 1102*3dd5c225SAndreas Gohr /** 1103*3dd5c225SAndreas Gohr * Render an external media file 1104*3dd5c225SAndreas Gohr * 1105*3dd5c225SAndreas Gohr * @param string $src full media URL 1106*3dd5c225SAndreas Gohr * @param string $title descriptive text 1107*3dd5c225SAndreas Gohr * @param string $align left|center|right 1108*3dd5c225SAndreas Gohr * @param int $width width of media in pixel 1109*3dd5c225SAndreas Gohr * @param int $height height of media in pixel 1110*3dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 1111*3dd5c225SAndreas Gohr * @param string $linking linkonly|detail|nolink 1112*3dd5c225SAndreas Gohr */ 11130ea51e63SMatt Perry function externalmedia($src, $title = null, $align = null, $width = null, 11140ea51e63SMatt Perry $height = null, $cache = null, $linking = null) { 111591df343aSAndreas Gohr list($src, $hash) = explode('#', $src, 2); 1116d98d4540SBen Coburn $noLink = false; 11178acb3108SAndreas Gohr $render = ($linking == 'linkonly') ? false : true; 1118b739ff0fSPierre Spring $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); 1119b739ff0fSPierre Spring 1120b739ff0fSPierre Spring $link['url'] = ml($src, array('cache' => $cache)); 11213685f775Sandi 1122*3dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src, false); 1123b739ff0fSPierre Spring if(substr($mime, 0, 5) == 'image' && $render) { 11242ca14335SEsther Brunner // link only jpeg images 112544881bd0Shenning.noren // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true; 1126f50634f0SAnika Henke } elseif(($mime == 'application/x-shockwave-flash' || media_supportedav($mime)) && $render) { 11272a2a2ba2SAnika Henke // don't link movies 112844881bd0Shenning.noren $noLink = true; 11292ca14335SEsther Brunner } else { 11302ca14335SEsther Brunner // add file icons 113127bf7924STom N Harris $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext); 113227bf7924STom N Harris $link['class'] .= ' mediafile mf_'.$class; 11332ca14335SEsther Brunner } 11342ca14335SEsther Brunner 113591df343aSAndreas Gohr if($hash) $link['url'] .= '#'.$hash; 113691df343aSAndreas Gohr 11373685f775Sandi //output formatted 1138dc673a5bSjoe.lapp if($linking == 'nolink' || $noLink) $this->doc .= $link['name']; 11392ca14335SEsther Brunner else $this->doc .= $this->_formatLink($link); 11400cecf9d5Sandi } 11410cecf9d5Sandi 11424826ab45Sandi /** 11433db95becSAndreas Gohr * Renders an RSS feed 1144b625487dSandi * 1145b625487dSandi * @author Andreas Gohr <andi@splitbrain.org> 1146b625487dSandi */ 11473db95becSAndreas Gohr function rss($url, $params) { 1148b625487dSandi global $lang; 11493db95becSAndreas Gohr global $conf; 11503db95becSAndreas Gohr 11513db95becSAndreas Gohr require_once(DOKU_INC.'inc/FeedParser.php'); 11523db95becSAndreas Gohr $feed = new FeedParser(); 115300077af8SAndreas Gohr $feed->set_feed_url($url); 1154b625487dSandi 1155b625487dSandi //disable warning while fetching 1156*3dd5c225SAndreas Gohr if(!defined('DOKU_E_LEVEL')) { 1157*3dd5c225SAndreas Gohr $elvl = error_reporting(E_ERROR); 1158*3dd5c225SAndreas Gohr } 11593db95becSAndreas Gohr $rc = $feed->init(); 1160*3dd5c225SAndreas Gohr if(isset($elvl)) { 1161*3dd5c225SAndreas Gohr error_reporting($elvl); 1162*3dd5c225SAndreas Gohr } 1163b625487dSandi 11643db95becSAndreas Gohr //decide on start and end 11653db95becSAndreas Gohr if($params['reverse']) { 11663db95becSAndreas Gohr $mod = -1; 11673db95becSAndreas Gohr $start = $feed->get_item_quantity() - 1; 11683db95becSAndreas Gohr $end = $start - ($params['max']); 1169b2a412b0SAndreas Gohr $end = ($end < -1) ? -1 : $end; 11703db95becSAndreas Gohr } else { 11713db95becSAndreas Gohr $mod = 1; 11723db95becSAndreas Gohr $start = 0; 11733db95becSAndreas Gohr $end = $feed->get_item_quantity(); 1174d91ab76fSMatt Perry $end = ($end > $params['max']) ? $params['max'] : $end; 11753db95becSAndreas Gohr } 11763db95becSAndreas Gohr 1177a2d649c4Sandi $this->doc .= '<ul class="rss">'; 11783db95becSAndreas Gohr if($rc) { 11793db95becSAndreas Gohr for($x = $start; $x != $end; $x += $mod) { 11801bde1582SAndreas Gohr $item = $feed->get_item($x); 11813db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1182d2ea3363SAndreas Gohr // support feeds without links 1183d2ea3363SAndreas Gohr $lnkurl = $item->get_permalink(); 1184d2ea3363SAndreas Gohr if($lnkurl) { 1185793361f8SAndreas Gohr // title is escaped by SimplePie, we unescape here because it 1186793361f8SAndreas Gohr // is escaped again in externallink() FS#1705 1187*3dd5c225SAndreas Gohr $this->externallink( 1188*3dd5c225SAndreas Gohr $item->get_permalink(), 1189*3dd5c225SAndreas Gohr html_entity_decode($item->get_title(), ENT_QUOTES, 'UTF-8') 1190*3dd5c225SAndreas Gohr ); 1191d2ea3363SAndreas Gohr } else { 1192d2ea3363SAndreas Gohr $this->doc .= ' '.$item->get_title(); 1193d2ea3363SAndreas Gohr } 11943db95becSAndreas Gohr if($params['author']) { 11951bde1582SAndreas Gohr $author = $item->get_author(0); 11961bde1582SAndreas Gohr if($author) { 11971bde1582SAndreas Gohr $name = $author->get_name(); 11981bde1582SAndreas Gohr if(!$name) $name = $author->get_email(); 11991bde1582SAndreas Gohr if($name) $this->doc .= ' '.$lang['by'].' '.$name; 12001bde1582SAndreas Gohr } 12013db95becSAndreas Gohr } 12023db95becSAndreas Gohr if($params['date']) { 12032e7e0c29SAndreas Gohr $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')'; 12043db95becSAndreas Gohr } 12051bde1582SAndreas Gohr if($params['details']) { 12063db95becSAndreas Gohr $this->doc .= '<div class="detail">'; 1207173dccb7STom N Harris if($conf['htmlok']) { 12081bde1582SAndreas Gohr $this->doc .= $item->get_description(); 12093db95becSAndreas Gohr } else { 12101bde1582SAndreas Gohr $this->doc .= strip_tags($item->get_description()); 12113db95becSAndreas Gohr } 12123db95becSAndreas Gohr $this->doc .= '</div>'; 12133db95becSAndreas Gohr } 12143db95becSAndreas Gohr 12153db95becSAndreas Gohr $this->doc .= '</div></li>'; 1216b625487dSandi } 1217b625487dSandi } else { 12183db95becSAndreas Gohr $this->doc .= '<li><div class="li">'; 1219a2d649c4Sandi $this->doc .= '<em>'.$lang['rssfailed'].'</em>'; 1220b625487dSandi $this->externallink($url); 122145e147ccSAndreas Gohr if($conf['allowdebug']) { 122245e147ccSAndreas Gohr $this->doc .= '<!--'.hsc($feed->error).'-->'; 122345e147ccSAndreas Gohr } 12243db95becSAndreas Gohr $this->doc .= '</div></li>'; 1225b625487dSandi } 1226a2d649c4Sandi $this->doc .= '</ul>'; 1227b625487dSandi } 1228b625487dSandi 1229*3dd5c225SAndreas Gohr /** 1230*3dd5c225SAndreas Gohr * Start a table 1231*3dd5c225SAndreas Gohr * 1232*3dd5c225SAndreas Gohr * @param int $maxcols maximum number of columns 1233*3dd5c225SAndreas Gohr * @param int $numrows NOT IMPLEMENTED 1234*3dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1235*3dd5c225SAndreas Gohr */ 1236619736fdSAdrian Lang function table_open($maxcols = null, $numrows = null, $pos = null) { 1237b5742cedSPierre Spring // initialize the row counter used for classes 1238b5742cedSPierre Spring $this->_counter['row_counter'] = 0; 1239619736fdSAdrian Lang $class = 'table'; 1240619736fdSAdrian Lang if($pos !== null) { 1241619736fdSAdrian Lang $class .= ' '.$this->startSectionEdit($pos, 'table'); 1242619736fdSAdrian Lang } 1243619736fdSAdrian Lang $this->doc .= '<div class="'.$class.'"><table class="inline">'. 1244619736fdSAdrian Lang DOKU_LF; 12450cecf9d5Sandi } 12460cecf9d5Sandi 1247*3dd5c225SAndreas Gohr /** 1248*3dd5c225SAndreas Gohr * Close a table 1249*3dd5c225SAndreas Gohr * 1250*3dd5c225SAndreas Gohr * @param int $pos byte position in the original source 1251*3dd5c225SAndreas Gohr */ 1252619736fdSAdrian Lang function table_close($pos = null) { 1253a8574918SAnika Henke $this->doc .= '</table></div>'.DOKU_LF; 1254619736fdSAdrian Lang if($pos !== null) { 125590df9a4dSAdrian Lang $this->finishSectionEdit($pos); 12560cecf9d5Sandi } 1257619736fdSAdrian Lang } 12580cecf9d5Sandi 1259*3dd5c225SAndreas Gohr /** 1260*3dd5c225SAndreas Gohr * Open a table header 1261*3dd5c225SAndreas Gohr */ 1262f05a1cc5SGerrit Uitslag function tablethead_open() { 1263f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'<thead>'.DOKU_LF; 1264f05a1cc5SGerrit Uitslag } 1265f05a1cc5SGerrit Uitslag 1266*3dd5c225SAndreas Gohr /** 1267*3dd5c225SAndreas Gohr * Close a table header 1268*3dd5c225SAndreas Gohr */ 1269f05a1cc5SGerrit Uitslag function tablethead_close() { 1270f05a1cc5SGerrit Uitslag $this->doc .= DOKU_TAB.'</thead>'.DOKU_LF; 1271f05a1cc5SGerrit Uitslag } 1272f05a1cc5SGerrit Uitslag 1273*3dd5c225SAndreas Gohr /** 1274*3dd5c225SAndreas Gohr * Open a table row 1275*3dd5c225SAndreas Gohr */ 12760cecf9d5Sandi function tablerow_open() { 1277b5742cedSPierre Spring // initialize the cell counter used for classes 1278b5742cedSPierre Spring $this->_counter['cell_counter'] = 0; 1279b5742cedSPierre Spring $class = 'row'.$this->_counter['row_counter']++; 1280b5742cedSPierre Spring $this->doc .= DOKU_TAB.'<tr class="'.$class.'">'.DOKU_LF.DOKU_TAB.DOKU_TAB; 12810cecf9d5Sandi } 12820cecf9d5Sandi 1283*3dd5c225SAndreas Gohr /** 1284*3dd5c225SAndreas Gohr * Close a table row 1285*3dd5c225SAndreas Gohr */ 12860cecf9d5Sandi function tablerow_close() { 1287a2d649c4Sandi $this->doc .= DOKU_LF.DOKU_TAB.'</tr>'.DOKU_LF; 12880cecf9d5Sandi } 12890cecf9d5Sandi 1290*3dd5c225SAndreas Gohr /** 1291*3dd5c225SAndreas Gohr * Open a table header cell 1292*3dd5c225SAndreas Gohr * 1293*3dd5c225SAndreas Gohr * @param int $colspan 1294*3dd5c225SAndreas Gohr * @param string $align left|center|right 1295*3dd5c225SAndreas Gohr * @param int $rowspan 1296*3dd5c225SAndreas Gohr */ 12970ea51e63SMatt Perry function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 1298b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 12990cecf9d5Sandi if(!is_null($align)) { 1300b5742cedSPierre Spring $class .= ' '.$align.'align'; 13010cecf9d5Sandi } 1302b5742cedSPierre Spring $class .= '"'; 1303b5742cedSPierre Spring $this->doc .= '<th '.$class; 13040cecf9d5Sandi if($colspan > 1) { 1305a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1306a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 13070cecf9d5Sandi } 130825b97867Shakan.sandell if($rowspan > 1) { 130925b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 131025b97867Shakan.sandell } 1311a2d649c4Sandi $this->doc .= '>'; 13120cecf9d5Sandi } 13130cecf9d5Sandi 1314*3dd5c225SAndreas Gohr /** 1315*3dd5c225SAndreas Gohr * Close a table header cell 1316*3dd5c225SAndreas Gohr */ 13170cecf9d5Sandi function tableheader_close() { 1318a2d649c4Sandi $this->doc .= '</th>'; 13190cecf9d5Sandi } 13200cecf9d5Sandi 1321*3dd5c225SAndreas Gohr /** 1322*3dd5c225SAndreas Gohr * Open a table cell 1323*3dd5c225SAndreas Gohr * 1324*3dd5c225SAndreas Gohr * @param int $colspan 1325*3dd5c225SAndreas Gohr * @param string $align left|center|right 1326*3dd5c225SAndreas Gohr * @param int $rowspan 1327*3dd5c225SAndreas Gohr */ 13280ea51e63SMatt Perry function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 1329b5742cedSPierre Spring $class = 'class="col'.$this->_counter['cell_counter']++; 13300cecf9d5Sandi if(!is_null($align)) { 1331b5742cedSPierre Spring $class .= ' '.$align.'align'; 13320cecf9d5Sandi } 1333b5742cedSPierre Spring $class .= '"'; 1334b5742cedSPierre Spring $this->doc .= '<td '.$class; 13350cecf9d5Sandi if($colspan > 1) { 1336a28fd914SAndreas Gohr $this->_counter['cell_counter'] += $colspan - 1; 1337a2d649c4Sandi $this->doc .= ' colspan="'.$colspan.'"'; 13380cecf9d5Sandi } 133925b97867Shakan.sandell if($rowspan > 1) { 134025b97867Shakan.sandell $this->doc .= ' rowspan="'.$rowspan.'"'; 134125b97867Shakan.sandell } 1342a2d649c4Sandi $this->doc .= '>'; 13430cecf9d5Sandi } 13440cecf9d5Sandi 1345*3dd5c225SAndreas Gohr /** 1346*3dd5c225SAndreas Gohr * Close a table cell 1347*3dd5c225SAndreas Gohr */ 13480cecf9d5Sandi function tablecell_close() { 1349a2d649c4Sandi $this->doc .= '</td>'; 13500cecf9d5Sandi } 13510cecf9d5Sandi 1352*3dd5c225SAndreas Gohr #region Utility functions 13530cecf9d5Sandi 1354ba11bd29Sandi /** 13553fd0b676Sandi * Build a link 13563fd0b676Sandi * 13573fd0b676Sandi * Assembles all parts defined in $link returns HTML for the link 1358ba11bd29Sandi * 1359ba11bd29Sandi * @author Andreas Gohr <andi@splitbrain.org> 1360ba11bd29Sandi */ 1361433bef32Sandi function _formatLink($link) { 1362ba11bd29Sandi //make sure the url is XHTML compliant (skip mailto) 1363ba11bd29Sandi if(substr($link['url'], 0, 7) != 'mailto:') { 1364ba11bd29Sandi $link['url'] = str_replace('&', '&', $link['url']); 1365ba11bd29Sandi $link['url'] = str_replace('&amp;', '&', $link['url']); 1366ba11bd29Sandi } 1367ba11bd29Sandi //remove double encodings in titles 1368ba11bd29Sandi $link['title'] = str_replace('&amp;', '&', $link['title']); 1369ba11bd29Sandi 1370453493f2SAndreas Gohr // be sure there are no bad chars in url or title 1371453493f2SAndreas Gohr // (we can't do this for name because it can contain an img tag) 1372453493f2SAndreas Gohr $link['url'] = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22')); 1373453493f2SAndreas Gohr $link['title'] = strtr($link['title'], array('>' => '>', '<' => '<', '"' => '"')); 1374453493f2SAndreas Gohr 1375ba11bd29Sandi $ret = ''; 1376ba11bd29Sandi $ret .= $link['pre']; 1377ba11bd29Sandi $ret .= '<a href="'.$link['url'].'"'; 1378bb4866bdSchris if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"'; 1379bb4866bdSchris if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"'; 1380bb4866bdSchris if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"'; 1381bb4866bdSchris if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"'; 138244a6b4c7SAndreas Gohr if(!empty($link['rel'])) $ret .= ' rel="'.$link['rel'].'"'; 1383bb4866bdSchris if(!empty($link['more'])) $ret .= ' '.$link['more']; 1384ba11bd29Sandi $ret .= '>'; 1385ba11bd29Sandi $ret .= $link['name']; 1386ba11bd29Sandi $ret .= '</a>'; 1387ba11bd29Sandi $ret .= $link['suf']; 1388ba11bd29Sandi return $ret; 1389ba11bd29Sandi } 1390ba11bd29Sandi 1391ba11bd29Sandi /** 13923fd0b676Sandi * Renders internal and external media 13933fd0b676Sandi * 13943fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1395*3dd5c225SAndreas Gohr * @param string $src media ID 1396*3dd5c225SAndreas Gohr * @param string $title descriptive text 1397*3dd5c225SAndreas Gohr * @param string $align left|center|right 1398*3dd5c225SAndreas Gohr * @param int $width width of media in pixel 1399*3dd5c225SAndreas Gohr * @param int $height height of media in pixel 1400*3dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 1401*3dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 1402*3dd5c225SAndreas Gohr * @return string 14033fd0b676Sandi */ 14040ea51e63SMatt Perry function _media($src, $title = null, $align = null, $width = null, 14050ea51e63SMatt Perry $height = null, $cache = null, $render = true) { 14063fd0b676Sandi 14073fd0b676Sandi $ret = ''; 14083fd0b676Sandi 1409*3dd5c225SAndreas Gohr list($ext, $mime) = mimetype($src); 14103fd0b676Sandi if(substr($mime, 0, 5) == 'image') { 1411b739ff0fSPierre Spring // first get the $title 1412b739ff0fSPierre Spring if(!is_null($title)) { 1413b739ff0fSPierre Spring $title = $this->_xmlEntities($title); 1414b739ff0fSPierre Spring } elseif($ext == 'jpg' || $ext == 'jpeg') { 1415b739ff0fSPierre Spring //try to use the caption from IPTC/EXIF 1416b739ff0fSPierre Spring require_once(DOKU_INC.'inc/JpegMeta.php'); 141767f9913dSAndreas Gohr $jpeg = new JpegMeta(mediaFN($src)); 1418b739ff0fSPierre Spring if($jpeg !== false) $cap = $jpeg->getTitle(); 1419*3dd5c225SAndreas Gohr if(!empty($cap)) { 1420b739ff0fSPierre Spring $title = $this->_xmlEntities($cap); 1421b739ff0fSPierre Spring } 1422b739ff0fSPierre Spring } 1423b739ff0fSPierre Spring if(!$render) { 1424b739ff0fSPierre Spring // if the picture is not supposed to be rendered 1425b739ff0fSPierre Spring // return the title of the picture 1426b739ff0fSPierre Spring if(!$title) { 1427b739ff0fSPierre Spring // just show the sourcename 14283009a773SAndreas Gohr $title = $this->_xmlEntities(utf8_basename(noNS($src))); 1429b739ff0fSPierre Spring } 1430b739ff0fSPierre Spring return $title; 1431b739ff0fSPierre Spring } 14323fd0b676Sandi //add image tag 14336de3759aSAndreas Gohr $ret .= '<img src="'.ml($src, array('w' => $width, 'h' => $height, 'cache' => $cache)).'"'; 14343fd0b676Sandi $ret .= ' class="media'.$align.'"'; 14353fd0b676Sandi 1436b739ff0fSPierre Spring if($title) { 1437b739ff0fSPierre Spring $ret .= ' title="'.$title.'"'; 1438b739ff0fSPierre Spring $ret .= ' alt="'.$title.'"'; 14393fd0b676Sandi } else { 14403fd0b676Sandi $ret .= ' alt=""'; 14413fd0b676Sandi } 14423fd0b676Sandi 14433fd0b676Sandi if(!is_null($width)) 14443fd0b676Sandi $ret .= ' width="'.$this->_xmlEntities($width).'"'; 14453fd0b676Sandi 14463fd0b676Sandi if(!is_null($height)) 14473fd0b676Sandi $ret .= ' height="'.$this->_xmlEntities($height).'"'; 14483fd0b676Sandi 14493fd0b676Sandi $ret .= ' />'; 14503fd0b676Sandi 145117954bb5SAnika Henke } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { 14522a2a2ba2SAnika Henke // first get the $title 145317954bb5SAnika Henke $title = !is_null($title) ? $this->_xmlEntities($title) : false; 14542a2a2ba2SAnika Henke if(!$render) { 145517954bb5SAnika Henke // if the file is not supposed to be rendered 145617954bb5SAnika Henke // return the title of the file (just the sourcename if there is no title) 145717954bb5SAnika Henke return $title ? $title : $this->_xmlEntities(utf8_basename(noNS($src))); 14582a2a2ba2SAnika Henke } 14592a2a2ba2SAnika Henke 14602a2a2ba2SAnika Henke $att = array(); 14612a2a2ba2SAnika Henke $att['class'] = "media$align"; 146217954bb5SAnika Henke if($title) { 146317954bb5SAnika Henke $att['title'] = $title; 146417954bb5SAnika Henke } 14652a2a2ba2SAnika Henke 146617954bb5SAnika Henke if(media_supportedav($mime, 'video')) { 146717954bb5SAnika Henke //add video 146879e53fe5SAnika Henke $ret .= $this->_video($src, $width, $height, $att); 1469b44a5dceSAnika Henke } 147017954bb5SAnika Henke if(media_supportedav($mime, 'audio')) { 1471b44a5dceSAnika Henke //add audio 1472b44a5dceSAnika Henke $ret .= $this->_audio($src, $att); 147317954bb5SAnika Henke } 1474b44a5dceSAnika Henke 14753fd0b676Sandi } elseif($mime == 'application/x-shockwave-flash') { 14761c882ba8SAndreas Gohr if(!$render) { 14771c882ba8SAndreas Gohr // if the flash is not supposed to be rendered 14781c882ba8SAndreas Gohr // return the title of the flash 14791c882ba8SAndreas Gohr if(!$title) { 14801c882ba8SAndreas Gohr // just show the sourcename 14813009a773SAndreas Gohr $title = utf8_basename(noNS($src)); 14821c882ba8SAndreas Gohr } 148307bf32b2SAndreas Gohr return $this->_xmlEntities($title); 14841c882ba8SAndreas Gohr } 14851c882ba8SAndreas Gohr 148607bf32b2SAndreas Gohr $att = array(); 148707bf32b2SAndreas Gohr $att['class'] = "media$align"; 148807bf32b2SAndreas Gohr if($align == 'right') $att['align'] = 'right'; 148907bf32b2SAndreas Gohr if($align == 'left') $att['align'] = 'left'; 1490*3dd5c225SAndreas Gohr $ret .= html_flashobject( 1491*3dd5c225SAndreas Gohr ml($src, array('cache' => $cache), true, '&'), $width, $height, 149207bf32b2SAndreas Gohr array('quality' => 'high'), 149307bf32b2SAndreas Gohr null, 149407bf32b2SAndreas Gohr $att, 1495*3dd5c225SAndreas Gohr $this->_xmlEntities($title) 1496*3dd5c225SAndreas Gohr ); 14970f428d7dSAndreas Gohr } elseif($title) { 14983fd0b676Sandi // well at least we have a title to display 14993fd0b676Sandi $ret .= $this->_xmlEntities($title); 15003fd0b676Sandi } else { 15015291ca3aSAndreas Gohr // just show the sourcename 15023009a773SAndreas Gohr $ret .= $this->_xmlEntities(utf8_basename(noNS($src))); 15033fd0b676Sandi } 15043fd0b676Sandi 15053fd0b676Sandi return $ret; 15063fd0b676Sandi } 15073fd0b676Sandi 1508*3dd5c225SAndreas Gohr /** 1509*3dd5c225SAndreas Gohr * Escape string for output 1510*3dd5c225SAndreas Gohr * 1511*3dd5c225SAndreas Gohr * @param $string 1512*3dd5c225SAndreas Gohr * @return string 1513*3dd5c225SAndreas Gohr */ 1514433bef32Sandi function _xmlEntities($string) { 1515de117061Schris return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 15160cecf9d5Sandi } 15170cecf9d5Sandi 15188a831f2bSAndreas Gohr /** 15198a831f2bSAndreas Gohr * Creates a linkid from a headline 1520c5a8fd96SAndreas Gohr * 1521*3dd5c225SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1522c5a8fd96SAndreas Gohr * @param string $title The headline title 1523c5a8fd96SAndreas Gohr * @param boolean $create Create a new unique ID? 1524*3dd5c225SAndreas Gohr * @return string 15258a831f2bSAndreas Gohr */ 1526c5a8fd96SAndreas Gohr function _headerToLink($title, $create = false) { 1527c5a8fd96SAndreas Gohr if($create) { 15284ceab83fSAndreas Gohr return sectionID($title, $this->headers); 15294ceab83fSAndreas Gohr } else { 1530443d207bSAndreas Gohr $check = false; 1531443d207bSAndreas Gohr return sectionID($title, $check); 1532c5a8fd96SAndreas Gohr } 15330cecf9d5Sandi } 15340cecf9d5Sandi 1535af587fa8Sandi /** 15363fd0b676Sandi * Construct a title and handle images in titles 15373fd0b676Sandi * 15380b7c14c2Sandi * @author Harry Fuecks <hfuecks@gmail.com> 1539*3dd5c225SAndreas Gohr * @param string|array $title either string title or media array 1540*3dd5c225SAndreas Gohr * @param string $default default title if nothing else is found 1541*3dd5c225SAndreas Gohr * @param bool $isImage will be set to true if it's a media file 1542*3dd5c225SAndreas Gohr * @param null|string $id linked page id (used to extract title from first heading) 1543*3dd5c225SAndreas Gohr * @param string $linktype content|navigation 1544*3dd5c225SAndreas Gohr * @return string HTML of the title, might be full image tag or just escaped text 15453fd0b676Sandi */ 15460ea51e63SMatt Perry function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') { 154744881bd0Shenning.noren $isImage = false; 154829657f9eSAndreas Gohr if(is_array($title)) { 154929657f9eSAndreas Gohr $isImage = true; 155029657f9eSAndreas Gohr return $this->_imageTitle($title); 155129657f9eSAndreas Gohr } elseif(is_null($title) || trim($title) == '') { 1552fe9ec250SChris Smith if(useHeading($linktype) && $id) { 155367c15eceSMichael Hamann $heading = p_get_first_heading($id); 1554bb0a59d4Sjan if($heading) { 1555433bef32Sandi return $this->_xmlEntities($heading); 1556bb0a59d4Sjan } 1557bb0a59d4Sjan } 1558433bef32Sandi return $this->_xmlEntities($default); 155968c26e6dSMichael Klier } else { 156068c26e6dSMichael Klier return $this->_xmlEntities($title); 15610cecf9d5Sandi } 15620cecf9d5Sandi } 15630cecf9d5Sandi 15640cecf9d5Sandi /** 1565*3dd5c225SAndreas Gohr * Returns HTML code for images used in link titles 15663fd0b676Sandi * 15673fd0b676Sandi * @author Andreas Gohr <andi@splitbrain.org> 1568*3dd5c225SAndreas Gohr * @param string $img 1569*3dd5c225SAndreas Gohr * @return string HTML img tag or similar 15700cecf9d5Sandi */ 1571433bef32Sandi function _imageTitle($img) { 1572d9baf1a7SKazutaka Miyasaka global $ID; 1573d9baf1a7SKazutaka Miyasaka 1574d9baf1a7SKazutaka Miyasaka // some fixes on $img['src'] 1575d9baf1a7SKazutaka Miyasaka // see internalmedia() and externalmedia() 1576*3dd5c225SAndreas Gohr list($img['src']) = explode('#', $img['src'], 2); 1577d9baf1a7SKazutaka Miyasaka if($img['type'] == 'internalmedia') { 1578d9baf1a7SKazutaka Miyasaka resolve_mediaid(getNS($ID), $img['src'], $exists); 1579d9baf1a7SKazutaka Miyasaka } 1580d9baf1a7SKazutaka Miyasaka 1581*3dd5c225SAndreas Gohr return $this->_media( 1582*3dd5c225SAndreas Gohr $img['src'], 15834826ab45Sandi $img['title'], 15844826ab45Sandi $img['align'], 15854826ab45Sandi $img['width'], 15864826ab45Sandi $img['height'], 1587*3dd5c225SAndreas Gohr $img['cache'] 1588*3dd5c225SAndreas Gohr ); 15890cecf9d5Sandi } 1590b739ff0fSPierre Spring 1591b739ff0fSPierre Spring /** 1592*3dd5c225SAndreas Gohr * helperfunction to return a basic link to a media 1593*3dd5c225SAndreas Gohr * 1594*3dd5c225SAndreas Gohr * used in internalmedia() and externalmedia() 1595b739ff0fSPierre Spring * 1596b739ff0fSPierre Spring * @author Pierre Spring <pierre.spring@liip.ch> 1597*3dd5c225SAndreas Gohr * @param string $src media ID 1598*3dd5c225SAndreas Gohr * @param string $title descriptive text 1599*3dd5c225SAndreas Gohr * @param string $align left|center|right 1600*3dd5c225SAndreas Gohr * @param int $width width of media in pixel 1601*3dd5c225SAndreas Gohr * @param int $height height of media in pixel 1602*3dd5c225SAndreas Gohr * @param string $cache cache|recache|nocache 1603*3dd5c225SAndreas Gohr * @param bool $render should the media be embedded inline or just linked 1604*3dd5c225SAndreas Gohr * @return array associative array with link config 1605b739ff0fSPierre Spring */ 1606d91ab76fSMatt Perry function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render) { 1607b739ff0fSPierre Spring global $conf; 1608b739ff0fSPierre Spring 1609b739ff0fSPierre Spring $link = array(); 1610b739ff0fSPierre Spring $link['class'] = 'media'; 1611b739ff0fSPierre Spring $link['style'] = ''; 1612b739ff0fSPierre Spring $link['pre'] = ''; 1613b739ff0fSPierre Spring $link['suf'] = ''; 1614b739ff0fSPierre Spring $link['more'] = ''; 1615b739ff0fSPierre Spring $link['target'] = $conf['target']['media']; 1616b739ff0fSPierre Spring $link['title'] = $this->_xmlEntities($src); 1617b739ff0fSPierre Spring $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render); 1618b739ff0fSPierre Spring 1619b739ff0fSPierre Spring return $link; 1620b739ff0fSPierre Spring } 162191459163SAnika Henke 16222a2a2ba2SAnika Henke /** 16232a2a2ba2SAnika Henke * Embed video(s) in HTML 16242a2a2ba2SAnika Henke * 16252a2a2ba2SAnika Henke * @author Anika Henke <anika@selfthinker.org> 16262a2a2ba2SAnika Henke * 16272a2a2ba2SAnika Henke * @param string $src - ID of video to embed 16282a2a2ba2SAnika Henke * @param int $width - width of the video in pixels 16292a2a2ba2SAnika Henke * @param int $height - height of the video in pixels 16302a2a2ba2SAnika Henke * @param array $atts - additional attributes for the <video> tag 1631f50634f0SAnika Henke * @return string 16322a2a2ba2SAnika Henke */ 163379e53fe5SAnika Henke function _video($src, $width, $height, $atts = null) { 16342a2a2ba2SAnika Henke // prepare width and height 16352a2a2ba2SAnika Henke if(is_null($atts)) $atts = array(); 16362a2a2ba2SAnika Henke $atts['width'] = (int) $width; 16372a2a2ba2SAnika Henke $atts['height'] = (int) $height; 16382a2a2ba2SAnika Henke if(!$atts['width']) $atts['width'] = 320; 16392a2a2ba2SAnika Henke if(!$atts['height']) $atts['height'] = 240; 16402a2a2ba2SAnika Henke 16413d7a9e0aSAnika Henke // prepare alternative formats 16423d7a9e0aSAnika Henke $extensions = array('webm', 'ogv', 'mp4'); 16433d7a9e0aSAnika Henke $alternatives = media_alternativefiles($src, $extensions); 164499f943f6SAnika Henke $poster = media_alternativefiles($src, array('jpg', 'png'), true); 164599f943f6SAnika Henke $posterUrl = ''; 164699f943f6SAnika Henke if(!empty($poster)) { 16472d338eabSAndreas Gohr $posterUrl = ml(reset($poster), '', true, '&'); 164899f943f6SAnika Henke } 16492a2a2ba2SAnika Henke 1650f50634f0SAnika Henke $out = ''; 165179e53fe5SAnika Henke // open video tag 1652f50634f0SAnika Henke $out .= '<video '.buildAttributes($atts).' controls="controls"'; 16533641199aSAnika Henke if($posterUrl) $out .= ' poster="'.hsc($posterUrl).'"'; 1654f50634f0SAnika Henke $out .= '>'.NL; 16553641199aSAnika Henke $fallback = ''; 165679e53fe5SAnika Henke 165779e53fe5SAnika Henke // output source for each alternative video format 165879e53fe5SAnika Henke foreach($alternatives as $mime => $file) { 16592d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 166017954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 16613d7a9e0aSAnika Henke 1662f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 166379e53fe5SAnika Henke // alternative content (just a link to the file) 1664*3dd5c225SAndreas Gohr $fallback .= $this->internalmedia($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 16653d7a9e0aSAnika Henke } 16662a2a2ba2SAnika Henke 16672a2a2ba2SAnika Henke // finish 16683641199aSAnika Henke $out .= $fallback; 1669f50634f0SAnika Henke $out .= '</video>'.NL; 1670f50634f0SAnika Henke return $out; 16712a2a2ba2SAnika Henke } 16722a2a2ba2SAnika Henke 1673b44a5dceSAnika Henke /** 1674b44a5dceSAnika Henke * Embed audio in HTML 1675b44a5dceSAnika Henke * 1676b44a5dceSAnika Henke * @author Anika Henke <anika@selfthinker.org> 1677b44a5dceSAnika Henke * 1678b44a5dceSAnika Henke * @param string $src - ID of audio to embed 16796d4af72aSAnika Henke * @param array $atts - additional attributes for the <audio> tag 1680f50634f0SAnika Henke * @return string 1681b44a5dceSAnika Henke */ 1682b44a5dceSAnika Henke function _audio($src, $atts = null) { 1683b44a5dceSAnika Henke 1684b44a5dceSAnika Henke // prepare alternative formats 1685b44a5dceSAnika Henke $extensions = array('ogg', 'mp3', 'wav'); 1686b44a5dceSAnika Henke $alternatives = media_alternativefiles($src, $extensions); 1687b44a5dceSAnika Henke 1688f50634f0SAnika Henke $out = ''; 1689b44a5dceSAnika Henke // open audio tag 1690f50634f0SAnika Henke $out .= '<audio '.buildAttributes($atts).' controls="controls">'.NL; 16913641199aSAnika Henke $fallback = ''; 1692b44a5dceSAnika Henke 1693b44a5dceSAnika Henke // output source for each alternative audio format 1694b44a5dceSAnika Henke foreach($alternatives as $mime => $file) { 16952d338eabSAndreas Gohr $url = ml($file, '', true, '&'); 169617954bb5SAnika Henke $title = $atts['title'] ? $atts['title'] : $this->_xmlEntities(utf8_basename(noNS($file))); 1697b44a5dceSAnika Henke 1698f50634f0SAnika Henke $out .= '<source src="'.hsc($url).'" type="'.$mime.'" />'.NL; 1699b44a5dceSAnika Henke // alternative content (just a link to the file) 1700*3dd5c225SAndreas Gohr $fallback .= $this->internalmedia($file, $title, null, null, null, $cache = null, $linking = 'linkonly', $return = true); 1701b44a5dceSAnika Henke } 1702b44a5dceSAnika Henke 1703b44a5dceSAnika Henke // finish 17043641199aSAnika Henke $out .= $fallback; 1705f50634f0SAnika Henke $out .= '</audio>'.NL; 1706f50634f0SAnika Henke return $out; 1707b44a5dceSAnika Henke } 1708b44a5dceSAnika Henke 1709*3dd5c225SAndreas Gohr #endregion 17100cecf9d5Sandi} 17110cecf9d5Sandi 1712e3776c06SMichael Hamann//Setup VIM: ex: et ts=4 : 1713