xref: /dokuwiki/inc/parser/metadata.php (revision 3daf9b20cab478b0c91d02f47cc3e0de195961ae)
1<?php
2/**
3 * Renderer for metadata
4 *
5 * @author Esther Brunner <wikidesign@gmail.com>
6 */
7if(!defined('DOKU_INC')) die('meh.');
8
9if ( !defined('DOKU_LF') ) {
10    // Some whitespace to help View > Source
11    define ('DOKU_LF',"\n");
12}
13
14if ( !defined('DOKU_TAB') ) {
15    // Some whitespace to help View > Source
16    define ('DOKU_TAB',"\t");
17}
18
19require_once DOKU_INC . 'inc/parser/renderer.php';
20
21/**
22 * The Renderer
23 */
24class Doku_Renderer_metadata extends Doku_Renderer {
25
26    var $doc  = '';
27    var $meta = array();
28    var $persistent = array();
29
30    var $headers = array();
31    var $capture = true;
32    var $store   = '';
33    var $firstimage = '';
34
35    function getFormat(){
36        return 'metadata';
37    }
38
39    function document_start(){
40        global $ID;
41
42        $this->headers = array();
43
44        // external pages are missing create date
45        if(!$this->persistent['date']['created']){
46            $this->persistent['date']['created'] = filectime(wikiFN($ID));
47        }
48        if(!isset($this->persistent['user'])){
49            $this->persistent['user'] = '';
50        }
51        if(!isset($this->persistent['creator'])){
52            $this->persistent['creator'] = '';
53        }
54        // reset metadata to persistent values
55        $this->meta = $this->persistent;
56    }
57
58    function document_end(){
59        global $ID;
60
61        // store internal info in metadata (notoc,nocache)
62        $this->meta['internal'] = $this->info;
63
64        if (!isset($this->meta['description']['abstract'])){
65            // cut off too long abstracts
66            $this->doc = trim($this->doc);
67            if (strlen($this->doc) > 500)
68                $this->doc = utf8_substr($this->doc, 0, 500).'…';
69            $this->meta['description']['abstract'] = $this->doc;
70        }
71
72        $this->meta['relation']['firstimage'] = $this->firstimage;
73
74        if(!isset($this->meta['date']['modified'])){
75            $this->meta['date']['modified'] = filemtime(wikiFN($ID));
76        }
77
78    }
79
80    function toc_additem($id, $text, $level) {
81        global $conf;
82
83        //only add items within configured levels
84        if($level >= $conf['toptoclevel'] && $level <= $conf['maxtoclevel']){
85            // the TOC is one of our standard ul list arrays ;-)
86            $this->meta['description']['tableofcontents'][] = array(
87              'hid'   => $id,
88              'title' => $text,
89              'type'  => 'ul',
90              'level' => $level-$conf['toptoclevel']+1
91            );
92        }
93
94    }
95
96    function header($text, $level, $pos) {
97        if (!isset($this->meta['title'])) $this->meta['title'] = $text;
98
99        // add the header to the TOC
100        $hid = $this->_headerToLink($text,'true');
101        $this->toc_additem($hid, $text, $level);
102
103        // add to summary
104        if ($this->capture && ($level > 1)) $this->doc .= DOKU_LF.$text.DOKU_LF;
105    }
106
107    function section_open($level){}
108    function section_close(){}
109
110    function cdata($text){
111      if ($this->capture) $this->doc .= $text;
112    }
113
114    function p_open(){
115      if ($this->capture) $this->doc .= DOKU_LF;
116    }
117
118    function p_close(){
119        if ($this->capture){
120            if (strlen($this->doc) > 250) $this->capture = false;
121            else $this->doc .= DOKU_LF;
122        }
123    }
124
125    function linebreak(){
126        if ($this->capture) $this->doc .= DOKU_LF;
127    }
128
129    function hr(){
130        if ($this->capture){
131            if (strlen($this->doc) > 250) $this->capture = false;
132            else $this->doc .= DOKU_LF.'----------'.DOKU_LF;
133        }
134    }
135
136    /**
137     * Callback for footnote start syntax
138     *
139     * All following content will go to the footnote instead of
140     * the document. To achieve this the previous rendered content
141     * is moved to $store and $doc is cleared
142     *
143     * @author Andreas Gohr <andi@splitbrain.org>
144     */
145    function footnote_open() {
146        if ($this->capture){
147            // move current content to store and record footnote
148            $this->store = $this->doc;
149            $this->doc   = '';
150        }
151    }
152
153    /**
154     * Callback for footnote end syntax
155     *
156     * All rendered content is moved to the $footnotes array and the old
157     * content is restored from $store again
158     *
159     * @author Andreas Gohr
160     */
161    function footnote_close() {
162        if ($this->capture){
163            // restore old content
164            $this->doc = $this->store;
165            $this->store = '';
166        }
167    }
168
169    function listu_open(){
170        if ($this->capture) $this->doc .= DOKU_LF;
171    }
172
173    function listu_close(){
174        if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false;
175    }
176
177    function listo_open(){
178        if ($this->capture) $this->doc .= DOKU_LF;
179    }
180
181    function listo_close(){
182        if ($this->capture && (strlen($this->doc) > 250)) $this->capture = false;
183    }
184
185    function listitem_open($level){
186        if ($this->capture) $this->doc .= str_repeat(DOKU_TAB, $level).'* ';
187    }
188
189    function listitem_close(){
190        if ($this->capture) $this->doc .= DOKU_LF;
191    }
192
193    function listcontent_open(){}
194    function listcontent_close(){}
195
196    function unformatted($text){
197        if ($this->capture) $this->doc .= $text;
198    }
199
200    function preformatted($text){
201        if ($this->capture) $this->doc .= $text;
202    }
203
204    function file($text, $lang = null, $file = null){
205        if ($this->capture){
206            $this->doc .= DOKU_LF.$text;
207            if (strlen($this->doc) > 250) $this->capture = false;
208            else $this->doc .= DOKU_LF;
209        }
210    }
211
212    function quote_open(){
213        if ($this->capture) $this->doc .= DOKU_LF.DOKU_TAB.'"';
214    }
215
216    function quote_close(){
217        if ($this->capture){
218            $this->doc .= '"';
219            if (strlen($this->doc) > 250) $this->capture = false;
220            else $this->doc .= DOKU_LF;
221        }
222    }
223
224    function code($text, $language = null, $file = null){
225        if ($this->capture){
226            $this->doc .= DOKU_LF.$text;
227            if (strlen($this->doc) > 250) $this->capture = false;
228            else $this->doc .= DOKU_LF;
229      }
230    }
231
232    function acronym($acronym){
233        if ($this->capture) $this->doc .= $acronym;
234    }
235
236    function smiley($smiley){
237        if ($this->capture) $this->doc .= $smiley;
238    }
239
240    function entity($entity){
241        if ($this->capture) $this->doc .= $entity;
242    }
243
244    function multiplyentity($x, $y){
245        if ($this->capture) $this->doc .= $x.'×'.$y;
246    }
247
248    function singlequoteopening(){
249        global $lang;
250        if ($this->capture) $this->doc .= $lang['singlequoteopening'];
251    }
252
253    function singlequoteclosing(){
254        global $lang;
255        if ($this->capture) $this->doc .= $lang['singlequoteclosing'];
256    }
257
258    function apostrophe() {
259        global $lang;
260        if ($this->capture) $this->doc .= $lang['apostrophe'];
261    }
262
263    function doublequoteopening(){
264        global $lang;
265        if ($this->capture) $this->doc .= $lang['doublequoteopening'];
266    }
267
268    function doublequoteclosing(){
269        global $lang;
270        if ($this->capture) $this->doc .= $lang['doublequoteclosing'];
271    }
272
273    function camelcaselink($link) {
274        $this->internallink($link, $link);
275    }
276
277    function locallink($hash, $name = null){}
278
279    /**
280     * keep track of internal links in $this->meta['relation']['references']
281     */
282    function internallink($id, $name = null){
283        global $ID;
284
285        if(is_array($name)) {
286            $this->_firstimage($name['src']);
287            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
288        }
289
290        $parts = explode('?', $id, 2);
291        if (count($parts) === 2) {
292            $id = $parts[0];
293        }
294
295
296        $default = $this->_simpleTitle($id);
297
298        // first resolve and clean up the $id
299        resolve_pageid(getNS($ID), $id, $exists);
300        list($page, $hash) = explode('#', $id, 2);
301
302        // set metadata
303        $this->meta['relation']['references'][$page] = $exists;
304        // $data = array('relation' => array('isreferencedby' => array($ID => true)));
305        // p_set_metadata($id, $data);
306
307        // add link title to summary
308        if ($this->capture){
309            $name = $this->_getLinkTitle($name, $default, $id);
310            $this->doc .= $name;
311        }
312    }
313
314    function externallink($url, $name = null){
315        if(is_array($name)) {
316            $this->_firstimage($name['src']);
317            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
318        }
319
320        if ($this->capture){
321            $this->doc .= $this->_getLinkTitle($name, '<' . $url . '>');
322        }
323    }
324
325    function interwikilink($match, $name = null, $wikiName, $wikiUri){
326        if(is_array($name)) {
327            $this->_firstimage($name['src']);
328            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
329        }
330
331        if ($this->capture){
332            list($wikiUri, $hash) = explode('#', $wikiUri, 2);
333            $name = $this->_getLinkTitle($name, $wikiUri);
334            $this->doc .= $name;
335        }
336    }
337
338    function windowssharelink($url, $name = null){
339        if(is_array($name)) {
340            $this->_firstimage($name['src']);
341            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
342        }
343
344        if ($this->capture){
345            if ($name) $this->doc .= $name;
346            else $this->doc .= '<'.$url.'>';
347        }
348    }
349
350    function emaillink($address, $name = null){
351        if(is_array($name)) {
352            $this->_firstimage($name['src']);
353            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
354        }
355
356        if ($this->capture){
357            if ($name) $this->doc .= $name;
358            else $this->doc .= '<'.$address.'>';
359        }
360    }
361
362    function internalmedia($src, $title=null, $align=null, $width=null,
363                           $height=null, $cache=null, $linking=null){
364        if ($this->capture && $title) $this->doc .= '['.$title.']';
365        $this->_firstimage($src);
366        $this->_recordMediaUsage($src);
367    }
368
369    function externalmedia($src, $title=null, $align=null, $width=null,
370                           $height=null, $cache=null, $linking=null){
371        if ($this->capture && $title) $this->doc .= '['.$title.']';
372        $this->_firstimage($src);
373    }
374
375    function rss($url,$params) {
376        $this->meta['relation']['haspart'][$url] = true;
377
378        $this->meta['date']['valid']['age'] =
379              isset($this->meta['date']['valid']['age']) ?
380                  min($this->meta['date']['valid']['age'],$params['refresh']) :
381                  $params['refresh'];
382    }
383
384    //----------------------------------------------------------
385    // Utils
386
387    /**
388     * Removes any Namespace from the given name but keeps
389     * casing and special chars
390     *
391     * @author Andreas Gohr <andi@splitbrain.org>
392     */
393    function _simpleTitle($name){
394        global $conf;
395
396        if(is_array($name)) return '';
397
398        if($conf['useslash']){
399            $nssep = '[:;/]';
400        }else{
401            $nssep = '[:;]';
402        }
403        $name = preg_replace('!.*'.$nssep.'!','',$name);
404        //if there is a hash we use the anchor name only
405        $name = preg_replace('!.*#!','',$name);
406        return $name;
407    }
408
409    /**
410     * Creates a linkid from a headline
411     *
412     * @param string  $title   The headline title
413     * @param boolean $create  Create a new unique ID?
414     * @author Andreas Gohr <andi@splitbrain.org>
415     */
416    function _headerToLink($title, $create=false) {
417        if($create){
418            return sectionID($title,$this->headers);
419        }else{
420            $check = false;
421            return sectionID($title,$check);
422        }
423    }
424
425    /**
426     * Construct a title and handle images in titles
427     *
428     * @author Harry Fuecks <hfuecks@gmail.com>
429     */
430    function _getLinkTitle($title, $default, $id=null) {
431        global $conf;
432
433        $isImage = false;
434        if (is_array($title)){
435            if($title['title']) return '['.$title['title'].']';
436        } else if (is_null($title) || trim($title)==''){
437            if (useHeading('content') && $id){
438                $heading = p_get_first_heading($id,METADATA_DONT_RENDER);
439                if ($heading) return $heading;
440            }
441            return $default;
442        } else {
443            return $title;
444        }
445    }
446
447    function _firstimage($src){
448        if($this->firstimage) return;
449        global $ID;
450
451        list($src,$hash) = explode('#',$src,2);
452        if(!media_isexternal($src)){
453            resolve_mediaid(getNS($ID),$src, $exists);
454        }
455        if(preg_match('/.(jpe?g|gif|png)$/i',$src)){
456            $this->firstimage = $src;
457        }
458    }
459
460    function _recordMediaUsage($src) {
461        global $ID;
462
463        list ($src, $hash) = explode('#', $src, 2);
464        if (media_isexternal($src)) return;
465        resolve_mediaid(getNS($ID), $src, $exists);
466        $this->meta['relation']['media'][$src] = $exists;
467    }
468}
469
470//Setup VIM: ex: et ts=4 :
471