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