1<?php 2/** 3 * Eventum Plugin: Evetnum SCM addons 4 * 5 * interpret eventum issue tags in DokuWiki 6 * 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 8 * @author Elan Ruusamäe <glen@delfi.ee> 9 */ 10// must be run within Dokuwiki 11if(!defined('DOKU_INC')) die(); 12 13if (file_exists($autoload = __DIR__. '/vendor/autoload.php')) { 14 require_once $autoload; 15} else { 16 // try from include path 17 require_once 'XML/RPC.php'; 18 require_once 'class.Eventum_RPC.php'; 19} 20 21/** 22 * All DokuWiki plugins to extend the parser/rendering mechanism 23 * need to inherit from this class 24 */ 25class syntax_plugin_eventum extends DokuWiki_Syntax_Plugin { 26 /* 27 * keys that we use for caching, to avoid running out of memory when serializing 28 */ 29 static $cache_keys = array( 30 'iss_summary' => 'summary', 31 'sta_title' => 'status', 32 'sta_is_closed' => 'is_closed', 33 ); 34 35 /** 36 * What kind of syntax are we? 37 */ 38 function getType() { 39 return 'substition'; 40 } 41 42 /** 43 * Where to sort in? 44 */ 45 function getSort() { 46 return 290; 47 } 48 49 /** 50 * Connect pattern to lexer 51 */ 52 function connectTo($mode) { 53 $this->Lexer->addSpecialPattern('\[\[issue>.+?\]\]', $mode, 'plugin_eventum'); 54 } 55 56 57 /** 58 * Handle the match 59 */ 60 function handle($match, $state, $pos, Doku_Handler $handler) { 61 $raw = $match = substr($match, 8, -2); 62 // extract title 63 list($match, $title) = explode('|', $match, 2); 64 // extract id 65 list($id, $attrs) = explode('&', $match, 2); 66 67 $data = array('raw' => $raw, 'id' => $id, 'attrs' => $attrs); 68 69 // set title only when specified to be able to make difference between empty and unset 70 if ($title !== null) { 71 $data['title'] = trim($title); 72 } 73 74 return $data; 75 } 76 77 function cache($id, $data = null) { 78 global $conf; 79 $cachefile = $conf['cachedir'].'/'.$this->getPluginName().'.cache'; 80 81 // mode: get but no cachefile 82 if ($data === null && !file_exists($cachefile)) { 83 return null; 84 } 85 86 // read cache 87 $cache = array(); 88 if (file_exists($cachefile)) { 89 $cache = unserialize(file_get_contents($cachefile)); 90 } 91 92 // expire as long as page edit time to make page edit smooth but still 93 // have almost accurate data. 94 $mtime = time() - $conf['locktime']; 95 foreach ($cache as $i => $ent) { 96 if ($ent['mtime'] < $mtime) { 97 unset($cache[$i]); 98 } 99 } 100 101 // mode get: 102 if ($data === null) { 103 return isset($cache[$id]) ? $cache[$id] : null; 104 } 105 106 // mode: set 107 $cache[$id] = $data; 108 file_put_contents($cachefile, serialize($cache)); 109 return true; 110 } 111 112 /* 113 * combine keys from $data array to new one. 114 * rename keys to be named as $value says. 115 */ 116 function filter_keys($keys, $data) { 117 $res = array(); 118 foreach ($keys as $key => $value) { 119 // remap old key to new one 120 if (isset($data[$key])) { 121 $res[$value] = $data[$key]; 122 continue; 123 } 124 // use already existing new name 125 if (isset($data[$value])) { 126 $res[$value] = $data[$value]; 127 continue; 128 } 129 } 130 return $res; 131 } 132 133 /** 134 * Query data from Eventum server 135 */ 136 function query($id) { 137 global $conf; 138 139 $cache = $this->cache($id); 140 if ($cache !== null) { 141 return $cache; 142 } 143 144 static $client = null; 145 static $eventum_url, $rpc_url; 146 147 if (!$client) { 148 // setup RPC object 149 $rpc_url = $this->getConf('url') . '/rpc/xmlrpc.php'; 150 $client = new Eventum_RPC($rpc_url); 151 $client->setCredentials($this->getConf('username'), $this->getConf('password')); 152 //$client->setDebug(1); 153 154 // Issue link 155 $eventum_url = $this->getConf('url') . '/view.php?id='; 156 } 157 $data['url'] = $eventum_url . $id; 158 159 try { 160 $data['details'] = self::filter_keys(self::$cache_keys, $client->getSimpleIssueDetails((int )$id)); 161 162 } catch (Eventum_RPC_Exception $e) { 163 $data['error'] = $e->getMessage(); 164 165 if ($conf['allowdebug']) { 166 $data['rpcurl'] = $rpc_url; 167 } 168 } 169 170 $data['id'] = $id; 171 $data['mtime'] = time(); 172 $this->cache($id, $data); 173 174 return $data; 175 } 176 177 /** 178 * Create output 179 */ 180 function render($format, Doku_Renderer $renderer, $data) { 181 global $ID; 182 183 // fetch extra data from eventum 184 $data += $this->query($data['id']); 185 186 // link title 187 $link = 'issue #'. $data['id']; 188 189 if ($data['error']) { 190 if ($format == 'xhtml') { 191 $renderer->doc .= $link; 192 $renderer->doc .= ': <i style="color:red">'.$data['error'].'</i>'; 193 if (isset($data['rpcurl'])) { 194 $renderer->doc .= " <tt>RPC URL: {$data['rpcurl']}</tt>"; 195 } 196 } else { 197 $renderer->cdata($data['error']); 198 } 199 return; 200 } 201 202 if (!isset($data['title'])) { 203 $data['title'] = $data['details']['summary']; 204 } 205 206 if ($format == 'xhtml' || $format == 'odt') { 207 $html = ''; 208 $html .= $this->link($format, $data['url'], $link, $data['details']['summary']); 209 if ($data['title']) { 210 $html .= ': '. hsc($data['title']); 211 } 212 213 if ($data['details']['status']) { 214 $html .= ' '. $this->emphasis($format, '('.$data['details']['status'].')'); 215 } 216 217 if ($data['details']['is_closed']) { 218 $html = $this->strike($format, $html); 219 } 220 221 $renderer->doc .= $this->html($format, $html); 222 223 } elseif ($format == 'odt') { 224 $renderer->externallink($data['url'], $link); 225 $renderer->cdata(': '.$data['title']); 226 } 227 228 return true; 229 } 230 231 /** odt/html export helpers, partly ripped from odt plugin */ 232 function _xmlEntities($value) { 233 return str_replace( array('&','"',"'",'<','>'), array('&','"',''','<','>'), $value); 234 } 235 236 function strike($format, $text) { 237 $doc = ''; 238 if ($format == 'xhtml') { 239 $doc .= '<strike>'; 240 $doc .= $text; 241 $doc .= '</strike>'; 242 } elseif ($format == 'odt') { 243 $doc .= '<text:span text:style-name="del">'; 244 $doc .= $text; 245 $doc .= '</text:span>'; 246 } 247 return $doc; 248 } 249 250 function emphasis($format, $text) { 251 if ($format == 'xhtml') { 252 $doc .= '<i>'; 253 $doc .= $text; 254 $doc .= '</i>'; 255 } elseif ($format == 'odt') { 256 $doc .= '<text:span text:style-name="Emphasis">'; 257 $doc .= $text; 258 $doc .= '</text:span>'; 259 } 260 return $doc; 261 } 262 263 function html($format, $text) { 264 $doc = ''; 265 if ($format == 'xhtml') { 266 $doc .= $text; 267 } elseif ($format == 'odt') { 268 $doc .= '<text:span>'; 269 $doc .= $text; 270 $doc .= '</text:span>'; 271 } 272 return $doc; 273 } 274 275 function link($format, $url, $name, $title) { 276 $doc = ''; 277 if ($format == 'xhtml') { 278 $doc .= '<a class="iw_eventum" href="'.$url.'" target="_blank" title="'.$title.'">'.hsc($name).'</a>'; 279 280 } elseif ($format == 'odt') { 281 $url = $this->_xmlEntities($url); 282 $doc .= '<text:a xlink:type="simple" xlink:href="'.$url.'">'; 283 $doc .= $name; // we get the name already XML encoded 284 $doc .= '</text:a>'; 285 } 286 return $doc; 287 } 288} 289 290//Setup VIM: ex: et ts=4 enc=utf-8 : 291