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