1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 //require_once 'MTrack/Captcha.php';
7 require_once 'MTrackWeb.php';
8 class MTrackWeb_Ticket extends MTrackWeb
15 var $tid = 0; // or the MD5 rep.
22 //require_once 'MTrack/ACL.php';
23 // MTrackACL::requireAllRights('Browser', 'read');
31 if (!isset($_REQUEST['ajax_body'])) {
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;
42 $this->issue = DB_DataObject::factory('mtrack_ticket');
44 if (!$this->id || $ !$this->issue->get($this->id)) {
45 $this->jerr("no such ticket");
53 //$this->issue->augmentFormFields($this->fieldset());
56 $this->preview = false;
57 $this->error = array();
60 if (!$this->id && !$this->hasPerm('MTrack.Issue','A')) {
61 return HTML_FlexyFramework::run('Noperm');
64 !$this->hasPerm('MTrack.Issue','S') || // general permission to view
65 !$this->ticket->hasPerm($this->authUser,'S') // specific permission on this bug.
68 return HTML_FlexyFramework::run('Noperm');
71 // new is always editable..????
72 $this->editable = $this->id ?
73 $this->ticket->hasPerm($this->authUser,'E') : true;
75 $this->issue->milestoneURL = $this->baseURL.'/Milestone'; // fix me later..
77 $this->showEditBar = false;
79 if ($this->editable && $this->id && !$this->preview) {
80 $this->showEditBar = true;
83 $this->initEditForm();
89 function post($pi=0) // handle the post...
94 if (isset($_POST['cancel'])) {
95 header("Location: {$$this->baseURL}/Ticket/$this->issue->nsident");
99 if (!MTrack_Captcha::check('ticket')) {
100 $this->error[] = "CAPTCHA failed, please try again";
102 $this->preview = isset($_POST['preview']) ? true : false;
107 MTrackACL::requireAllRights("Tickets", 'create');
109 MTrackACL::requireAllRights("ticket:" . $this->issue->tid, 'modify');
111 } catch (Exception $e) {
112 $this->error[] = $e->getMessage();
116 $comment = empty($_POST['comment']) ? '' : $_POST['comment'];
119 if (!strlen($comment)) {
120 $comment = $_POST['summary'];
123 $CS = MTrackChangeset::begin("ticket:X", $comment);
124 } catch (Exception $e) {
125 $this->error[] = $e->getMessage();
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();
138 switch($db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
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+$'";
146 $max = "select max(cast(nsident as UNSIGNED)) + 1 from tickets";
150 $max = 'select max(cast(nsident as integer)) + 1 from tickets';
156 list($this->issue->nsident) = MTrackDB::q($max)->fetchAll(PDO::FETCH_COLUMN, 0);
157 if ($this->issue->nsident === null) {
158 $this->issue->nsident = 1;
162 if (isset($_POST['action']) && !$this->preview) {
163 $act= explode('_', $_POST['action'] , 2);
164 //var_dump($act);exit;
169 $this->issue->reOpen();
172 $this->issue->resolution = 'fixed';
173 $this->issue->close();
174 $_POST['estimated'] = $this->issue->estimated;
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';
188 //$this->issue->resolution = $_POST['resolution'];
189 $this->issue->resolution = $act[1];
190 $this->issue->close();
191 $_POST['estimated'] = $this->issue->estimated;
195 $this->issue->status = $act[1];
211 $this->issue->applyPOSTData($_POST);
215 foreach ($fields as $fieldname) {
216 if (isset($_POST[$fieldname]) && strlen($_POST[$fieldname])) {
217 $this->issue->$fieldname = $_POST[$fieldname];
219 $this->issue->$fieldname = null;
223 $kw = $this->issue->getKeywords();
224 $kill = array_values($kw);
225 foreach (preg_split('/[ \t,]+/', $_POST['keywords']) as $w) {
229 $x = array_search($w, $kw);
231 $k = MTrackKeyword::loadByWord($w);
233 $k = new MTrackKeyword;
237 $this->issue->assocKeyword($k);
239 $w = array_search($w, $kill);
245 foreach ($kill as $w) {
246 $this->issue->dissocKeyword($w);
249 $ms = $this->issue->getMilestones();
251 if (isset($_POST['milestone']) && is_array($_POST['milestone'])) {
252 foreach ($_POST['milestone'] as $mid) {
253 $this->issue->assocMilestone($mid);
257 foreach ($kill as $mid) {
258 $this->issue->dissocMilestone($mid);
261 $ms = $this->issue->getComponents();
263 if (isset($_POST['component']) && is_array($_POST['component'])) {
264 foreach ($_POST['component'] as $mid) {
265 $this->issue->assocComponent($mid);
269 foreach ($kill as $mid) {
270 $this->issue->dissocComponent($mid);
273 if (!empty($_POST['comment'])) {
274 $this->issue->addComment($_POST['comment']);
277 $this->issue->addEffort(
278 empty($_POST['spent']) ? 0 : $_POST['spent'],
279 empty($_POST['estimate']) ? 0 : $_POST['estimate']
282 if (!count($this->error)) {
284 $this->issue->save($CS);
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);
292 if ($this->id == 'new') {
293 MTrackWatch::watch_object('ticket', $this->issue->tid, MTrackAuth::whoami());
297 $CS->setObject("ticket:" . $this->issue->tid);
298 } catch (Exception $e) {
299 $this->error[] = $e->getMessage();
303 if (!count($this->error)) {
304 if (!empty($_FILES['attachments'])) {
305 require_once 'MTrack/Attachment.php';
306 foreach ($_FILES['attachments']['name'] as $fileid => $name) {
308 MTrackAttachment::add("ticket:{$this->issue->tid}",
309 $_FILES['attachments']['tmp_name'][$fileid],
310 $_FILES['attachments']['name'][$fileid],
316 if (!count($this->error) && $this->id != 'new') {
317 require_once 'MTrack/Attachment.php';
318 MTrackAttachment::process_delete("ticket:{$this->issue->tid}", $CS);
321 if (isset($_POST['apply']) && !count($this->error)) {
323 header("Location: {$this->baseURL}/Ticket/{$this->issue->nsident}");
329 function initEditForm($params = array())
331 require_once 'HTML/Template/Flexy/Element.php';
332 require_once 'HTML/Template/Flexy/Factory.php';
333 $this->elements = array();
337 foreach(array( 'classification', 'priority', 'severity', 'resolution' ) as $c) {
338 $d = DB_DataObject::factory('core_enum');
340 $d->orderBy('seqid ASC, name ASC');
342 $d->createBaseEntries();
345 $this->elements[$c] = new HTML_Template_Flexy_Element('select');
346 $this->elements[$c]->setOptions($d->fetchAll('id','name'));
350 if ($this->issue->project_id) {
351 $d = DB_DataObject::factory('mtrack_project_component');
352 $d->project_id = $this->issue->project_id;
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));
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));
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???
379 $this->elements['owner'] = new HTML_Template_Flexy_Element('select');
380 $this->elements['owner']->setOptions($users);
384 // keywords -- in toArray...
388 $this->change_status = array();
389 $this->resolve_status = array();
392 // for coder's they can only change this ticke to certian states
395 // Nasty - I really do not like the acl code in this ...
396 require_once 'MTrack/TicketState.php';
398 $ST = new MTrackTicketState;
399 $ST = $ST->enumerate();
401 unset($ST['closed']);
402 unset($ST[$this->issue->status]);
404 $this->change_status = empty($ST) ? array() : array_keys($ST);
406 $ac = MTrackAuth::getUserClass($this->authUser->userid);
407 //var_dump($ac);exit;
408 // KLUDGE! - remove later...
409 if ($ac == 'admin') {
411 $this->resolve_status= array('fixed');
412 $R = new MTrackResolution;
413 $resolutions = $R->enumerate();
414 unset($resolutions['fixed']);
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 );
422 // $html .= mtrack_radio('action', 'reopen', $_POST['action']);
423 // $html .= " <label for='reopen'>Reopen ticket</label><br>\n";
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);