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