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