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