xref: /dokuwiki/inc/parser/metadata.php (revision b288bdefdcc243374f385a3adaea329256016e4c)
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        $default = $this->_simpleTitle($id);
296
297        // first resolve and clean up the $id
298        resolve_pageid(getNS($ID), $id, $exists);
299        list($page, $hash) = explode('#', $id, 2);
300
301        // set metadata
302        $this->meta['relation']['references'][$page] = $exists;
303        // $data = array('relation' => array('isreferencedby' => array($ID => true)));
304        // p_set_metadata($id, $data);
305
306        // add link title to summary
307        if ($this->capture){
308            $name = $this->_getLinkTitle($name, $default, $id);
309            $this->doc .= $name;
310        }
311    }
312
313    function externallink($url, $name = null){
314        if(is_array($name)) {
315            $this->_firstimage($name['src']);
316            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
317        }
318
319        if ($this->capture){
320            $this->doc .= $this->_getLinkTitle($name, '<' . $url . '>');
321        }
322    }
323
324    function interwikilink($match, $name = null, $wikiName, $wikiUri){
325        if(is_array($name)) {
326            $this->_firstimage($name['src']);
327            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
328        }
329
330        if ($this->capture){
331            list($wikiUri, $hash) = explode('#', $wikiUri, 2);
332            $name = $this->_getLinkTitle($name, $wikiUri);
333            $this->doc .= $name;
334        }
335    }
336
337    function windowssharelink($url, $name = null){
338        if(is_array($name)) {
339            $this->_firstimage($name['src']);
340            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
341        }
342
343        if ($this->capture){
344            if ($name) $this->doc .= $name;
345            else $this->doc .= '<'.$url.'>';
346        }
347    }
348
349    function emaillink($address, $name = null){
350        if(is_array($name)) {
351            $this->_firstimage($name['src']);
352            if ($name['type'] == 'internalmedia') $this->_recordMediaUsage($name['src']);
353        }
354
355        if ($this->capture){
356            if ($name) $this->doc .= $name;
357            else $this->doc .= '<'.$address.'>';
358        }
359    }
360
361    function internalmedia($src, $title=null, $align=null, $width=null,
362                           $height=null, $cache=null, $linking=null){
363        if ($this->capture && $title) $this->doc .= '['.$title.']';
364        $this->_firstimage($src);
365        $this->_recordMediaUsage($src);
366    }
367
368    function externalmedia($src, $title=null, $align=null, $width=null,
369                           $height=null, $cache=null, $linking=null){
370        if ($this->capture && $title) $this->doc .= '['.$title.']';
371        $this->_firstimage($src);
372    }
373
374    function rss($url,$params) {
375        $this->meta['relation']['haspart'][$url] = true;
376
377        $this->meta['date']['valid']['age'] =
378              isset($this->meta['date']['valid']['age']) ?
379                  min($this->meta['date']['valid']['age'],$params['refresh']) :
380                  $params['refresh'];
381    }
382
383    //----------------------------------------------------------
384    // Utils
385
386    /**
387     * Removes any Namespace from the given name but keeps
388     * casing and special chars
389     *
390     * @author Andreas Gohr <andi@splitbrain.org>
391     */
392    function _simpleTitle($name){
393        global $conf;
394
395        if(is_array($name)) return '';
396
397        if($conf['useslash']){
398            $nssep = '[:;/]';
399        }else{
400            $nssep = '[:;]';
401        }
402        $name = preg_replace('!.*'.$nssep.'!','',$name);
403        //if there is a hash we use the anchor name only
404        $name = preg_replace('!.*#!','',$name);
405        return $name;
406    }
407
408    /**
409     * Creates a linkid from a headline
410     *
411     * @param string  $title   The headline title
412     * @param boolean $create  Create a new unique ID?
413     * @author Andreas Gohr <andi@splitbrain.org>
414     */
415    function _headerToLink($title, $create=false) {
416        if($create){
417            return sectionID($title,$this->headers);
418        }else{
419            $check = false;
420            return sectionID($title,$check);
421        }
422    }
423
424    /**
425     * Construct a title and handle images in titles
426     *
427     * @author Harry Fuecks <hfuecks@gmail.com>
428     */
429    function _getLinkTitle($title, $default, $id=null) {
430        global $conf;
431
432        $isImage = false;
433        if (is_array($title)){
434            if($title['title']) return '['.$title['title'].']';
435        } else if (is_null($title) || trim($title)==''){
436            if (useHeading('content') && $id){
437                $heading = p_get_first_heading($id,METADATA_DONT_RENDER);
438                if ($heading) return $heading;
439            }
440            return $default;
441        } else {
442            return $title;
443        }
444    }
445
446    function _firstimage($src){
447        if($this->firstimage) return;
448        global $ID;
449
450        list($src,$hash) = explode('#',$src,2);
451        if(!media_isexternal($src)){
452            resolve_mediaid(getNS($ID),$src, $exists);
453        }
454        if(preg_match('/.(jpe?g|gif|png)$/i',$src)){
455            $this->firstimage = $src;
456        }
457    }
458
459    function _recordMediaUsage($src) {
460        global $ID;
461
462        list ($src, $hash) = explode('#', $src, 2);
463        if (media_isexternal($src)) return;
464        resolve_mediaid(getNS($ID), $src, $exists);
465        $this->meta['relation']['media'][$src] = $exists;
466    }
467}
468
469//Setup VIM: ex: et ts=4 :
470