1<?php 2/** 3* DokuMicroBugTracker Plugin: allows to create simple bugtracker 4* 5* @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6* @author Benoît HERVIER <khertan@khertan.net> 7*/ 8//error_reporting(E_ALL);ini_set('display_errors', true); 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'syntax.php'); 12function metaFN2($id,$ext){ global $conf; $id = cleanID($id); $id = str_replace(':','/',$id); $fn = $conf['metadir'].'/'.utf8_encodeFN($id).$ext; return $fn;} 13 14/** 15* All DokuWiki plugins to extend the parser/rendering mechanism 16* need to inherit from this class 17*/ 18class syntax_plugin_dokumicrobugtracker extends DokuWiki_Syntax_Plugin 19{ 20 /** 21 * return some info 22 */ 23 function getInfo(){ 24 return confToHash(dirname(__FILE__).'/INFO'); 25 } 26 27 function getType(){ return 'substition';} 28 function getPType(){ return 'block';} 29 function getSort(){ return 167;} 30 /** 31 32 * Connect pattern to lexer 33 */ 34 function connectTo($mode){ 35 $this->Lexer->addSpecialPattern('\{\{dokumicrobugtracker>[^}]*\}\}',$mode,'plugin_dokumicrobugtracker'); 36 } 37 /** 38 * Handle the match 39 */ 40 function handle($match, $state, $pos, &$handler){ 41 $match = substr($match,22,-2); //strip markup from start and end 42 //handle params 43 $data = array(); 44 $data['match'] = $match; 45 $params = explode('|',$match,3); 46 47 //Default Value 48 $data['display'] = 'ALL'; 49 $data['status'] = 'ALL'; 50 foreach($params as $param){ 51 $splitparam = explode('=',$param); 52 if ($splitparam[1] != '') 53 { 54 if ($splitparam[0]=='project') 55 {$data['project'] = $splitparam[1]; 56 /*continue;*/} 57 58 if ($splitparam[0]=='status') 59 {$data['status'] = strtoupper($splitparam[1]); 60 /*continue;*/} 61 62 if ($splitparam[0]=='display') 63 {$data['display'] = strtoupper($splitparam[1]); 64 /*continue;*/} 65 } 66 } 67 68 return $data; 69 } 70 71 /** * Captcha OK */ 72 function _captcha_ok() 73 { 74 $helper = null; 75 if(@is_dir(DOKU_PLUGIN.'captcha')) 76 $helper = plugin_load('helper','captcha'); 77 if(!is_null($helper) && $helper->isEnabled()) 78 { 79 return $helper->check(); 80 } 81 return ($this->getConf('use_captcha')); 82 } 83 84 /** 85 * Create output 86 */ 87 function render($mode, &$renderer, $data) { 88 global $ID; 89 if ($mode == 'xhtml'){ 90 91 $renderer->info['cache'] = false; 92 93 // get bugs file contents 94 $pfile = metaFN2(md5($data['project']), '.bugs'); 95 if (@file_exists($pfile)) 96 {$bugs = unserialize(@file_get_contents($pfile));} 97 else 98 {$bugs = array();} 99 100 101 102 $Generated_Header = ''; 103 if (($data['display']=='FORM') || ($data['display']=='ALL')) 104 { 105 //If it s a usr report add it to the pseudo db 106 $Generated_Header = ''; 107 if (isset($_REQUEST['severity'])) { 108 if ($_REQUEST['severity']) 109 { 110 if ($_REQUEST['version']== '') 111 { 112 $Generated_Header = '<div class ="error">Please enter a version number.</div>'; 113 } 114 elseif ($_REQUEST['description']== '') 115 { 116 $Generated_Header = '<div class ="error">Please enter a description.</div>'; 117 } 118 elseif (!$this->_captcha_ok()) 119 { 120 $Generated_Header = '<div class ="error">Wrong answer to the antispam question.</div>'; 121 } 122 else 123 { 124 if (checkSecurityToken()) 125 { 126 //Add it 127 $bug_id=count($bugs); 128 foreach ($bugs as $value) 129 {if ($value['id'] >= $bug_id) {$bug_id=$value['id'] + 1;}} 130 $bugs[$bug_id]['id'] = $bug_id; 131 $bugs[$bug_id]['version'] = htmlspecialchars(stripslashes($_REQUEST['version'])); 132 $bugs[$bug_id]['severity'] = htmlspecialchars(stripslashes($_REQUEST['severity'])); 133 $bugs[$bug_id]['status'] = "New"; 134 $bugs[$bug_id]['description'] = htmlspecialchars(stripslashes($_REQUEST['description'])); 135 $bugs[$bug_id]['resolution'] = ''; 136 $bugs[$bug_id]['author'] = htmlspecialchars(stripslashes($_REQUEST['email'])); 137 //Ecriture en pseudo db 138 $fh = fopen($pfile, 'w'); 139 fwrite($fh, serialize($bugs)); 140 fclose($fh); 141 $Generated_Header = '<div style="border: 3px green solid; background-color: lightgreen; margin: 10px; padding: 10px;">Your report have been successfully stored as bug#'.$bug_id.'</div>'; 142 $this->_emailForNewBug($bug_id,$data['project'],$bugs[$bug_id]['version'],$bugs[$bug_id]['severity'],$bugs[$bug_id]['description']); 143 $_REQUEST['description'] = ''; 144 } 145 } 146 } 147 } 148 } 149 $Generated_Table = ''; 150 $Generated_Scripts = ''; 151 $Generated_Report = ''; 152 153 // Creation de la table 154 if (($data['display']=='BUGS') || ($data['display']=='ALL')) 155 { 156 $Generated_Table = $this->_table_render($bugs,$data); 157 $Generated_Scripts = $this->_scripts_render(); 158 } 159 160 // Count only ... 161 if ($data['display']=='COUNT') 162 { 163 $Generated_Table = $this->_count_render($bugs); 164 } 165 // Generation du form 166 if (($data['display']=='FORM') || ($data['display']=='ALL')) 167 {$Generated_Report = $this->_report_render();} 168 169 // Render 170 $renderer->doc .= $Generated_Header.$Generated_Table.$Generated_Scripts.$Generated_Report; 171 } 172 } 173 174 function _count_render($bugs) 175 { 176 $count = array(); 177 foreach ($bugs as $bug) 178 { 179 $status = $this->_get_one_value($bug,'status'); 180 if ($status != '') 181 if ($this->_get_one_value($count,$status)=='') 182 {$count[$status] = array(1,$status);} 183 else 184 {$count[$status][0] += 1;} 185 } 186 $rendered_count = '<ul>'; 187 foreach ($count as $value) 188 { 189 $rendered_count .= '<li>'.$value[1].' : '.$value[0].'</li>'; 190 } 191 $rendered_count .= '</ul>'; 192 return $rendered_count; 193 } 194 195 function _explode_pref_to_jsarray($pref_name) 196 { 197 $jsarray = ""; 198 $values = explode(',', $this->getConf($pref_name)); 199 foreach ($values as $value) { 200 $jsarray .= '[\''.$value.'\',\''.$value.'\'],'; 201 } 202 return $jsarray; 203 } 204 205 206 207 208 function _edit_scripts_render() 209 { 210 $BASE = DOKU_BASE."lib/plugins/dokumicrobugtracker/"; 211 return " 212 <script type=\"text/javascript\"><!-- 213 jQuery(document).ready(function() { 214 215 //Delete row 216 jQuery('td.deleterow').click( function () { 217 /* Get the position of the current data from the node */ 218 var aPos = oTable.fnGetPosition( this ); 219 /* Get the data array for this row */ 220 var answer = confirm('Are you sure you want to delete this report ?'); 221 if (answer) { 222 jQuery.ajax({ 223 url : '".$BASE."edit.php', 224 type: 'POST', 225 data: 'row_id='+this.parentNode.getAttribute('id')+'&field=delete', 226 method:'POST', 227 }).done(function() { 228 oTable.fnDeleteRow(aPos[0]); 229 }); 230 } 231 }); 232 233 /* Init DataTables */ 234 var oTable = jQuery('.display').dataTable( { 235 \"aaSorting\": [[ 0, \"desc\" ]], 236 \"aLengthMenu\": [[10, 25, 50, -1], [10, 25, 50, \"All\"]], 237 \"bLengthChange\": true, 238 \"bAutoWidth\": true 239 }); 240 241 /* Apply the jEditable handlers to the table */ 242 jQuery('td[class!=\"deleterow\"]', oTable.fnGetNodes()).editable( '".$BASE."edit.php', { 243 \"placeholder\": '', 244 \"callback\": function( sValue, y ) { 245 var aPos = oTable.fnGetPosition( this ); 246 oTable.fnUpdate( sValue, aPos[0], aPos[1] ); 247 }, 248 \"submitdata\": function ( value, settings ) { 249 var oSettings = oTable.fnSettings(); // you can find all sorts of goodies in the Settings 250 var col_id = oSettings.aoColumns[oTable.fnGetPosition( this )[2]].sTitle; //for this code, we just want the sTitle 251 252 253 return { 254 \"row_id\": this.parentNode.getAttribute('id'), 255 \"field\": col_id, 256 257 \"column\": oTable.fnGetPosition( this )[2] 258 }; 259 }, 260 \"height\": \"14px\" 261 }); 262 263 //Bind keydown for auto validation 264 jQuery('td.editbox').bind('keydown', function(event) { 265 if(event.keyCode==9) { 266 jQuery(this).find(\"input\").submit(); 267 if ($(this).is(\".lasteditbox\")) { 268 jQuery(\"td.editbox:first\").click(); 269 } else { 270 jQuery(this).next(\"td.editbox\").click(); 271 } 272 return false; 273 } 274 }); 275 }); 276 // --></script>"; 277 } 278 279 function _default_scripts_render() { 280 $BASE = DOKU_BASE."lib/plugins/dokumicrobugtracker/"; 281 return " 282 <script type=\"text/javascript\"><!-- 283 jQuery(document).ready(function() { 284 /* Init DataTables */ 285 var oTable = jQuery('.display').dataTable( { 286 \"aaSorting\": [[ 0, \"desc\" ]], 287 \"aLengthMenu\": [[10, 25, 50, -1], [10, 25, 50, \"All\"]], 288 \"bLengthChange\": true, 289 \"bAutoWidth\": true 290 }); 291 }); 292 // --></script>"; 293 } 294 295 function _scripts_render() { 296 global $ID; 297 if (auth_quickaclcheck($ID) >= AUTH_ADMIN) { 298 return $this->_edit_scripts_render(); 299 } else { 300 return $this->_default_scripts_render(); 301 } 302 } 303 304 function _table_render($bugs,$data) 305 { 306 global $ID; 307 if (auth_quickaclcheck($ID) >= AUTH_ADMIN) 308 { $head = "<div class='dokumicrobugtracker_div'><table id='".$data['project']."' class=\"display sortable editable inline resizable \"><thead><tr><td id='id'>Id</td><td id='Status'>Status</td><td id='Severity'>Severity</td><td id='Version'>Version</td><td id='Description'>Description</td><td id='Resolution'>Resolution</td><td class=\"noedit nocol\"></td></tr></thead>"; } 309 else 310 { $head = "<div class='dokumicrobugtracker_div'><table id='".$data['project']."' class=\"display sortable inline resizable \"><thead><tr><td id='id'>Id</td><td id='Status'>Status</td><td id='Severity'>Severity</td><td id='Version'>Version</td><td id='Description'>Description</td><td id='Resolution'>Resolution</td></tr></thead>"; } 311 312 $body = "<tbody>"; 313 foreach ($bugs as $bug) 314 { 315 316 317 if (($data['status']=='ALL') || (strtoupper($bug['status'])==$data['status'])) { 318 $body .= '<tr id = "'.$data['project'].' '.$this->_get_one_value($bug,'id').'" class="'.$this->_status_color($bug).'">'. 319 '<td>'.$this->_get_one_value($bug,'id').'</td>'. 320 '<td>'.$this->_get_one_value($bug,'status').'</td>'. 321 '<td>'.$this->_get_one_value($bug,'severity').'</td>'. 322 '<td>'.$this->_get_one_value($bug,'version').'</td>'. 323 '<td class="canbreak">'.$this->_get_one_value($bug,'description').'</td>'. 324 '<td>'.$this->_get_one_value($bug,'resolution').'</td>'; 325 if (auth_quickaclcheck($ID) >= AUTH_ADMIN) { 326 $body .= '<td class="deleterow">Delete</td>'; 327 } 328 $body .= '</tr>'; 329 } 330 } 331 $body .= '</tbody></table></div>'; 332 return $head.$body; 333 } 334 335 function _status_color($bug) { 336 $statuses = explode(',', $this->getConf('statuses')); 337 $key = array_search($bug['status'],$statuses); 338 339 if ($key == 0) 340 return 'status_bad'; 341 if ($key == count($statuses) - 1) 342 return 'status_good'; 343 return 'status_normal'; 344 } 345 346 function _get_one_value($bug, $key) { 347 if (array_key_exists($key,$bug)) 348 return $bug[$key]; 349 return ''; 350 } 351 352 function _emailForNewBug($id,$project,$version,$severity,$description) 353 { 354 if ($this->getConf('send_email')==1) 355 { 356 $body='A new bug have entered in the project : '.$project.' (id : '.$id.")\n\n".' Version : '.$version.' ('.$severity.") :\n".$description; 357 $subject='A new bug have entered in the project : '.$project.' Version :'.$version.' ('.$severity.') : '.$id; 358 $from=$this->getConf('email_address') ; 359 $to=$from; 360 mail_send($to, $subject, $body, $from, $cc='', $bcc='', $headers=null, $params=null); 361 } 362 } 363 364 function _report_render() 365 { 366 global $lang; 367 global $ID; 368 $ret = '<br /><br /><form class="dokumicrobugtracker__form" method="post" action="'.$_SERVER['REQUEST_URI'].'" accept-charset="'.$lang['encoding'].'"><p>'; 369 $ret .= formSecurityToken(false). 370 '<input type="hidden" name="do" value="show" />'. 371 '<input type="hidden" name="id" value="'.$ID.'" />'. 372 '<label> Version : </label><input class="dokumicrobugtracker__option" name="version" type="text" maxlength="20" value="'.$_REQUEST['version'].'"/>'. 373 '<label> Email : </label><input class="dokumicrobugtracker__option" name="email" type="text" value="'.$_REQUEST['email'].'"/></p>'. 374 '<p><label> Severity : </label>'. 375 ' <select class="element select small dokumicrobugtracker__option" name="severity">'; 376 $severities = explode(',', $this->getConf('severities')); 377 foreach ($severities as $severity) { 378 $ret .= '<option value="'.$severity.'" >'.$severity.'</option>'; 379 } 380 $ret .= ' </select></p>'. 381 '<p><label> Description : </label><br /><textarea class="dokumicrobugtracker__option" name="description">'.$_REQUEST['description'].'</textarea></p>'; 382 383 if ($this->getConf('use_captcha')==1) { 384 $helper = null; 385 if(@is_dir(DOKU_PLUGIN.'captcha')) { 386 $helper = plugin_load('helper','captcha'); } 387 if(!is_null($helper) && $helper->isEnabled()) { 388 $ret .= '<p>'.$helper->getHTML().'</p>';} 389 } 390 391 $ret .= '<p><input class="button" type="submit" '. 392 'value="Report" /></p>'. 393 '</form>'; 394 395 return $ret; 396 } 397 398 399 function _show_message($string){ 400 return "<script type='text/javascript'> 401 alert('$string'); 402 </script>"; 403 } 404} 405?> 406