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