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