MTrackWeb/Events.php
[web.mtrack] / MTrackWeb / Events.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3  
4 // is this working ???
5 // partly - event.toHTML is borked... - we should use templating for this..
6 require_once 'MTrackWeb/Ticket.php';
7
8 class MTrackWeb_Events extends MTrackWeb_Ticket
9 {
10     var $id; // 0 = new
11     var $issue = false;
12     var $preview;
13     var $error;
14     var $editable;
15     var $tid = 0;
16     
17     function get($pi=0)
18     {
19         $this->id = $pi ?  $pi: (isset($_GET['id']) ? $_GET['id'] : 0);
20         $this->id  = (int) $this->id ;
21         $this->loadIssue();
22         
23         if (!$this->issue) {
24             $this->jerr( "OPPS");
25         }
26  
27         $start = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
28         $limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit'] : 100;
29         
30         // fetch all the 
31         
32         DB_DataObject::debugLevel(1);
33         $d = DB_DataObject::factory('mtrack_change');
34         $d->ontable = 'mtrack_ticket';
35         $d->onid = $this->id;
36         $d->orderBy('changedate ASC'); // last first?
37         $d->limit ($start, $limit);
38         $d->audit = array();
39         $d->find();
40         
41         $this->events = array();
42         while ($d->fetch()) {
43             $this->events[$d->id] = clone($d);
44         }
45         
46         $d = DB_DataObject::factory('mtrack_change_audit');
47         $d->whereAddIn('change_id', array_keys($this->events), 'int');
48         $d->find();
49         
50         while ($d->fetch()) {
51             $this->events[$d->change_id]->audit[] = clone($d);
52         }
53         
54           
55         foreach($this->events as $id=>$c) {
56             echo $this->eventToHtml($c);
57         }
58         exit;
59         //$this->renderEvents();
60     }
61     
62     function eventToHtml($e)
63     {
64         
65     
66         
67         $preamble = 0;
68         
69         $cid = "comment:{$e->id}";
70         
71         // tidied up by jquery..
72         $timestamp = $this->link->date($e->changedate, false);
73          
74   
75         $comments = array();
76
77         // default is that something changed..
78         $type = 'Changed';
79         
80         $comment_body = '';
81         
82         $comment_title = '';
83         $comment_fields = array();
84         
85               
86         if (!empty($e->reason)) {
87             $comments[] = $e->reason;
88             $type = 'Comment added';
89             $comment_title = array_shift(explode("\n", $e->reason));
90         }
91         
92         
93         foreach ($e->audit as $citem) {
94                 //print_r($citem);
95             $main = false;
96             
97             $ar = explode(':', $citem->fieldname, 3);
98             if (count($ar) != 3) {
99                 continue;
100             }
101             
102             list($tbl,,$field) = $ar;
103
104             if ($tbl != 'ticket') {
105                 // can get here if we created a new keyword, for example
106                 //var_dump($citem);
107                 continue;
108             }
109                 
110             switch($field) {
111               
112                 case 'spent': //?? ignore why?
113                     continue;
114                     
115                     
116              
117                 case '@components': 
118                     $comment_fields[] = $field;
119                     $ar = MTrackComponent::loadByIds($citem->value);
120                     $ar = array_map( function($e) { return  $e->toHtml(); }, $ar); 
121                     $citem->value = join(', ', $ar);
122                     
123                     
124                     break;
125                 
126                 case '@milestones': 
127                     $comment_fields[] = $field;
128                     $citem->value = $e->get_milestones_list($citem->value);
129                     break;
130                 
131                 case '@keywords':
132                 
133                 
134                     $comment_fields[] = $field;
135                     $ar = MTrackKeyword::loadByIds($citem->value);
136                     $ar = array_map( function($e) { return  $e->toHtml(); }, $ar); 
137                     $citem->value = join(', ', $ar);
138                     break;
139                     
140                 case  'estimated': 
141                     $comment_fields[] = $field;
142                     if ($citem->value !== null) {
143                         $citem->value += 0;
144                     }
145                     if ($citem->oldvalue !== null) {
146                         $citem->oldvalue += 0;
147                     }
148                     break;
149                 
150                 default:
151                     $comment_fields[] = $field;
152                     if ($field[0] == '@') {
153                         $main = isset($pseudo_fields[$field]) ? $pseudo_fields[$field] : '';
154                         $field = substr($field, 1, -1);
155                     } else {
156                         $main = $e->issue->$field;
157                     }
158
159             }
160
161        
162                 
163             //if ($f) {
164           //      $label = htmlentities($f->label, ENT_QUOTES, 'utf-8');
165            // } else {
166                 if ($field == 'attachment' && strlen($citem->oldvalue)) {
167                     $label = "Attachment: $citem[oldvalue]";
168                 } else {
169                     $label = ucfirst($field);
170                 }
171            // }
172
173 /* what does this do.. why not used set/changed/deleted etc..
174             if ($citem->oldvalue == null) {
175                   // don't bother printing out a set if this is the initial thing
176                    // and if the field values are currently the same 
177
178                 if ($main != $citem['value'] || $e->change_id != 'top') {
179
180                         // Special case for description; since it is multi-line and often
181                         // very large, render it as a diff against the current ticket
182                          // description field  
183                     if ($field == 'description') {
184                         if ($this->issue->description == $citem['value']) {
185                             $comment_body .= "<b>Description</b>: no longer empty; see above<br>";
186                             continue;
187                         }
188
189                         $initial_lines = count(explode("\n", $this->issue->description));
190                         $diff = $this->diff_strings($this->issue->description, $citem['value']);
191                         $diff_add = 0;
192                         $diff_rem = 0;
193                         foreach (explode("\n", $diff) as $line) {
194                                 if (!strlen($line)) continue;
195                                 if ($line[0] == '-') {
196                                   $diff_rem++;
197                                 } else if ($line[0] == '+') {
198                                   $diff_add++;
199                                 }
200                         }
201                         if (abs($diff_add - $diff_rem) > $initial_lines / 2) {
202                             $comment_body .= "<b>initial $label</b><br>" .
203                                     MTrack_Wiki::format_to_html($citem['value']);
204                         } else {
205                                 $diff = $this->collapse_diff($diff);
206                                 $comment_body .= "<b>initial $label</b> (diff to above):<br>$diff\n";
207                         }
208                     } else {
209                           $comment_body .= "<b>$label</b> $citem[value]<br>\n";
210                     }
211                 }
212                 continue;
213             } 
214 */
215             if ($citem->action  == 'changed') {
216                 $lines = explode("\n", $citem->value, 3);
217                 if (count($lines) >= 2) {
218                     $diff = $this->diff_strings($citem->oldvalue, $citem->value);
219                     $diff = $this->collapse_diff($diff);
220                     $comment_body .= "<b>$label</b> $citem[action]\n$diff\n";
221                 } else {
222                     $comment_body .= "<b>$label</b> $citem[action] to $citem[value]<br>\n";
223                 }
224                 continue;
225             } 
226             
227             $comment_body .= "<b>$label</b> $citem[action]<br>\n";
228             
229         }
230         
231         $commit_info = array();
232         if ($comment_title && 
233             preg_match('/\(In \[changeset:([^,]+),([a-z0-9]+)\]\)(.*)$/i', $comment_title, $commit_info)) {
234             $type = 'Commit';
235             
236             
237             $comment_title = '<a class="changesetlink" href="'. $GLOBALS['ABSWEB'] . 'changeset.php/'. 
238                 $commit_info[1].'/'.$commit_info[2]. '">[' . $commit_info[2] . ']</a>'.$commit_info[3];
239             
240             
241         }
242         if ($this->id == $this->issue->created) {
243             $type = 'Created';
244             $comment_title = 'Issue Created';
245         }
246         
247         
248         
249         
250       
251         $html = '
252             <div class="ticketevent">
253                 <span class="ticketevent-expand" id="ticketevent-expand-' . $this->cid . '">+ ' . $type.'</span> 
254                 <a class="pmark" href="#'.$cid.'">#'.$this->cid.'</a> 
255                 
256                 <span class="ticketevent-expand" id="ticketevent-expand-title-' . $this->cid . '">' .
257                 (strlen($comment_title) ? $comment_title : implode(', ', $comment_fields)) . '
258                 </span>
259                 
260                 <span style="float:right">' . 
261                     $link->username($this->who, array('no_image' => true, 'fullname' => true))  . '
262                     -  <a name="'.$cid.'">'.$timestamp.'</a> 
263                 </span>
264                 
265             </div>
266             <div class="ticketchangeinfo" id="ticketchangeinfo-' . $this->cid . '">' . 
267                $link->username($this->who, array('no_name' => true, 'size' => 48));
268                    
269               
270               
271          
272         foreach ($this->effort as $eff) {
273             $exp = (float)$eff['expended'];
274             if ($eff['expended'] != 0) {
275                 $comment_body .= "<b>spent</b> $exp hours<br>\n";
276                 $preamble++;
277             }
278         }
279      
280  
281         if ($preamble) {
282             $html .= "<br>\n";
283             $preamble = 0;
284         }
285         
286         foreach ($comments as $cid => $text) {
287             // look for changesets in the comments..
288             // and display them as expandable linsk..
289             $html .= MTrack_Wiki::format_to_html($text);
290         }
291         
292         
293         $html .= '</div>';
294         
295         
296         
297         if ($commit_info) {
298             // list the files that where changed...
299             //$html.= print_r($M, true);
300             $rp = '/' . $commit_info[1] . '/' . $commit_info[2];
301             $fullrp = $rp;
302             $repo = MTrackSCM::factory($rp);
303             $cd = $repo->historyWithChangelog(null, 1, 'rev', $rp);
304             //print_r($cd);exit; 
305             
306             
307             if (!is_array($cd->ent->files) || !count($cd->ent->files)) {
308                 return $html;
309             }
310             
311             $num = count(array_keys($cd->ent->files));
312             $map = array(
313                 'D' => 'DELETED',
314                 'M' => 'MODIFIED',
315                 'A' => 'ADDED'
316             );
317             
318             $file_summary = array();
319             foreach($map as $k=>$v) {
320                 $summary[$v] = 0;
321             }
322             foreach ($cd->ent->files as $id => $file) {
323                
324                 
325                 $type = isset($map[$file->status])  ? $map[$file->status] : '??? '. $file->status;
326                 
327                 if ($num > 10) 
328                 {
329                     // will bork on unknown...
330                     $summary[$type]++;
331                     continue;
332                 }
333                 
334                 
335             $html .= '
336                 <div class="ticketevent ticketevent-fileitem">
337                     <span class="ticketevent-expand" id="ticketevent-expand-' . $this->cid . '.' . $id .'">+ ' . $type.'</span> 
338                     <span title="' . $fullrp . '?part=' . $id. '"  class="ticketevent-expand" id="ticketevent-expand-title-' . $this->cid . '.' . $id . '">' .
339                     $file->nameToHtml() . '
340                     </span>
341                     <span title="' . $fullrp . '?part=' . $id. '"  class="ticketevent-expand-diff changesetlink">[View Diff]</span> 
342                     <span title="' . $fullrp . '" class="ticketevent-expand-view changesetlink">[View File]</span>
343                     <span title="' . $fullrp . '" class="ticketevent-expand-view changesetlink">[View History]</span>
344                 </div>
345                 <div class="ticketchangeinfo" id="ticketchangeinfo-' . $this->cid . '.' . $id  . '"></div>
346         ';            
347                 
348                 
349                
350             }
351             
352             if ($num>  10) {
353                 $ar = array();
354                 foreach($summary as $k=>$v) {
355                     if (empty($v)) {
356                         continue;
357                     }
358                     $ar[] = $v . ' ' . $k;
359                 }
360                 $html .= '
361                 <div class="ticketevent ticketevent-fileitem">
362                     <span class="ticketevent-expand" id="ticketevent-expand-' . $this->cid . '.0">+ MULTIPLE FILES: </span>
363                     <span title="' . $fullrp . '?part=' . 0 . '"  class="ticketevent-expand" id="ticketevent-expand-title-' . $this->cid . '.' . $id . '">' .
364                     implode(', ', $ar) . '
365                     </span>
366                     <span title="' . $fullrp . '"  class="ticketevent-expand-diff changesetlink">[View Diff]</span> 
367                     <span title="' . $fullrp . '" class="ticketevent-expand-view changesetlink">[View File]</span>
368                     <span title="' . $fullrp . '" class="ticketevent-expand-view changesetlink">[View History]</span>
369                 </div>
370                 <div class="ticketchangeinfo" id="ticketchangeinfo-' . $this->cid . '"></div>
371         ';            
372                 
373             }
374             
375             //$html .= print_r($cd, true);
376              
377         }
378         
379          
380         return $html ;
381                 
382                 
383     }
384     
385     
386     function collapse_diff($diff)
387     {
388       static $idnum = 1;
389       require_once 'MTrackWeb/Changeset.php';
390       $cs = new MTrackWeb_Changeset();
391       $id = 'diff_' . $idnum++;
392       return "<br>" .
393         "<button onclick='\$(&quot;#$id&quot;).toggle(); return false;'>Toggle diff</button>".
394         "<div id='$id' style='display:none'>" .
395             $cs->diff($diff) . "</div>";
396     }
397     
398     function diff_strings($before, $now)
399     {
400          
401         $tempdir = sys_get_temp_dir();
402         $afile = tempnam($tempdir, "mtrack");
403         $bfile = tempnam($tempdir, "mtrack");
404         file_put_contents($afile, $before);
405         file_put_contents($bfile, $now);
406         static $diff = false;
407         if (!$diff) {
408             require_once 'System.php';
409             $diff= System::which('diff');
410         }
411         if (PHP_OS == 'SunOS') {
412             // TODO: make an option to allow use of gnu diff on solaris
413            $diff = shell_exec("$diff -u $afile $bfile");
414            $diff = str_replace($afile, 'before', $diff);
415            $diff = str_replace($bfile, 'now', $diff);
416          } else {
417             $diff = shell_exec("$diff --label before --label now -u $afile $bfile");
418          }
419         unlink($afile);
420         unlink($bfile);
421         $diff = htmlentities($diff, ENT_COMPAT, 'utf-8');
422         return $diff;
423     }
424      
425     
426       
427 }