source: documentation/trunk/packages/dokuwiki-2011-05-25a/inc/parser/xhtml.php@ 25027

Last change on this file since 25027 was 25027, checked in by jmt12, 12 years ago

Adding the packages directory, and within it a configured version of dokuwiki all ready to run

File size: 38.1 KB
Line 
1<?php
2/**
3 * Renderer for XHTML output
4 *
5 * @author Harry Fuecks <[email protected]>
6 * @author Andreas Gohr <[email protected]>
7 */
8if(!defined('DOKU_INC')) die('meh.');
9
10if ( !defined('DOKU_LF') ) {
11 // Some whitespace to help View > Source
12 define ('DOKU_LF',"\n");
13}
14
15if ( !defined('DOKU_TAB') ) {
16 // Some whitespace to help View > Source
17 define ('DOKU_TAB',"\t");
18}
19
20require_once DOKU_INC . 'inc/parser/renderer.php';
21require_once DOKU_INC . 'inc/html.php';
22
23/**
24 * The Renderer
25 */
26class Doku_Renderer_xhtml extends Doku_Renderer {
27
28 // @access public
29 var $doc = ''; // will contain the whole document
30 var $toc = array(); // will contain the Table of Contents
31
32 private $sectionedits = array(); // A stack of section edit data
33
34 var $headers = array();
35 var $footnotes = array();
36 var $lastlevel = 0;
37 var $node = array(0,0,0,0,0);
38 var $store = '';
39
40 var $_counter = array(); // used as global counter, introduced for table classes
41 var $_codeblock = 0; // counts the code and file blocks, used to provide download links
42
43 /**
44 * Register a new edit section range
45 *
46 * @param $type string The section type identifier
47 * @param $title string The section title
48 * @param $start int The byte position for the edit start
49 * @return string A marker class for the starting HTML element
50 * @author Adrian Lang <[email protected]>
51 */
52 public function startSectionEdit($start, $type, $title = null) {
53 static $lastsecid = 0;
54 $this->sectionedits[] = array(++$lastsecid, $start, $type, $title);
55 return 'sectionedit' . $lastsecid;
56 }
57
58 /**
59 * Finish an edit section range
60 *
61 * @param $end int The byte position for the edit end; null for the rest of
62 the page
63 * @author Adrian Lang <[email protected]>
64 */
65 public function finishSectionEdit($end = null) {
66 list($id, $start, $type, $title) = array_pop($this->sectionedits);
67 if (!is_null($end) && $end <= $start) {
68 return;
69 }
70 $this->doc .= "<!-- EDIT$id " . strtoupper($type) . ' ';
71 if (!is_null($title)) {
72 $this->doc .= '"' . str_replace('"', '', $title) . '" ';
73 }
74 $this->doc .= "[$start-" . (is_null($end) ? '' : $end) . '] -->';
75 }
76
77 function getFormat(){
78 return 'xhtml';
79 }
80
81
82 function document_start() {
83 //reset some internals
84 $this->toc = array();
85 $this->headers = array();
86 }
87
88 function document_end() {
89 // Finish open section edits.
90 while (count($this->sectionedits) > 0) {
91 if ($this->sectionedits[count($this->sectionedits) - 1][1] <= 1) {
92 // If there is only one section, do not write a section edit
93 // marker.
94 array_pop($this->sectionedits);
95 } else {
96 $this->finishSectionEdit();
97 }
98 }
99
100 if ( count ($this->footnotes) > 0 ) {
101 $this->doc .= '<div class="footnotes">'.DOKU_LF;
102
103 $id = 0;
104 foreach ( $this->footnotes as $footnote ) {
105 $id++; // the number of the current footnote
106
107 // check its not a placeholder that indicates actual footnote text is elsewhere
108 if (substr($footnote, 0, 5) != "@@FNT") {
109
110 // open the footnote and set the anchor and backlink
111 $this->doc .= '<div class="fn">';
112 $this->doc .= '<sup><a href="#fnt__'.$id.'" id="fn__'.$id.'" name="fn__'.$id.'" class="fn_bot">';
113 $this->doc .= $id.')</a></sup> '.DOKU_LF;
114
115 // get any other footnotes that use the same markup
116 $alt = array_keys($this->footnotes, "@@FNT$id");
117
118 if (count($alt)) {
119 foreach ($alt as $ref) {
120 // set anchor and backlink for the other footnotes
121 $this->doc .= ', <sup><a href="#fnt__'.($ref+1).'" id="fn__'.($ref+1).'" name="fn__'.($ref+1).'" class="fn_bot">';
122 $this->doc .= ($ref+1).')</a></sup> '.DOKU_LF;
123 }
124 }
125
126 // add footnote markup and close this footnote
127 $this->doc .= $footnote;
128 $this->doc .= '</div>' . DOKU_LF;
129 }
130 }
131 $this->doc .= '</div>'.DOKU_LF;
132 }
133
134 // Prepare the TOC
135 global $conf;
136 if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']){
137 global $TOC;
138 $TOC = $this->toc;
139 }
140
141 // make sure there are no empty paragraphs
142 $this->doc = preg_replace('#<p>\s*</p>#','',$this->doc);
143 }
144
145 function toc_additem($id, $text, $level) {
146 global $conf;
147
148 //handle TOC
149 if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){
150 $this->toc[] = html_mktocitem($id, $text, $level-$conf['toptoclevel']+1);
151 }
152 }
153
154 function header($text, $level, $pos) {
155 global $conf;
156
157 if(!$text) return; //skip empty headlines
158
159 $hid = $this->_headerToLink($text,true);
160
161 //only add items within configured levels
162 $this->toc_additem($hid, $text, $level);
163
164 // adjust $node to reflect hierarchy of levels
165 $this->node[$level-1]++;
166 if ($level < $this->lastlevel) {
167 for ($i = 0; $i < $this->lastlevel-$level; $i++) {
168 $this->node[$this->lastlevel-$i-1] = 0;
169 }
170 }
171 $this->lastlevel = $level;
172
173 if ($level <= $conf['maxseclevel'] &&
174 count($this->sectionedits) > 0 &&
175 $this->sectionedits[count($this->sectionedits) - 1][2] === 'section') {
176 $this->finishSectionEdit($pos - 1);
177 }
178
179 // write the header
180 $this->doc .= DOKU_LF.'<h'.$level;
181 if ($level <= $conf['maxseclevel']) {
182 $this->doc .= ' class="' . $this->startSectionEdit($pos, 'section', $text) . '"';
183 }
184 $this->doc .= '><a name="'.$hid.'" id="'.$hid.'">';
185 $this->doc .= $this->_xmlEntities($text);
186 $this->doc .= "</a></h$level>".DOKU_LF;
187 }
188
189 function section_open($level) {
190 $this->doc .= '<div class="level' . $level . '">' . DOKU_LF;
191 }
192
193 function section_close() {
194 $this->doc .= DOKU_LF.'</div>'.DOKU_LF;
195 }
196
197 function cdata($text) {
198 $this->doc .= $this->_xmlEntities($text);
199 }
200
201 function p_open() {
202 $this->doc .= DOKU_LF.'<p>'.DOKU_LF;
203 }
204
205 function p_close() {
206 $this->doc .= DOKU_LF.'</p>'.DOKU_LF;
207 }
208
209 function linebreak() {
210 $this->doc .= '<br/>'.DOKU_LF;
211 }
212
213 function hr() {
214 $this->doc .= '<hr />'.DOKU_LF;
215 }
216
217 function strong_open() {
218 $this->doc .= '<strong>';
219 }
220
221 function strong_close() {
222 $this->doc .= '</strong>';
223 }
224
225 function emphasis_open() {
226 $this->doc .= '<em>';
227 }
228
229 function emphasis_close() {
230 $this->doc .= '</em>';
231 }
232
233 function underline_open() {
234 $this->doc .= '<em class="u">';
235 }
236
237 function underline_close() {
238 $this->doc .= '</em>';
239 }
240
241 function monospace_open() {
242 $this->doc .= '<code>';
243 }
244
245 function monospace_close() {
246 $this->doc .= '</code>';
247 }
248
249 function subscript_open() {
250 $this->doc .= '<sub>';
251 }
252
253 function subscript_close() {
254 $this->doc .= '</sub>';
255 }
256
257 function superscript_open() {
258 $this->doc .= '<sup>';
259 }
260
261 function superscript_close() {
262 $this->doc .= '</sup>';
263 }
264
265 function deleted_open() {
266 $this->doc .= '<del>';
267 }
268
269 function deleted_close() {
270 $this->doc .= '</del>';
271 }
272
273 /**
274 * Callback for footnote start syntax
275 *
276 * All following content will go to the footnote instead of
277 * the document. To achieve this the previous rendered content
278 * is moved to $store and $doc is cleared
279 *
280 * @author Andreas Gohr <[email protected]>
281 */
282 function footnote_open() {
283
284 // move current content to store and record footnote
285 $this->store = $this->doc;
286 $this->doc = '';
287 }
288
289 /**
290 * Callback for footnote end syntax
291 *
292 * All rendered content is moved to the $footnotes array and the old
293 * content is restored from $store again
294 *
295 * @author Andreas Gohr
296 */
297 function footnote_close() {
298
299 // recover footnote into the stack and restore old content
300 $footnote = $this->doc;
301 $this->doc = $this->store;
302 $this->store = '';
303
304 // check to see if this footnote has been seen before
305 $i = array_search($footnote, $this->footnotes);
306
307 if ($i === false) {
308 // its a new footnote, add it to the $footnotes array
309 $id = count($this->footnotes)+1;
310 $this->footnotes[count($this->footnotes)] = $footnote;
311 } else {
312 // seen this one before, translate the index to an id and save a placeholder
313 $i++;
314 $id = count($this->footnotes)+1;
315 $this->footnotes[count($this->footnotes)] = "@@FNT".($i);
316 }
317
318 // output the footnote reference and link
319 $this->doc .= '<sup><a href="#fn__'.$id.'" name="fnt__'.$id.'" id="fnt__'.$id.'" class="fn_top">'.$id.')</a></sup>';
320 }
321
322 function listu_open() {
323 $this->doc .= '<ul>'.DOKU_LF;
324 }
325
326 function listu_close() {
327 $this->doc .= '</ul>'.DOKU_LF;
328 }
329
330 function listo_open() {
331 $this->doc .= '<ol>'.DOKU_LF;
332 }
333
334 function listo_close() {
335 $this->doc .= '</ol>'.DOKU_LF;
336 }
337
338 function listitem_open($level) {
339 $this->doc .= '<li class="level'.$level.'">';
340 }
341
342 function listitem_close() {
343 $this->doc .= '</li>'.DOKU_LF;
344 }
345
346 function listcontent_open() {
347 $this->doc .= '<div class="li">';
348 }
349
350 function listcontent_close() {
351 $this->doc .= '</div>'.DOKU_LF;
352 }
353
354 function unformatted($text) {
355 $this->doc .= $this->_xmlEntities($text);
356 }
357
358 /**
359 * Execute PHP code if allowed
360 *
361 * @param string $wrapper html element to wrap result if $conf['phpok'] is okff
362 *
363 * @author Andreas Gohr <[email protected]>
364 */
365 function php($text, $wrapper='code') {
366 global $conf;
367
368 if($conf['phpok']){
369 ob_start();
370 eval($text);
371 $this->doc .= ob_get_contents();
372 ob_end_clean();
373 } else {
374 $this->doc .= p_xhtml_cached_geshi($text, 'php', $wrapper);
375 }
376 }
377
378 function phpblock($text) {
379 $this->php($text, 'pre');
380 }
381
382 /**
383 * Insert HTML if allowed
384 *
385 * @param string $wrapper html element to wrap result if $conf['htmlok'] is okff
386 *
387 * @author Andreas Gohr <[email protected]>
388 */
389 function html($text, $wrapper='code') {
390 global $conf;
391
392 if($conf['htmlok']){
393 $this->doc .= $text;
394 } else {
395 $this->doc .= p_xhtml_cached_geshi($text, 'html4strict', $wrapper);
396 }
397 }
398
399 function htmlblock($text) {
400 $this->html($text, 'pre');
401 }
402
403 function quote_open() {
404 $this->doc .= '<blockquote><div class="no">'.DOKU_LF;
405 }
406
407 function quote_close() {
408 $this->doc .= '</div></blockquote>'.DOKU_LF;
409 }
410
411 function preformatted($text) {
412 $this->doc .= '<pre class="code">' . trim($this->_xmlEntities($text),"\n\r") . '</pre>'. DOKU_LF;
413 }
414
415 function file($text, $language=null, $filename=null) {
416 $this->_highlight('file',$text,$language,$filename);
417 }
418
419 function code($text, $language=null, $filename=null) {
420 $this->_highlight('code',$text,$language,$filename);
421 }
422
423 /**
424 * Use GeSHi to highlight language syntax in code and file blocks
425 *
426 * @author Andreas Gohr <[email protected]>
427 */
428 function _highlight($type, $text, $language=null, $filename=null) {
429 global $conf;
430 global $ID;
431 global $lang;
432
433 if($filename){
434 // add icon
435 list($ext) = mimetype($filename,false);
436 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
437 $class = 'mediafile mf_'.$class;
438
439 $this->doc .= '<dl class="'.$type.'">'.DOKU_LF;
440 $this->doc .= '<dt><a href="'.exportlink($ID,'code',array('codeblock'=>$this->_codeblock)).'" title="'.$lang['download'].'" class="'.$class.'">';
441 $this->doc .= hsc($filename);
442 $this->doc .= '</a></dt>'.DOKU_LF.'<dd>';
443 }
444
445 if ($text{0} == "\n") {
446 $text = substr($text, 1);
447 }
448 if (substr($text, -1) == "\n") {
449 $text = substr($text, 0, -1);
450 }
451
452 // - code HTML is now temporarily stored in a local string so we can
453 // restore specific HTML comments [jmt12]
454 $code_text = '';
455 if ( is_null($language) ) {
456 $code_text = '<pre class="'.$type.'">'.$this->_xmlEntities($text).'</pre>'.DOKU_LF; // [jmt12]
457 } else {
458 $class = 'code'; //we always need the code class to make the syntax highlighting apply
459 if($type != 'code') $class .= ' '.$type;
460
461 $code_text = "<pre class=\"$class $language\">".p_xhtml_cached_geshi($text, $language, '').'</pre>'.DOKU_LF; // [jmt12]
462 }
463 // - restore id comments! [jmt12]
464 $code_text = preg_replace('/\&lt\;\!\-\-\s+id\:([^\s]+)\s+\-\-\&gt\;/','<!-- id:\1 -->', $code_text);
465 $this->doc .= $code_text; // [jmt12]
466
467 if($filename){
468 $this->doc .= '</dd></dl>'.DOKU_LF;
469 }
470 $this->_codeblock++;
471 }
472
473 function acronym($acronym) {
474
475 if ( array_key_exists($acronym, $this->acronyms) ) {
476
477 $title = $this->_xmlEntities($this->acronyms[$acronym]);
478
479 $this->doc .= '<acronym title="'.$title
480 .'">'.$this->_xmlEntities($acronym).'</acronym>';
481
482 } else {
483 $this->doc .= $this->_xmlEntities($acronym);
484 }
485 }
486
487 function smiley($smiley) {
488 if ( array_key_exists($smiley, $this->smileys) ) {
489 $title = $this->_xmlEntities($this->smileys[$smiley]);
490 $this->doc .= '<img src="'.DOKU_BASE.'lib/images/smileys/'.$this->smileys[$smiley].
491 '" class="middle" alt="'.
492 $this->_xmlEntities($smiley).'" />';
493 } else {
494 $this->doc .= $this->_xmlEntities($smiley);
495 }
496 }
497
498 /*
499 * not used
500 function wordblock($word) {
501 if ( array_key_exists($word, $this->badwords) ) {
502 $this->doc .= '** BLEEP **';
503 } else {
504 $this->doc .= $this->_xmlEntities($word);
505 }
506 }
507 */
508
509 function entity($entity) {
510 if ( array_key_exists($entity, $this->entities) ) {
511 $this->doc .= $this->entities[$entity];
512 } else {
513 $this->doc .= $this->_xmlEntities($entity);
514 }
515 }
516
517 function multiplyentity($x, $y) {
518 $this->doc .= "$x&times;$y";
519 }
520
521 function singlequoteopening() {
522 global $lang;
523 $this->doc .= $lang['singlequoteopening'];
524 }
525
526 function singlequoteclosing() {
527 global $lang;
528 $this->doc .= $lang['singlequoteclosing'];
529 }
530
531 function apostrophe() {
532 global $lang;
533 $this->doc .= $lang['apostrophe'];
534 }
535
536 function doublequoteopening() {
537 global $lang;
538 $this->doc .= $lang['doublequoteopening'];
539 }
540
541 function doublequoteclosing() {
542 global $lang;
543 $this->doc .= $lang['doublequoteclosing'];
544 }
545
546 /**
547 */
548 function camelcaselink($link) {
549 $this->internallink($link,$link);
550 }
551
552
553 function locallink($hash, $name = NULL){
554 global $ID;
555 $name = $this->_getLinkTitle($name, $hash, $isImage);
556 $hash = $this->_headerToLink($hash);
557 $title = $ID.' &crarr;';
558 $this->doc .= '<a href="#'.$hash.'" title="'.$title.'" class="wikilink1">';
559 $this->doc .= $name;
560 $this->doc .= '</a>';
561 }
562
563 /**
564 * Render an internal Wiki Link
565 *
566 * $search,$returnonly & $linktype are not for the renderer but are used
567 * elsewhere - no need to implement them in other renderers
568 *
569 * @author Andreas Gohr <[email protected]>
570 */
571 function internallink($id, $name = NULL, $search=NULL,$returnonly=false,$linktype='content') {
572 global $conf;
573 global $ID;
574
575 $params = '';
576 $parts = explode('?', $id, 2);
577 if (count($parts) === 2) {
578 $id = $parts[0];
579 $params = $parts[1];
580 }
581
582 // For empty $id we need to know the current $ID
583 // We need this check because _simpleTitle needs
584 // correct $id and resolve_pageid() use cleanID($id)
585 // (some things could be lost)
586 if ($id === '') {
587 $id = $ID;
588 }
589
590 // default name is based on $id as given
591 $default = $this->_simpleTitle($id);
592
593 // now first resolve and clean up the $id
594 resolve_pageid(getNS($ID),$id,$exists);
595
596 $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype);
597 if ( !$isImage ) {
598 if ( $exists ) {
599 $class='wikilink1';
600 } else {
601 $class='wikilink2';
602 $link['rel']='nofollow';
603 }
604 } else {
605 $class='media';
606 }
607
608 //keep hash anchor
609 list($id,$hash) = explode('#',$id,2);
610 if(!empty($hash)) $hash = $this->_headerToLink($hash);
611
612 //prepare for formating
613 $link['target'] = $conf['target']['wiki'];
614 $link['style'] = '';
615 $link['pre'] = '';
616 $link['suf'] = '';
617 // highlight link to current page
618 if ($id == $ID) {
619 $link['pre'] = '<span class="curid">';
620 $link['suf'] = '</span>';
621 }
622 $link['more'] = '';
623 $link['class'] = $class;
624 $link['url'] = wl($id, $params);
625 $link['name'] = $name;
626 $link['title'] = $id;
627 //add search string
628 if($search){
629 ($conf['userewrite']) ? $link['url'].='?' : $link['url'].='&amp;';
630 if(is_array($search)){
631 $search = array_map('rawurlencode',$search);
632 $link['url'] .= 's[]='.join('&amp;s[]=',$search);
633 }else{
634 $link['url'] .= 's='.rawurlencode($search);
635 }
636 }
637
638 //keep hash
639 if($hash) $link['url'].='#'.$hash;
640
641 //output formatted
642 if($returnonly){
643 return $this->_formatLink($link);
644 }else{
645 $this->doc .= $this->_formatLink($link);
646 }
647 }
648
649 function externallink($url, $name = NULL) {
650 global $conf;
651
652 $name = $this->_getLinkTitle($name, $url, $isImage);
653
654 // url might be an attack vector, only allow registered protocols
655 if(is_null($this->schemes)) $this->schemes = getSchemes();
656 list($scheme) = explode('://',$url);
657 $scheme = strtolower($scheme);
658 if(!in_array($scheme,$this->schemes)) $url = '';
659
660 // is there still an URL?
661 if(!$url){
662 $this->doc .= $name;
663 return;
664 }
665
666 // set class
667 if ( !$isImage ) {
668 $class='urlextern';
669 } else {
670 $class='media';
671 }
672
673 // [jmt12] Replace the macro ~~localhost~~ with the hostname (and port
674 // number if necessary) of the current host
675 if (preg_match('/^http:\/\/~~baseurl~~(\/.*)$/', $url, $matches))
676 {
677 $host = $_SERVER['HTTP_HOST'];
678 $path = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/'));
679 $url = 'http://' . $host . $path . $matches[1];
680 }
681 // [jmt12]
682
683 //prepare for formating
684 $link['target'] = $conf['target']['extern'];
685 $link['style'] = '';
686 $link['pre'] = '';
687 $link['suf'] = '';
688 $link['more'] = '';
689 $link['class'] = $class;
690 $link['url'] = $url;
691
692 $link['name'] = $name;
693 $link['title'] = $this->_xmlEntities($url);
694 if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"';
695
696 //output formatted
697 $this->doc .= $this->_formatLink($link);
698 }
699
700 /**
701 */
702 function interwikilink($match, $name = NULL, $wikiName, $wikiUri) {
703 global $conf;
704
705 $link = array();
706 $link['target'] = $conf['target']['interwiki'];
707 $link['pre'] = '';
708 $link['suf'] = '';
709 $link['more'] = '';
710 $link['name'] = $this->_getLinkTitle($name, $wikiUri, $isImage);
711
712 //get interwiki URL
713 $url = $this->_resolveInterWiki($wikiName,$wikiUri);
714
715 if ( !$isImage ) {
716 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$wikiName);
717 $link['class'] = "interwiki iw_$class";
718 } else {
719 $link['class'] = 'media';
720 }
721
722 //do we stay at the same server? Use local target
723 if( strpos($url,DOKU_URL) === 0 ){
724 $link['target'] = $conf['target']['wiki'];
725 }
726
727 $link['url'] = $url;
728 $link['title'] = htmlspecialchars($link['url']);
729
730 //output formatted
731 $this->doc .= $this->_formatLink($link);
732 }
733
734 /**
735 */
736 function windowssharelink($url, $name = NULL) {
737 global $conf;
738 global $lang;
739 //simple setup
740 $link['target'] = $conf['target']['windows'];
741 $link['pre'] = '';
742 $link['suf'] = '';
743 $link['style'] = '';
744
745 $link['name'] = $this->_getLinkTitle($name, $url, $isImage);
746 if ( !$isImage ) {
747 $link['class'] = 'windows';
748 } else {
749 $link['class'] = 'media';
750 }
751
752
753 $link['title'] = $this->_xmlEntities($url);
754 $url = str_replace('\\','/',$url);
755 $url = 'file:///'.$url;
756 $link['url'] = $url;
757
758 //output formatted
759 $this->doc .= $this->_formatLink($link);
760 }
761
762 function emaillink($address, $name = NULL) {
763 global $conf;
764 //simple setup
765 $link = array();
766 $link['target'] = '';
767 $link['pre'] = '';
768 $link['suf'] = '';
769 $link['style'] = '';
770 $link['more'] = '';
771
772 $name = $this->_getLinkTitle($name, '', $isImage);
773 if ( !$isImage ) {
774 $link['class']='mail';
775 } else {
776 $link['class']='media';
777 }
778
779 $address = $this->_xmlEntities($address);
780 $address = obfuscate($address);
781 $title = $address;
782
783 if(empty($name)){
784 $name = $address;
785 }
786
787 if($conf['mailguard'] == 'visible') $address = rawurlencode($address);
788
789 $link['url'] = 'mailto:'.$address;
790 $link['name'] = $name;
791 $link['title'] = $title;
792
793 //output formatted
794 $this->doc .= $this->_formatLink($link);
795 }
796
797 function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
798 $height=NULL, $cache=NULL, $linking=NULL) {
799 global $ID;
800 list($src,$hash) = explode('#',$src,2);
801 resolve_mediaid(getNS($ID),$src, $exists);
802
803 $noLink = false;
804 $render = ($linking == 'linkonly') ? false : true;
805 $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
806
807 list($ext,$mime,$dl) = mimetype($src,false);
808 if(substr($mime,0,5) == 'image' && $render){
809 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),($linking=='direct'));
810 }elseif($mime == 'application/x-shockwave-flash' && $render){
811 // don't link flash movies
812 $noLink = true;
813 }else{
814 // add file icons
815 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
816 $link['class'] .= ' mediafile mf_'.$class;
817 $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true);
818 }
819
820 if($hash) $link['url'] .= '#'.$hash;
821
822 //markup non existing files
823 if (!$exists)
824 $link['class'] .= ' wikilink2';
825
826 //output formatted
827 if ($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
828 else $this->doc .= $this->_formatLink($link);
829 }
830
831 function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
832 $height=NULL, $cache=NULL, $linking=NULL) {
833 list($src,$hash) = explode('#',$src,2);
834 $noLink = false;
835 $render = ($linking == 'linkonly') ? false : true;
836 $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render);
837
838 $link['url'] = ml($src,array('cache'=>$cache));
839
840 list($ext,$mime,$dl) = mimetype($src,false);
841 if(substr($mime,0,5) == 'image' && $render){
842 // link only jpeg images
843 // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = true;
844 }elseif($mime == 'application/x-shockwave-flash' && $render){
845 // don't link flash movies
846 $noLink = true;
847 }else{
848 // add file icons
849 $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
850 $link['class'] .= ' mediafile mf_'.$class;
851 }
852
853 if($hash) $link['url'] .= '#'.$hash;
854
855 //output formatted
856 if ($linking == 'nolink' || $noLink) $this->doc .= $link['name'];
857 else $this->doc .= $this->_formatLink($link);
858 }
859
860 /**
861 * Renders an RSS feed
862 *
863 * @author Andreas Gohr <[email protected]>
864 */
865 function rss ($url,$params){
866 global $lang;
867 global $conf;
868
869 require_once(DOKU_INC.'inc/FeedParser.php');
870 $feed = new FeedParser();
871 $feed->set_feed_url($url);
872
873 //disable warning while fetching
874 if (!defined('DOKU_E_LEVEL')) { $elvl = error_reporting(E_ERROR); }
875 $rc = $feed->init();
876 if (!defined('DOKU_E_LEVEL')) { error_reporting($elvl); }
877
878 //decide on start and end
879 if($params['reverse']){
880 $mod = -1;
881 $start = $feed->get_item_quantity()-1;
882 $end = $start - ($params['max']);
883 $end = ($end < -1) ? -1 : $end;
884 }else{
885 $mod = 1;
886 $start = 0;
887 $end = $feed->get_item_quantity();
888 $end = ($end > $params['max']) ? $params['max'] : $end;;
889 }
890
891 $this->doc .= '<ul class="rss">';
892 if($rc){
893 for ($x = $start; $x != $end; $x += $mod) {
894 $item = $feed->get_item($x);
895 $this->doc .= '<li><div class="li">';
896 // support feeds without links
897 $lnkurl = $item->get_permalink();
898 if($lnkurl){
899 // title is escaped by SimplePie, we unescape here because it
900 // is escaped again in externallink() FS#1705
901 $this->externallink($item->get_permalink(),
902 htmlspecialchars_decode($item->get_title()));
903 }else{
904 $this->doc .= ' '.$item->get_title();
905 }
906 if($params['author']){
907 $author = $item->get_author(0);
908 if($author){
909 $name = $author->get_name();
910 if(!$name) $name = $author->get_email();
911 if($name) $this->doc .= ' '.$lang['by'].' '.$name;
912 }
913 }
914 if($params['date']){
915 $this->doc .= ' ('.$item->get_local_date($conf['dformat']).')';
916 }
917 if($params['details']){
918 $this->doc .= '<div class="detail">';
919 if($conf['htmlok']){
920 $this->doc .= $item->get_description();
921 }else{
922 $this->doc .= strip_tags($item->get_description());
923 }
924 $this->doc .= '</div>';
925 }
926
927 $this->doc .= '</div></li>';
928 }
929 }else{
930 $this->doc .= '<li><div class="li">';
931 $this->doc .= '<em>'.$lang['rssfailed'].'</em>';
932 $this->externallink($url);
933 if($conf['allowdebug']){
934 $this->doc .= '<!--'.hsc($feed->error).'-->';
935 }
936 $this->doc .= '</div></li>';
937 }
938 $this->doc .= '</ul>';
939 }
940
941 // $numrows not yet implemented
942 function table_open($maxcols = null, $numrows = null, $pos = null){
943 global $lang;
944 // initialize the row counter used for classes
945 $this->_counter['row_counter'] = 0;
946 $class = 'table';
947 if ($pos !== null) {
948 $class .= ' ' . $this->startSectionEdit($pos, 'table');
949 }
950 $this->doc .= '<div class="' . $class . '"><table class="inline">' .
951 DOKU_LF;
952 }
953
954 function table_close($pos = null){
955 $this->doc .= '</table></div>'.DOKU_LF;
956 if ($pos !== null) {
957 $this->finishSectionEdit($pos);
958 }
959 }
960
961 function tablerow_open(){
962 // initialize the cell counter used for classes
963 $this->_counter['cell_counter'] = 0;
964 $class = 'row' . $this->_counter['row_counter']++;
965 $this->doc .= DOKU_TAB . '<tr class="'.$class.'">' . DOKU_LF . DOKU_TAB . DOKU_TAB;
966 }
967
968 function tablerow_close(){
969 $this->doc .= DOKU_LF . DOKU_TAB . '</tr>' . DOKU_LF;
970 }
971
972 function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1){
973 $class = 'class="col' . $this->_counter['cell_counter']++;
974 if ( !is_null($align) ) {
975 $class .= ' '.$align.'align';
976 }
977 $class .= '"';
978 $this->doc .= '<th ' . $class;
979 if ( $colspan > 1 ) {
980 $this->_counter['cell_counter'] += $colspan-1;
981 $this->doc .= ' colspan="'.$colspan.'"';
982 }
983 if ( $rowspan > 1 ) {
984 $this->doc .= ' rowspan="'.$rowspan.'"';
985 }
986 $this->doc .= '>';
987 }
988
989 function tableheader_close(){
990 $this->doc .= '</th>';
991 }
992
993 function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1){
994 $class = 'class="col' . $this->_counter['cell_counter']++;
995 if ( !is_null($align) ) {
996 $class .= ' '.$align.'align';
997 }
998 $class .= '"';
999 $this->doc .= '<td '.$class;
1000 if ( $colspan > 1 ) {
1001 $this->_counter['cell_counter'] += $colspan-1;
1002 $this->doc .= ' colspan="'.$colspan.'"';
1003 }
1004 if ( $rowspan > 1 ) {
1005 $this->doc .= ' rowspan="'.$rowspan.'"';
1006 }
1007 $this->doc .= '>';
1008 }
1009
1010 function tablecell_close(){
1011 $this->doc .= '</td>';
1012 }
1013
1014 //----------------------------------------------------------
1015 // Utils
1016
1017 /**
1018 * Build a link
1019 *
1020 * Assembles all parts defined in $link returns HTML for the link
1021 *
1022 * @author Andreas Gohr <[email protected]>
1023 */
1024 function _formatLink($link){
1025 //make sure the url is XHTML compliant (skip mailto)
1026 if(substr($link['url'],0,7) != 'mailto:'){
1027 $link['url'] = str_replace('&','&amp;',$link['url']);
1028 $link['url'] = str_replace('&amp;amp;','&amp;',$link['url']);
1029 }
1030 //remove double encodings in titles
1031 $link['title'] = str_replace('&amp;amp;','&amp;',$link['title']);
1032
1033 // be sure there are no bad chars in url or title
1034 // (we can't do this for name because it can contain an img tag)
1035 $link['url'] = strtr($link['url'],array('>'=>'%3E','<'=>'%3C','"'=>'%22'));
1036 $link['title'] = strtr($link['title'],array('>'=>'&gt;','<'=>'&lt;','"'=>'&quot;'));
1037
1038 $ret = '';
1039 $ret .= $link['pre'];
1040 $ret .= '<a href="'.$link['url'].'"';
1041 if(!empty($link['class'])) $ret .= ' class="'.$link['class'].'"';
1042 if(!empty($link['target'])) $ret .= ' target="'.$link['target'].'"';
1043 if(!empty($link['title'])) $ret .= ' title="'.$link['title'].'"';
1044 if(!empty($link['style'])) $ret .= ' style="'.$link['style'].'"';
1045 if(!empty($link['rel'])) $ret .= ' rel="'.$link['rel'].'"';
1046 if(!empty($link['more'])) $ret .= ' '.$link['more'];
1047 $ret .= '>';
1048 $ret .= $link['name'];
1049 $ret .= '</a>';
1050 $ret .= $link['suf'];
1051 return $ret;
1052 }
1053
1054 /**
1055 * Renders internal and external media
1056 *
1057 * @author Andreas Gohr <[email protected]>
1058 */
1059 function _media ($src, $title=NULL, $align=NULL, $width=NULL,
1060 $height=NULL, $cache=NULL, $render = true) {
1061
1062 $ret = '';
1063
1064 list($ext,$mime,$dl) = mimetype($src);
1065 if(substr($mime,0,5) == 'image'){
1066 // first get the $title
1067 if (!is_null($title)) {
1068 $title = $this->_xmlEntities($title);
1069 }elseif($ext == 'jpg' || $ext == 'jpeg'){
1070 //try to use the caption from IPTC/EXIF
1071 require_once(DOKU_INC.'inc/JpegMeta.php');
1072 $jpeg =new JpegMeta(mediaFN($src));
1073 if($jpeg !== false) $cap = $jpeg->getTitle();
1074 if($cap){
1075 $title = $this->_xmlEntities($cap);
1076 }
1077 }
1078 if (!$render) {
1079 // if the picture is not supposed to be rendered
1080 // return the title of the picture
1081 if (!$title) {
1082 // just show the sourcename
1083 $title = $this->_xmlEntities(basename(noNS($src)));
1084 }
1085 return $title;
1086 }
1087 //add image tag
1088 $ret .= '<img src="'.ml($src,array('w'=>$width,'h'=>$height,'cache'=>$cache)).'"';
1089 $ret .= ' class="media'.$align.'"';
1090
1091 // make left/right alignment for no-CSS view work (feeds)
1092 if($align == 'right') $ret .= ' align="right"';
1093 if($align == 'left') $ret .= ' align="left"';
1094
1095 if ($title) {
1096 $ret .= ' title="' . $title . '"';
1097 $ret .= ' alt="' . $title .'"';
1098 }else{
1099 $ret .= ' alt=""';
1100 }
1101
1102 if ( !is_null($width) )
1103 $ret .= ' width="'.$this->_xmlEntities($width).'"';
1104
1105 if ( !is_null($height) )
1106 $ret .= ' height="'.$this->_xmlEntities($height).'"';
1107
1108 $ret .= ' />';
1109
1110 }elseif($mime == 'application/x-shockwave-flash'){
1111 if (!$render) {
1112 // if the flash is not supposed to be rendered
1113 // return the title of the flash
1114 if (!$title) {
1115 // just show the sourcename
1116 $title = basename(noNS($src));
1117 }
1118 return $this->_xmlEntities($title);
1119 }
1120
1121 $att = array();
1122 $att['class'] = "media$align";
1123 if($align == 'right') $att['align'] = 'right';
1124 if($align == 'left') $att['align'] = 'left';
1125 $ret .= html_flashobject(ml($src,array('cache'=>$cache),true,'&'),$width,$height,
1126 array('quality' => 'high'),
1127 null,
1128 $att,
1129 $this->_xmlEntities($title));
1130 }elseif($title){
1131 // well at least we have a title to display
1132 $ret .= $this->_xmlEntities($title);
1133 }else{
1134 // just show the sourcename
1135 $ret .= $this->_xmlEntities(basename(noNS($src)));
1136 }
1137
1138 return $ret;
1139 }
1140
1141 function _xmlEntities($string) {
1142 return htmlspecialchars($string,ENT_QUOTES,'UTF-8');
1143 }
1144
1145 /**
1146 * Creates a linkid from a headline
1147 *
1148 * @param string $title The headline title
1149 * @param boolean $create Create a new unique ID?
1150 * @author Andreas Gohr <[email protected]>
1151 */
1152 function _headerToLink($title,$create=false) {
1153 if($create){
1154 return sectionID($title,$this->headers);
1155 }else{
1156 $check = false;
1157 return sectionID($title,$check);
1158 }
1159 }
1160
1161 /**
1162 * Construct a title and handle images in titles
1163 *
1164 * @author Harry Fuecks <[email protected]>
1165 */
1166 function _getLinkTitle($title, $default, & $isImage, $id=NULL, $linktype='content') {
1167 global $conf;
1168
1169 $isImage = false;
1170 if ( is_array($title) ) {
1171 $isImage = true;
1172 return $this->_imageTitle($title);
1173 } elseif ( is_null($title) || trim($title)=='') {
1174 if (useHeading($linktype) && $id) {
1175 $heading = p_get_first_heading($id);
1176 if ($heading) {
1177 return $this->_xmlEntities($heading);
1178 }
1179 }
1180 return $this->_xmlEntities($default);
1181 } else {
1182 return $this->_xmlEntities($title);
1183 }
1184 }
1185
1186 /**
1187 * Returns an HTML code for images used in link titles
1188 *
1189 * @todo Resolve namespace on internal images
1190 * @author Andreas Gohr <[email protected]>
1191 */
1192 function _imageTitle($img) {
1193 global $ID;
1194
1195 // some fixes on $img['src']
1196 // see internalmedia() and externalmedia()
1197 list($img['src'],$hash) = explode('#',$img['src'],2);
1198 if ($img['type'] == 'internalmedia') {
1199 resolve_mediaid(getNS($ID),$img['src'],$exists);
1200 }
1201
1202 return $this->_media($img['src'],
1203 $img['title'],
1204 $img['align'],
1205 $img['width'],
1206 $img['height'],
1207 $img['cache']);
1208 }
1209
1210 /**
1211 * _getMediaLinkConf is a helperfunction to internalmedia() and externalmedia()
1212 * which returns a basic link to a media.
1213 *
1214 * @author Pierre Spring <[email protected]>
1215 * @param string $src
1216 * @param string $title
1217 * @param string $align
1218 * @param string $width
1219 * @param string $height
1220 * @param string $cache
1221 * @param string $render
1222 * @access protected
1223 * @return array
1224 */
1225 function _getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render)
1226 {
1227 global $conf;
1228
1229 $link = array();
1230 $link['class'] = 'media';
1231 $link['style'] = '';
1232 $link['pre'] = '';
1233 $link['suf'] = '';
1234 $link['more'] = '';
1235 $link['target'] = $conf['target']['media'];
1236 $link['title'] = $this->_xmlEntities($src);
1237 $link['name'] = $this->_media($src, $title, $align, $width, $height, $cache, $render);
1238
1239 return $link;
1240 }
1241
1242
1243}
1244
1245//Setup VIM: ex: et ts=4 :
Note: See TracBrowser for help on using the repository browser.