MTrackWeb/Ticket.php
[web.mtrack] / MTrackWeb / Ticket.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3  
4 //require_once 'MTrack/Captcha.php';
5
6  
7 require_once 'MTrackWeb.php';
8 class MTrackWeb_Ticket extends MTrackWeb 
9 {
10     var $id; // 0 = new
11     var $issue;
12     var $preview;
13     var $error;
14     var $editable;
15     var $tid = 0; // or the MD5 rep.
16     
17      
18     
19      function getAuth() 
20     {
21         parent::getAuth();
22         //require_once 'MTrack/ACL.php';
23      //   MTrackACL::requireAllRights('Browser', 'read');
24         return true;
25   
26     }
27     
28     function get($pi= 0)
29     {
30     
31         if (!isset($_REQUEST['ajax_body'])) {
32             return;
33         }
34         $this->masterTempalte = 'ticket.html';
35         $this->id = $pi ?  $pi: (isset($_GET['id']) ? $_GET['id'] : 0);
36         $this->id  = $this->id  == 'new' ? 0 : $this->id;
37         $this->id  = (int) $this->id;
38         
39     
40         // -- load issue..
41
42         $this->issue = DB_DataObject::factory('mtrack_ticket');
43
44         if ($this->id) {
45             if (!$this->issue->get($this->id)) {
46                 throw new Exception("Invalid ticket $this->id");
47             }
48         }
49         
50         
51         
52         
53         
54         //$this->issue->augmentFormFields($this->fieldset());
55
56
57         $this->preview = false;
58         $this->error = array();
59        
60        
61         if (!$this->id && !$this->hasPerm('MTrack.Issue','A')) {
62             return HTML_FlexyFramework::run('Noperm');
63         }
64         if ($this->id &&  (
65                 !$this->hasPerm('MTrack.Issue','S') || // general permission to view
66                 !$this->ticket->hasPerm($this->authUser,'S') // specific permission on this bug.
67                 
68             )) {
69             return HTML_FlexyFramework::run('Noperm');
70         }
71         
72         // new is always editable..????
73         $this->editable = $this->id ?
74             $this->ticket->hasPerm($this->authUser,'E')  : true;
75          
76         $this->issue->milestoneURL = $this->baseURL.'/Milestone'; // fix me later..
77     
78         $this->showEditBar = false;
79          
80         if ($this->editable && $this->id   && !$this->preview) {
81             $this->showEditBar = true;
82         }
83          
84         $this->initEditForm();
85  
86         
87       
88     }
89    
90     function post($pi=0) // handle the post...
91     {
92         die("TODO");
93        $this->get($pi);
94        
95         if (isset($_POST['cancel'])) {
96             header("Location: {$$this->baseURL}/Ticket/$this->issue->nsident");
97             exit;
98           }
99           
100           if (!MTrack_Captcha::check('ticket')) {
101             $this->error[] = "CAPTCHA failed, please try again";
102           }
103           $this->preview = isset($_POST['preview']) ? true : false;
104
105           $comment = '';
106           try {
107             if (!$this->id) {
108               MTrackACL::requireAllRights("Tickets", 'create');
109             } else {
110               MTrackACL::requireAllRights("ticket:" . $this->issue->tid, 'modify');
111             }
112           } catch (Exception $e) {
113             $this->error[] = $e->getMessage();
114           }
115           
116           if (!$this->id) {
117             $comment = empty($_POST['comment']) ? '' : $_POST['comment'];
118           }
119           
120           if (!strlen($comment)) {
121             $comment = $_POST['summary'];
122           }
123           try {
124             $CS = MTrackChangeset::begin("ticket:X", $comment);
125           } catch (Exception $e) {
126             $this->error[] = $e->getMessage();
127             $CS = null;
128           }
129           if (!$this->id) {
130             // compute next id number.
131             // We don't use auto-number, because we allow for importing multiple
132             // projects with their own ticket sequence.
133             // During "normal" user-driven operation, we do want plain old id numbers
134             // so we compute it here, under a transaction
135             $db = MTrackDB::get();
136             
137             
138             
139             switch($db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
140                 case 'pgsql':
141                     // Some versions of postgres don't like that we have "abc123" for
142                     // identifiers, so match on the bigest number nsident fields only
143                     $max = "select max(cast(nsident as integer)) + 1 from tickets where nsident ~ '^\\\\d+$'";
144                     break;
145                 
146                 case 'mysql':
147                     $max = "select max(cast(nsident as UNSIGNED)) + 1 from tickets";
148                     break;
149                 
150                 default:
151                     $max = 'select max(cast(nsident as integer)) + 1 from tickets';   
152                     break;
153             }
154             
155             
156              
157             list($this->issue->nsident) = MTrackDB::q($max)->fetchAll(PDO::FETCH_COLUMN, 0);
158             if ($this->issue->nsident === null) {
159               $this->issue->nsident = 1;
160             }
161           }
162
163           if (isset($_POST['action']) && !$this->preview) {
164             $act= explode('_', $_POST['action'] , 2);
165             //var_dump($act);exit;
166             switch ($act[0]) {
167               case 'leave':
168                 break;
169               case 'reopen':
170                 $this->issue->reOpen();
171                 break;
172               case 'fixed':
173                 $this->issue->resolution = 'fixed';
174                 $this->issue->close();
175                 $_POST['estimated'] = $this->issue->estimated;
176                 break;
177                 
178               
179               case 'accept':
180                 // will be applied to the issue further down
181                 $_POST['owner'] = MTrackAuth::whoami();
182                 if ($this->issue->status == 'new') {
183                   $this->issue->status = 'open';
184                 }
185                 break;
186                 
187                 
188               case 'resolve':
189                 //$this->issue->resolution = $_POST['resolution'];
190                 $this->issue->resolution = $act[1];
191                 $this->issue->close();
192                 $_POST['estimated'] = $this->issue->estimated;
193                 break;  
194                 
195               case 'change':
196                 $this->issue->status = $act[1];
197                 break;
198             }
199           }
200
201           $fields = array(
202             'summary',
203             'description',
204             'classification',
205             'priority',
206             'severity',
207             'changelog',
208             'owner',
209             'cc',
210           );
211
212           $this->issue->applyPOSTData($_POST);
213
214          
215           
216           foreach ($fields as $fieldname) {
217             if (isset($_POST[$fieldname]) && strlen($_POST[$fieldname])) {
218               $this->issue->$fieldname = $_POST[$fieldname];
219             } else {
220               $this->issue->$fieldname = null;
221             }
222           }
223
224           $kw = $this->issue->getKeywords();
225           $kill = array_values($kw);
226           foreach (preg_split('/[ \t,]+/', $_POST['keywords']) as $w) {
227             if (!strlen($w)) {
228               continue;
229             }
230             $x = array_search($w, $kw);
231             if ($x === false) {
232               $k = MTrackKeyword::loadByWord($w);
233               if ($k === null) {
234                 $k = new MTrackKeyword;
235                 $k->keyword = $w;
236                 $k->save($CS);
237               }
238               $this->issue->assocKeyword($k);
239             } else {
240               $w = array_search($w, $kill);
241               if ($w !== false) {
242                 unset($kill[$w]);
243               }
244             }
245           }
246           foreach ($kill as $w) {
247             $this->issue->dissocKeyword($w);
248           }
249
250           $ms = $this->issue->getMilestones();
251           $kill = $ms;
252           if (isset($_POST['milestone']) && is_array($_POST['milestone'])) {
253             foreach ($_POST['milestone'] as $mid) {
254               $this->issue->assocMilestone($mid);
255               unset($kill[$mid]);
256             }
257           }
258           foreach ($kill as $mid) {
259             $this->issue->dissocMilestone($mid);
260           }
261
262           $ms = $this->issue->getComponents();
263           $kill = $ms;
264           if (isset($_POST['component']) && is_array($_POST['component'])) {
265             foreach ($_POST['component'] as $mid) {
266               $this->issue->assocComponent($mid);
267               unset($kill[$mid]);
268             }
269           }
270           foreach ($kill as $mid) {
271             $this->issue->dissocComponent($mid);
272           }
273           
274             if (!empty($_POST['comment'])) {
275                $this->issue->addComment($_POST['comment']);
276             }
277           
278           $this->issue->addEffort(
279             empty($_POST['spent']) ? 0 : $_POST['spent'], 
280             empty($_POST['estimate']) ? 0 : $_POST['estimate']
281         );
282
283           if (!count($this->error)) {
284             try {
285               $this->issue->save($CS);
286               
287               // make sure everyone is watching it!!!!
288                 if($this->issue->owner && $this->issue->tid) {
289                   // make sure owner is tracking it...
290                     MTrackWatch::watch_object('ticket', $this->issue->tid,  $this->issue->owner);
291                 }
292                 
293                 if ($this->id == 'new') {
294                     MTrackWatch::watch_object('ticket', $this->issue->tid,  MTrackAuth::whoami());
295                 }
296               
297               
298               $CS->setObject("ticket:" . $this->issue->tid);
299             } catch (Exception $e) {
300               $this->error[] = $e->getMessage();
301             }
302         }
303
304         if (!count($this->error)) {
305             if (!empty($_FILES['attachments'])) {
306                 require_once 'MTrack/Attachment.php';
307                 foreach ($_FILES['attachments']['name'] as $fileid => $name) {
308                       
309                     MTrackAttachment::add("ticket:{$this->issue->tid}",
310                         $_FILES['attachments']['tmp_name'][$fileid],
311                         $_FILES['attachments']['name'][$fileid],
312                         $CS
313                     );
314                 }
315             }
316         }
317         if (!count($this->error) && $this->id != 'new') {
318             require_once 'MTrack/Attachment.php';
319             MTrackAttachment::process_delete("ticket:{$this->issue->tid}", $CS);
320         }
321
322         if (isset($_POST['apply']) && !count($this->error)) {
323           $CS->commit();
324           header("Location: {$this->baseURL}/Ticket/{$this->issue->nsident}");
325           exit;
326         }
327     }
328       
329          
330     function initEditForm($params = array())
331     {
332         require_once 'HTML/Template/Flexy/Element.php';
333         require_once 'HTML/Template/Flexy/Factory.php';
334         $this->elements = array();
335         
336         
337         
338         foreach(array( 'classification', 'priority', 'severity', 'resolution' ) as $c)  {
339             $d = DB_DataObject::factory('core_enum');
340             $d->etype = $c;
341             $d->orderBy('seqid ASC, name ASC');
342             if (!$d->count()) {
343                 $d->createBaseEntries();
344                 
345             }
346             $this->elements[$c] = new HTML_Template_Flexy_Element('select');
347             $this->elements[$c]->setOptions($d->fetchAll('id','name'));
348             
349         }
350         
351         if ($this->issue->project_id) {
352             $d = DB_DataObject::factory('mtrack_project_component');
353             $d->project_id = $this->issue->project_id;
354             $d->orderBy('name');
355             $d->whereAdd('deleted != 1');
356             $this->elements['component[]'] = new HTML_Template_Flexy_Element('select');
357             $this->elements['component[]']->setOptions($d->fetchAll('id', 'name'));
358             $ar = $this->issue->components();
359             $this->elements['component[]']->setValue(array_keys($ar));
360         
361         }
362         if ($this->issue->project_id) {
363             $d = DB_DataObject::factory('mtrack_milestone');
364             $d->project_id = $this->issue->project_id;
365             $d->orderBy('(case when duedate is null then 1 else 0 end), duedate, name');
366             $d->whereAdd('completed != 1');
367             $d->whereAdd('deleted != 1');
368             $this->elements['milestone[]'] = new HTML_Template_Flexy_Element('select');
369             $this->elements['milestone[]']->setOptions($d->fetchAll('id', 'name'));
370             $ar = $this->issue->milestones();
371             $this->elements['milestone[]']->setValue(array_keys($ar));
372         }
373         
374         // FIX ME - need to determine who the owner is..
375         // for a new issue it's the person who created it.
376         // later on it's an assignement???
377         
378         $users = array();
379          
380         $this->elements['owner'] = new HTML_Template_Flexy_Element('select');
381         $this->elements['owner']->setOptions($users);
382         
383         
384         
385         // keywords -- in toArray...
386         // milestone 
387           
388           
389           $this->change_status = array();
390           $this->resolve_status = array();
391           if ($this->id) {
392          
393             // for coder's they can only change this ticke to certian states
394             
395             //print_r($groups);
396             // Nasty - I really do not like the acl code in this ...
397             require_once 'MTrack/TicketState.php';
398             
399             $ST = new MTrackTicketState;
400             $ST = $ST->enumerate();
401             //print_r($ST);
402             unset($ST['closed']);
403             unset($ST[$this->issue->status]);
404            
405             $this->change_status = empty($ST) ? array() : array_keys($ST);
406             
407             $ac = MTrackAuth::getUserClass($this->authUser->userid);
408             //var_dump($ac);exit;
409 // KLUDGE! - remove later...
410             if ($ac == 'admin') {
411                 
412                 $this->resolve_status= array('fixed');
413                 $R = new MTrackResolution;
414                 $resolutions = $R->enumerate();
415                 unset($resolutions['fixed']);
416                 
417                 $this->resolve_status= array_keys($resolutions);
418                 array_unshift($this->resolve_status, 'fixed');
419                // $html .= $this->mtrack_chg_status('action', 'resolve', 'Resolve as:', 'resolution', $resolutions, $this->issue );
420             } 
421
422            // } else {
423            //   $html .= mtrack_radio('action', 'reopen', $_POST['action']);
424            //   $html .= " <label for='reopen'>Reopen ticket</label><br>\n";
425            // }
426            
427         }
428         $this->elements = HTML_Template_Flexy_Factory::fromArray($this->issue->toArray(), $this->elements);
429         if (!empty($_POST)) {
430             $this->elements = HTML_Template_Flexy_Factory::fromArray($_POST, $this->elements);
431         }
432         
433         
434
435           
436     }
437    
438      
439     function eq($a,$b) {
440         return $a == $b;
441     }
442     
443 }