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