1<?php 2/** 3 * Meta Plugin: Sets metadata for the current page 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Esther Brunner <wikidesign@gmail.com> 7 */ 8 9/** 10 * All DokuWiki plugins to extend the parser/rendering mechanism 11 * need to inherit from this class 12 */ 13class syntax_plugin_meta extends DokuWiki_Syntax_Plugin 14{ 15 function getType() 16 { 17 return 'substition'; 18 } 19 20 function getSort() 21 { 22 return 99; 23 } 24 25 function connectTo($mode) 26 { 27 $this->Lexer->addSpecialPattern('~~META:.*?~~',$mode,'plugin_meta'); 28 } 29 30 /** 31 * Handle the match 32 */ 33 public function handle($match, $state, $pos, Doku_Handler $handler) 34 { 35 // strip ~~META: from start and ~~ from end 36 $match = substr($match,7,-2); 37 38 $data = array(); 39 $pairs = explode('&', $match); 40 foreach ($pairs as $pair) { 41 list($key, $value) = explode('=', $pair, 2); 42 list($key, $subkey) = explode(' ', $key, 2); 43 if (trim($subkey)) { 44 $data[trim($key)][trim($subkey)] = trim($value); 45 } else { 46 $data[trim($key)] = trim($value); 47 } 48 } 49 $data = array_change_key_case($data, CASE_LOWER); 50 51 return $data; 52 } 53 54 /** 55 * Create output 56 */ 57 public function render($mode, Doku_Renderer $renderer, $data) 58 { 59 if ($mode == 'xhtml') { 60 // don't output anything 61 return true; 62 } elseif ($mode == 'metadata') { 63 /** @var Doku_Renderer_metadata $renderer */ 64 65 // do some validation / conversion for date metadata 66 if (isset($data['date'])) { 67 if (is_array($data['date'])) { 68 foreach ($data['date'] as $key => $date) { 69 $date = $this->convertDate(trim($date)); 70 if (!$date) { 71 unset($data['date'][$key]); 72 } else { 73 $data['date'][$key] = $date; 74 } 75 } 76 } else { 77 unset($data['date']); 78 } 79 } 80 81 // now merge the arrays 82 $protected = array('description', 'date', 'contributor'); 83 foreach ($data as $key => $value) { 84 85 // be careful with sub-arrays of $meta['relation'] 86 if ($key == 'relation') { 87 foreach ($value as $subkey => $subvalue) { 88 if ($subkey == 'media') { 89 $renderer->meta[$key][$subkey][cleanID($subvalue)] = @file_exists(mediaFN($subvalue)); 90 } elseif ($subkey == 'firstimage') { 91 /* The metadata renderer overrides the first image value with its internal value at the end. 92 Therefore the only thing we can do is setting this internal value by calling _firstimage. 93 This fails if there has already been a first image saved. */ 94 $renderer->_firstimage($subvalue); 95 } else { 96 // for everything else assume that we have a page id 97 $renderer->meta[$key][$subkey][cleanID($subvalue)] = page_exists($subvalue); 98 } 99 } 100 } elseif (in_array($key, $protected)) { 101 // be careful with some sensitive arrays of $meta 102 if (is_array($renderer->meta) && array_key_exists($key, $renderer->meta)) { 103 $renderer->meta[$key] = array_merge($renderer->meta[$key], array($value)); 104 } else { 105 $renderer->meta[$key] = $value; 106 } 107 } else { 108 // no special treatment for the rest 109 $renderer->meta[$key] = $value; 110 } 111 } 112 } 113 } 114 115 /** 116 * converts YYYY-MM-DD[ hh:mm:ss][ -> [YYYY-MM-DD ]hh:mm:ss] to PHP timestamps 117 */ 118 private function convertDate($date) 119 { 120 list($start, $end) = explode('->', $date, 2); 121 122 if (!$end) { 123 // single date 124 list($date, $time) = explode(' ', trim($start), 2); 125 if (!preg_match('/\d{4}\-\d{2}\-\d{2}/', $date)) { 126 return false; 127 } 128 $time = $this->autocompleteTime($time); 129 return strtotime($date.' '.$time); 130 } else { 131 // duration 132 133 // start 134 list($startdate, $starttime) = explode(' ', trim($start), 2); 135 $startdate = $this->autocompleteDate($startdate); 136 if (!$startdate) { 137 return false; 138 } 139 $starttime = $this->autocompleteTime($starttime); 140 141 // end 142 list($enddate, $endtime) = explode(' ', trim($end), 2); 143 if (!trim($endtime)) { 144 // only time given 145 $end_date = $this->autocompleteDate($enddate, true); 146 if (!$end_date) { 147 $endtime = $this->autocompleteTime($enddate, true); 148 $enddate = $startdate; 149 } else { 150 // only date given 151 $enddate = $end_date; 152 $endtime = '23:59:59'; 153 } 154 } else { 155 $enddate = $this->autocompleteDate($enddate, true); 156 if (!$enddate) { 157 $enddate = $startdate; 158 } 159 $endtime = $this->autocompleteTime($endtime, true); 160 } 161 162 $start = strtotime($startdate.' '.$starttime); 163 $end = strtotime($enddate.' '.$endtime); 164 if (!$start || !$end) { 165 return false; 166 } 167 return array('start' => $start, 'end' => $end); 168 } 169 } 170 171 private function autocompleteDate($date, $end=false) 172 { 173 if (!preg_match('/^\d{4}\-\d{2}\-\d{2}$/', $date)) { 174 if (preg_match('/^\d{4}\-\d{2}$/', $date)) { 175 // we don't know which month 176 return ($end) ? $date.'-28' : $date.'-01'; 177 } elseif (preg_match('/^\d{4}$/', $date)) { 178 return ($end) ? $date.'-12-31' : $date.'-01-01'; 179 } else { 180 return false; 181 } 182 } else { 183 return $date; 184 } 185 } 186 187 private function autocompleteTime($time, $end=false) 188 { 189 if (!preg_match('/^\d{2}:\d{2}:\d{2}$/', $time)) { 190 if (preg_match('/^\d{2}:\d{2}$/', $time)) { 191 return ($end) ? $time.':59' : $time.':00'; 192 } elseif (preg_match('/^\d{2}$/', $time)) { 193 return ($end) ? $time.':59:59': $time.':00:00'; 194 } else { 195 return ($end) ? '23:59:59' : '00:00:00'; 196 } 197 } else { 198 return $time; 199 } 200 } 201} 202// vim:ts=4:sw=4:et:enc=utf-8: 203