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