1<?php 2/** 3 * 4 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 * @author Christoph Clausen <christoph.clausen@gmail.com> 6 */ 7// must be run within Dokuwiki 8if(!defined('DOKU_INC')) die(); 9$dataEntryFile = DOKU_PLUGIN.'data/syntax/entry.php'; 10if(file_exists($dataEntryFile)){ 11 require_once $dataEntryFile; 12} else { 13 msg('datatemplate: Cannot find Data plugin.', -1); 14 return; 15} 16 17class syntax_plugin_datatemplate_entry extends syntax_plugin_data_entry { 18 19 /** 20 * @var $dthlp helper_plugin_data will hold the data helper plugin 21 */ 22 var $dthlp = null; 23 24 /** 25 * Constructor. Load helper plugin 26 */ 27 function __construct(){ 28 $this->dthlp = plugin_load('helper', 'data'); 29 if(!$this->dthlp) msg('Loading the data helper failed. Make sure the data plugin is installed.',-1); 30 } 31 32 /** 33 * Connect pattern to lexer 34 */ 35 function connectTo($mode) { 36 $this->Lexer->addSpecialPattern('----+ *datatemplateentry(?: [ a-zA-Z0-9_]*)?-+\n.*?\n----+', 37 $mode,'plugin_datatemplate_entry'); 38 } 39 40 /** 41 * Handle the match - parse the data 42 */ 43 function handle($match, $state, $pos, Doku_Handler $handler){ 44 // The parser of the parent class should have nicely parsed all 45 // parameters. We want to extract the template parameter and treat 46 // it separately. 47 48 // strip datatemplateentry to get right classes 49 $match = preg_replace('/datatemplateentry/', '', $match, 1); 50 $data = parent::handle($match, $state, $pos, $handler); 51 if(array_key_exists('template', $data['cols'])) { 52 unset($data['cols']['template']); 53 $data['template'] = $data['data']['template']; 54 unset($data['data']['template']); 55 } 56 if(array_key_exists('template', $data)) { 57 $data['instructions'] = $this->_getInstructions($data); 58 } 59 return $data; 60 } 61 62 63 /** 64 * Create output or save the data 65 */ 66 function render($format, Doku_Renderer $renderer, $data) { 67 global $ID; 68 switch ($format){ 69 case 'xhtml': 70 /** @var $renderer Doku_Renderer_xhtml */ 71 $this->_showData($data,$renderer); 72 return true; 73 case 'metadata': 74 /** @var $renderer Doku_Renderer_metadata */ 75 $this->_saveRendereredData($data,$ID,$renderer); 76 return true; 77 case 'plugin_data_edit': 78 /** @var $renderer Doku_Renderer_plugin_data_edit */ 79 $this->_editData($data, $renderer); 80 return true; 81 default: 82 return false; 83 } 84 } 85 86 /** 87 * Get template file name and check existance and access rights. 88 * 89 * @param string $template value of 'template' key in entry 90 * @return int|string 91 * 0 if the file does not exist 92 * -1 if no permission to read the file 93 * the file name otherwise 94 */ 95 function _getFile($template) { 96 global $ID; 97 $wikipage = preg_split('/\#/u', $template, 2); 98 99 resolve_pageid(getNS($ID), $wikipage[0], $exists); 100 101 $file = wikiFN($wikipage[0]); 102 103 if (!@file_exists($file)) return 0; 104 105 if (auth_quickaclcheck($wikipage[0]) < 1) return -1; 106 107 return $file; 108 } 109 110 /** 111 * Generate wiki output from instructions 112 * 113 * @param $data array as returned by handle() 114 * @param &$R Doku_Renderer_xhtml 115 * @return bool|void 116 */ 117 function _showData($data, &$R) { 118 119 if(!array_key_exists('template', $data)) { 120 // If keyword "template" not present, we can leave 121 // the rendering to the parent class. 122 parent::_showData($data, $R); 123 return true; 124 } 125 126 // Treat possible errors first 127 if($data['instructions'] == 0) { 128 $R->doc .= '<div class="datatemplateentry">'; 129 $R->doc .= "Template {$data['template']} not found. "; 130 $R->internalLink($data['template'], '[Click here to create it]'); 131 $R->doc .= '</div>'; 132 return true; 133 } elseif ($data['instructions'] == -1) { 134 $R->doc .= '<div class="datatemplateentry"> No permissions to view the template </div>'; 135 return true; 136 } 137 138 // embed the included page 139 $R->doc .= '<div class="datatemplateentry ' . $data['classes'] . '">'; 140 141 // render the instructructions on the fly 142 $text = p_render('xhtml', $data['instructions'], $info); 143 144 // remove toc, section edit buttons and category tags 145 $patterns = array('!<div class="toc">.*?(</div>\n</div>)!s', 146 '#<!-- EDIT.*? \[(\d*-\d*)\] -->#e', 147 '!<div class="category">.*?</div>!s'); 148 $replace = array('','',''); 149 $text = preg_replace($patterns,$replace,$text); 150 151 $R->doc .= $text; 152 $R->doc .= '</div>'; 153 154 return true; 155 } 156 157 158 /** 159 * Read and process template file and return wiki instructions. 160 * Passes through the return value of _getFile if the file does not exist or cannot be accessed. 161 * If no template was specified, return empty array. 162 * 163 * @param array $data return of handle() 164 * @return bool|int|string 165 * 0 if the template file does not exist 166 * -1 if no permission to read the template file 167 * otherwise the template page as list of instructions with replacements performed 168 */ 169 function _getInstructions($data){ 170 // Get the raw file, and parse it into its instructions. This could be cached... maybe. 171 $file = $this->_getFile($data['template']); 172 if(!is_string($file)) return $file; 173 $rawFile = io_readfile($file); 174 175 $replacers['raw_keys'] = array(); 176 $replacers['raw_vals'] = array(); 177 $replacers['keys'] = array(); 178 $replacers['vals'] = array(); 179 180 foreach($data['data'] as $key => $val){ 181 if($val == '' || !count($val)) continue; 182 183 $replacers['keys'][] = "@@" . $key . "@@"; 184 $replacers['raw_keys'][] = "@@!" . $key . "@@"; 185 if(is_array($val)){ 186 $cnt = count($val); 187 $ret = ''; 188 $ret_raw = ''; 189 for ($i=0; $i<$cnt; $i++){ 190 $ret .= $this->_formatData($data['cols'][$key], $val[$i]); 191 $ret_raw .= $val[$i]; 192 if($i < $cnt - 1) { 193 $ret .= ', '; 194 $ret_raw .= ', '; 195 } 196 } 197 $replacers['vals'][] = $ret; 198 $replacers['raw_vals'][] = $ret_raw; 199 } else { 200 $replacers['vals'][] = $this->_formatData($data['cols'][$key], $val); 201 $replacers['raw_vals'][] = $val; 202 } 203 } 204 // First do raw replacements 205 $raw = str_replace($replacers['raw_keys'], $replacers['raw_vals'], $rawFile); 206 $raw = str_replace($replacers['keys'], $replacers['vals'], $raw); 207 $raw = preg_replace('/@@.*?@@/', '', $raw); 208 $instr = p_get_instructions($raw); 209 210 return $instr; 211 } 212 213 /** 214 * The data plugin defines this function in its helper class with the purpose 215 * to output XHTML code for the different column types. 216 * We want to let the dokuwiki renderer generate the required output, such 217 * that also metadata is handled correctly. Hence, we will try to translate 218 * each column type to the corresponding dokuwiki syntax. 219 * 220 * @param array $column 221 * @param string $value value of column. 222 * @return string DokuWiki syntax interpretation of value 223 */ 224 function _formatData($column, $value){ 225 $vals = explode("\n",$value); 226 $outs = array(); 227 foreach($vals as $val){ 228 $val = trim($val); 229 if($val=='') continue; 230 $type = $column['type']; 231 if (is_array($type)) $type = $type['type']; 232 switch($type){ 233 case 'page': 234 $val = $this->dthlp->_addPrePostFixes($column['type'], $val); 235 $outs[] = '[[' . $val. ']]'; 236 break; 237 case 'pageid': 238 case 'title': 239 list($id,$title) = explode('|',$val,2); 240 $id = $this->dthlp->_addPrePostFixes($column['type'], $id); 241 $outs[] = '[[' . $id . '|' . $title . ']]'; 242 break; 243 case 'nspage': 244 // no prefix/postfix here 245 $val = ':'.$column['key'].":$val"; 246 $outs[] = '[[' . $val . ']]'; 247 break; 248 case 'mail': 249 list($id,$title) = explode(' ',$val,2); 250 $id = $this->dthlp->_addPrePostFixes($column['type'], $id); 251 $outs[] = '[[' . $id . '|' . $title . ']]'; 252 break; 253 case 'url': 254 $val = $this->dthlp->_addPrePostFixes($column['type'], $val); 255 $outs[] = '[[' . $val . ']]'; 256 break; 257 case 'tag': 258 #todo not handled by datatemplate so far 259 $outs[] = '<a href="'.wl(str_replace('/',':',cleanID($column['key'])),array('dataflt'=>$column['key'].':'.$val )). 260 '" title="'.sprintf($this->getLang('tagfilter'),hsc($val)). 261 '" class="wikilink1">'.hsc($val).'</a>'; 262 break; 263 case 'wiki': 264 $val = $this->dthlp->_addPrePostFixes($column['type'], $val); 265 $outs[] = $val; 266 break; 267 default: 268 $val = $this->dthlp->_addPrePostFixes($column['type'], $val); 269 if(substr($type,0,3) == 'img'){ 270 $sz = (int) substr($type,3); 271 if(!$sz) $sz = 40; 272 $title = $column['key'].': '.basename(str_replace(':','/',$val)); 273 $outs[] = '{{' . $val. ($sz ? '?'.$sz:'') .'|'.$title. '}}'; 274 }else{ 275 $outs[] = $val; 276 } 277 } //todo add type 'timestamp' ... returns html.. 278 } 279 return join(', ',$outs); 280 } 281 282 /** 283 * Save date to the database 284 * 285 * We are overriding this function to properly generate the metadata for 286 * the parsed template, such that page title and table of contents are 287 * correct. 288 */ 289 function _saveRendereredData($data,$id,&$renderer){ 290 if(!array_key_exists('template', $data)) { 291 parent::_saveData($data, $id, $renderer->meta['title']); 292 return; 293 } 294 295 $file = $this->_getFile($data['template']); 296 $instr = $data['instructions']; 297 // If for some reason there are no instructions, don't do anything 298 // (Maybe for cache handling one should hand the template file name to the 299 // metadata, even though the file does not exist) 300 if(!is_string($file)) parent::_saveData($data, $id, $renderer->meta['title']); 301 $renderer->meta['relation']['haspart'][$file] = array('owner'=>$this->getPluginName()); 302 303 // Remove document_start and document_end from instructions 304 array_shift($instr); 305 array_pop($instr); 306 307 // loop through the instructions 308 for($i = 0; $i < count($instr); $i++) { 309 call_user_func_array(array($renderer, $instr[$i][0]), $instr[$i][1]); 310 } 311 312 parent::_saveData($data, $id, $renderer->meta['title']); 313 } 314} 315/* Local Variables: */ 316/* c-basic-offset: 4 */ 317/* End: */ 318