diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 38c1490967ccb8ae1b2ef31be678981bab239a2f..ab44bd3e3e0ac39343c5425ccec2f612d90f4445 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -238,6 +238,9 @@ class TicketsAjaxAPI extends AjaxController { $options[]=array('action'=>'Post Note','url'=>"tickets.php?id=$tid#note"); + if($thisstaff->canEditTickets()) + $options[]=array('action'=>'Edit Ticket','url'=>"tickets.php?id=$tid&a=edit"); + if($options) { echo '<ul class="tip_menu">'; foreach($options as $option) diff --git a/include/class.sla.php b/include/class.sla.php index 9ea62b0527fbc64ede3b94b51f6f9a1a9b91243a..cbd9aa43083643bdae20a9625207162b0c832f38 100644 --- a/include/class.sla.php +++ b/include/class.sla.php @@ -19,91 +19,113 @@ class SLA { var $info; - function SLA($id){ + function SLA($id) { $this->id=0; $this->load($id); } - function load($id) { + function load($id=0) { + + if(!$id && !($id=$this->getId())) + return false; $sql='SELECT * FROM '.SLA_TABLE.' WHERE id='.db_input($id); - if(($res=db_query($sql)) && db_num_rows($res)) { - $info=db_fetch_array($res); - $this->id=$info['id']; - $this->info=$info; - return true; - } - return false; + if(!($res=db_query($sql)) || !db_num_rows($res)) + return false; + + $this->ht=db_fetch_array($res); + $this->id=$this->ht['id']; + return true; } function reload() { - return $this->load($this->getId()); + return $this->load(); } - function getId(){ + function getId() { return $this->id; } - function getName(){ - return $this->info['name']; + function getName() { + return $this->ht['name']; } - function getGracePeriod(){ - return $this->info['grace_period']; + function getGracePeriod() { + return $this->ht['grace_period']; } - function getNotes(){ - return $this->info['notes']; + function getNotes() { + return $this->ht['notes']; } - function getInfo(){ - return $this->info; + function getHashtable() { + return $this->ht; } - function isActive(){ - return ($this->info['isactive']); + function getInfo() { + return $this->getHashtable(); } - function sendAlerts(){ - return (!$this->info['disable_overdue_alerts']); + function isActive() { + return ($this->ht['isactive']); } - function priorityEscalation(){ - return ($this->info['enable_priority_escalation']); + function sendAlerts() { + return (!$this->ht['disable_overdue_alerts']); } - function update($vars,&$errors){ - if(SLA::save($this->getId(),$vars,$errors)){ - $this->reload(); - return true; - } + function priorityEscalation() { + return ($this->ht['enable_priority_escalation']); + } + + function update($vars,&$errors) { - return false; + if(!SLA::save($this->getId(),$vars,$errors)) + return false; + + $this->reload(); + + return true; } - function delete(){ + function delete() { global $cfg; - if($cfg && $cfg->getDefaultSLAId()==$this->getId()) + if(!$cfg || $cfg->getDefaultSLAId()==$this->getId()) return false; $id=$this->getId(); $sql='DELETE FROM '.SLA_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; - if(db_query($sql) && ($num=db_affected_rows())){ + if(db_query($sql) && ($num=db_affected_rows())) { db_query('UPDATE '.DEPT_TABLE.' SET sla_id=0 WHERE sla_id='.db_input($id)); db_query('UPDATE '.TOPIC_TABLE.' SET sla_id=0 WHERE sla_id='.db_input($id)); - db_query('UPDATE '.TICKET_TABLE.' SET sla_id=0 WHERE sla_id='.db_input($id)); + db_query('UPDATE '.TICKET_TABLE.' SET sla_id='.db_input($cfg->getDefaultSLAId()).' WHERE sla_id='.db_input($id)); } return $num; } /** static functions **/ - function create($vars,&$errors){ + function create($vars,&$errors) { return SLA::save(0,$vars,$errors); } - function getIdByName($name){ + function getSLAs() { + + $slas=array(); + + $sql='SELECT id, name FROM '.SLA_TABLE; + if(($res=db_query($sql)) && db_num_rows($res)) { + while(list($id, $name)=db_fetch_row($res)) + $slas[$id]=$name; + + } + + return $slas; + } + + + function getIdByName($name) { $sql='SELECT id FROM '.SLA_TABLE.' WHERE name='.db_input($name); if(($res=db_query($sql)) && db_num_rows($res)) @@ -112,11 +134,11 @@ class SLA { return $id; } - function lookup($id){ + function lookup($id) { return ($id && is_numeric($id) && ($sla= new SLA($id)) && $sla->getId()==$id)?$sla:null; } - function save($id,$vars,&$errors){ + function save($id,$vars,&$errors) { if(!$vars['grace_period']) diff --git a/include/class.ticket.php b/include/class.ticket.php index 65d8caf9b2bfd266b64040a992806f6846e3a6d7..e887daae7d58ea040d13d72ed342f453fc801290 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -24,6 +24,7 @@ include_once(INCLUDE_DIR.'class.attachment.php'); include_once(INCLUDE_DIR.'class.banlist.php'); include_once(INCLUDE_DIR.'class.template.php'); include_once(INCLUDE_DIR.'class.priority.php'); +include_once(INCLUDE_DIR.'class.sla.php'); class Ticket{ @@ -285,6 +286,28 @@ class Ticket{ return $this->ht['ip_address']; } + function getHashtable() { + return $this->ht; + } + + function getUpdateInfo() { + + $info=array('name' => $this->getName(), + 'email' => $this->getEmail(), + 'phone' => $this->getPhone(), + 'phone_ext' => $this->getPhoneExt(), + 'subject' => $this->getSubject(), + 'source' => $this->getSource(), + 'topicId' => $this->getTopicId(), + 'priorityId' => $this->getPriorityId(), + 'slaId' => $this->getSLAId(), + 'duedate' => $this->getDueDate()?(Format::userdate('m/d/Y', Misc::db2gmtime($this->getDueDate()))):'', + 'time' => $this->getDueDate()?(Format::userdate('G:i', Misc::db2gmtime($this->getDueDate()))):'', + ); + + return $info; + } + function getLockId() { return $this->lock_id; } @@ -362,6 +385,17 @@ class Ticket{ return ''; } + function getAssignees() { + + $assignees=''; + if($staff=$this->getStaff()) + $assignees.=$staff->getName(); + + if($team=$this->getTeam()) + $assignees.=$team->getName(); + + return $assignees; + } function getTopicId(){ return $this->topic_id; @@ -1459,8 +1493,7 @@ class Ticket{ function deleteAttachments(){ - global $cfg; - + $deleted=0; // Clear reference table $res=db_query('DELETE FROM '.TICKET_ATTACHMENT_TABLE.' WHERE ticket_id='.db_input($this->getId())); @@ -1473,17 +1506,90 @@ class Ticket{ function delete(){ + $sql='DELETE FROM '.TICKET_TABLE.' WHERE ticket_id='.$this->getId().' LIMIT 1'; + if(!db_query($sql) || !db_affected_rows()) + return false; + + db_query('DELETE FROM '.TICKET_MESSAGE_TABLE.' WHERE ticket_id='.db_input($this->getId())); + db_query('DELETE FROM '.TICKET_RESPONSE_TABLE.' WHERE ticket_id='.db_input($this->getId())); + db_query('DELETE FROM '.TICKET_NOTE_TABLE.' WHERE ticket_id='.db_input($this->getId())); + $this->deleteAttachments(); + + return true; + } + + function update($vars, &$errors) { + + global $cfg, $thisstaff; + + if(!$cfg || !$thisstaff || !$thisstaff->canEditTickets()) + return false; + + $fields=array(); + $fields['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); + $fields['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Valid email required'); + $fields['subject'] = array('type'=>'string', 'required'=>1, 'error'=>'Subject required'); + $fields['topicId'] = array('type'=>'int', 'required'=>1, 'error'=>'Help topic required'); + $fields['slaId'] = array('type'=>'int', 'required'=>1, 'error'=>'SLA required'); + $fields['priorityId'] = array('type'=>'int', 'required'=>1, 'error'=>'Priority required'); + $fields['phone'] = array('type'=>'phone', 'required'=>0, 'error'=>'Valid phone # required'); + $fields['duedate'] = array('type'=>'date', 'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY'); + + $fields['note'] = array('type'=>'text', 'required'=>1, 'error'=>'Reason for the update required'); + + if(!Validator::process($fields, $vars, $errors) && !$errors['err']) + $errors['err'] ='Missing or invalid data - check the errors and try again'; + + if($vars['duedate']) { + if($this->isClosed()) + $errors['duedate']='Duedate can NOT be set on a closed ticket'; + elseif(!$vars['time'] || strpos($vars['time'],':')===false) + $errors['time']='Select time'; + elseif(strtotime($vars['duedate'].' '.$vars['time'])===false) + $errors['duedate']='Invalid duedate'; + elseif(strtotime($vars['duedate'].' '.$vars['time'])<=time()) + $errors['duedate']='Due date must be in the future'; + } + + //Make sure phone extension is valid + if($vars['phone_ext'] ) { + if(!is_numeric($vars['phone_ext']) && !$errors['phone']) + $errors['phone']='Invalid phone ext.'; + elseif(!$vars['phone']) //make sure they just didn't enter ext without phone # + $errors['phone']='Phone number required'; + } + + if($errors) return false; + + $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() ' + .' ,email='.db_input($vars['email']) + .' ,name='.db_input(Format::striptags($vars['name'])) + .' ,subject='.db_input(Format::striptags($vars['subject'])) + .' ,phone="'.db_input($vars['phone'],false).'"' + .' ,phone_ext='.db_input($vars['phone_ext']?$vars['phone_ext']:NULL) + .' ,priority_id='.db_input($vars['priorityId']) + .' ,topic_id='.db_input($vars['topicId']) + .' ,sla_id='.db_input($vars['slaId']) + .' ,duedate='.($vars['duedate']?db_input(date('Y-m-d G:i',Misc::dbtime($vars['duedate'].' '.$vars['time']))):'NULL'); + + if($vars['duedate']) { //We are setting new duedate... + $sql.=' ,isoverdue=0'; + } + + $sql.=' WHERE ticket_id='.db_input($this->getId()); + + if(!db_query($sql) || !db_affected_rows()) + return false; + + if(!$vars['note']) + $vars['note']=sprintf('Ticket Updated by %s', $thisstaff->getName()); + + $this->postNote('Ticket Updated', $vars['note']); + $this->reload(); - if(db_query('DELETE FROM '.TICKET_TABLE.' WHERE ticket_id='.$this->getId().' LIMIT 1') && db_affected_rows()): - db_query('DELETE FROM '.TICKET_MESSAGE_TABLE.' WHERE ticket_id='.db_input($this->getId())); - db_query('DELETE FROM '.TICKET_RESPONSE_TABLE.' WHERE ticket_id='.db_input($this->getId())); - db_query('DELETE FROM '.TICKET_NOTE_TABLE.' WHERE ticket_id='.db_input($this->getId())); - $this->deleteAttachments(); - return TRUE; - endif; - - return FALSE; + return true; } + /*============== Static functions. Use Ticket::function(params); ==================*/ function getIdByExtId($extid) { @@ -1599,84 +1705,6 @@ class Ticket{ return db_fetch_array(db_query($sql)); } - //FIXME: Refactor the code for version 1.7 - function update($var,&$errors) { - global $cfg,$thisstaff; - - $fields=array(); - $fields['name'] = array('type'=>'string', 'required'=>1, 'error'=>'Name required'); - $fields['email'] = array('type'=>'email', 'required'=>1, 'error'=>'Email is required'); - $fields['note'] = array('type'=>'text', 'required'=>1, 'error'=>'Reason for the update required'); - $fields['subject'] = array('type'=>'string', 'required'=>1, 'error'=>'Subject required'); - $fields['topicId'] = array('type'=>'int', 'required'=>0, 'error'=>'Invalid Selection'); - $fields['pri'] = array('type'=>'int', 'required'=>0, 'error'=>'Invalid Priority'); - $fields['phone'] = array('type'=>'phone', 'required'=>0, 'error'=>'Valid phone # required'); - $fields['duedate'] = array('type'=>'date', 'required'=>0, 'error'=>'Invalid date - must be MM/DD/YY'); - - - $params = new Validator($fields); - if(!$params->validate($var)){ - $errors=array_merge($errors,$params->errors()); - } - - if($var['duedate']){ - if($this->isClosed()) - $errors['duedate']='Duedate can NOT be set on a closed ticket'; - elseif(!$var['time'] || strpos($var['time'],':')===false) - $errors['time']='Select time'; - elseif(strtotime($var['duedate'].' '.$var['time'])===false) - $errors['duedate']='Invalid duedate'; - elseif(strtotime($var['duedate'].' '.$var['time'])<=time()) - $errors['duedate']='Due date must be in the future'; - } - - //Make sure phone extension is valid - if($var['phone_ext'] ) { - if(!is_numeric($var['phone_ext']) && !$errors['phone']) - $errors['phone']='Invalid phone ext.'; - elseif(!$var['phone']) //make sure they just didn't enter ext without phone # - $errors['phone']='Phone number required'; - } - - $cleartopic=false; - $topicDesc=''; - if($var['topicId'] && ($topic= new Topic($var['topicId'])) && $topic->getId()) { - $topicDesc=$topic->getName(); - }elseif(!$var['topicId'] && $this->getTopicId()){ - $topicDesc=''; - $cleartopic=true; - } - - - if(!$errors){ - $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() '. - ',email='.db_input($var['email']). - ',name='.db_input(Format::striptags($var['name'])). - ',subject='.db_input(Format::striptags($var['subject'])). - ',phone="'.db_input($var['phone'],false).'"'. - ',phone_ext='.db_input($var['phone_ext']?$var['phone_ext']:NULL). - ',priority_id='.db_input($var['pri']). - ',topic_id='.db_input($var['topicId']). - ',duedate='.($var['duedate']?db_input(date('Y-m-d G:i',Misc::dbtime($var['duedate'].' '.$var['time']))):'NULL'); - if($var['duedate']) { //We are setting new duedate... - $sql.=',isoverdue=0'; - } - if($topicDesc || $cleartopic) { //we're overwriting previous topic. - $sql.=',helptopic='.db_input($topicDesc); - } - $sql.=' WHERE ticket_id='.db_input($this->getId()); - //echo $sql; - if(db_query($sql)){ - $this->postNote('Ticket Updated',$var['note']); - $this->reload(); - return true; - } - } - - return false; - } - - /* * The mother of all functions...You break it you fix it! * @@ -1805,7 +1833,7 @@ class Ticket{ // OK...just do it. $deptId=$vars['deptId']; //pre-selected Dept if any. - $priorityId=$vars['pri']; + $priorityId=$vars['priorityId']; $source=ucfirst($vars['source']); $topic=NULL; // Intenal mapping magic...see if we need to overwrite anything diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..db80cf2f73fc53853905be23eaa6430e7eefd1de --- /dev/null +++ b/include/staff/ticket-edit.inc.php @@ -0,0 +1,173 @@ +<?php +if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canEditTickets() || !$ticket) die('Access Denied'); + +$info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo()); +?> +<form action="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit" method="post" id="save" enctype="multipart/form-data"> + <input type="hidden" name="do" value="update"> + <input type="hidden" name="a" value="edit"> + <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> + <h2>Update Ticket# <?php echo $ticket->getExtId(); ?></h2> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan="2"> + <h4>Ticket Update</h4> + <em><strong>User Information</strong>: Make sure the email address is valid.</em> + </th> + </tr> + </thead> + <tbody> + <tr> + <td width="160" class="required"> + Full Name: + </td> + <td> + <input type="text" size="45" name="name" value="<?php echo $info['name']; ?>"> + <span class="error">* <?php echo $errors['name']; ?></span> + </td> + </tr> + <tr> + <td width="160" class="required"> + Email Address: + </td> + <td> + <input type="text" size="45" name="email" value="<?php echo $info['email']; ?>"> + <span class="error">* <?php echo $errors['email']; ?></span> + </td> + </tr> + <tr> + <td width="160"> + Phone Number: + </td> + <td> + <input type="text" size="18" name="phone" value="<?php echo $info['phone']; ?>"> + <span class="error"> <?php echo $errors['phone']; ?></span> + Ext <input type="text" size="5" name="phone_ext" value="<?php echo $info['phone_ext']; ?>"> + <span class="error"> <?php echo $errors['phone_ext']; ?></span> + </td> + </tr> + <tr> + <th colspan="2"> + <em><strong>Ticket Information</strong>: Due date overwrites SLA's grace period.</em> + </th> + </tr> + <tr> + <td width="160" class="required"> + Ticket Source: + </td> + <td> + <select name="source"> + <option value="" selected >— Select Source —</option> + <option value="Phone" <?php echo ($info['source']=='Phone')?'selected="selected"':''; ?>>Phone</option> + <option value="Email" <?php echo ($info['source']=='Email')?'selected="selected"':''; ?>>Email</option> + <option value="Web" <?php echo ($info['source']=='Web')?'selected="selected"':''; ?>>Web</option> + <option value="API" <?php echo ($info['source']=='API')?'selected="selected"':''; ?>>API</option> + <option value="Other" <?php echo ($info['source']=='Other')?'selected="selected"':''; ?>>Other</option> + </select> + <font class="error"><b>*</b> <?=$errors['source']?></font> + </td> + </tr> + <tr> + <td width="160" class="required"> + Help Topic: + </td> + <td> + <select name="topicId"> + <option value="" selected >— Select Help Topic —</option> + <?php + if($topics=Topic::getHelpTopics()) { + foreach($topics as $id =>$name) { + echo sprintf('<option value="%d" %s>%s</option>', + $id, ($info['topicId']==$id)?'selected="selected"':'',$name); + } + } + ?> + </select> + <font class="error"><b>*</b> <?=$errors['topicId']?></font> + </td> + </tr> + <tr> + <td width="160" class="required"> + Priority Level: + </td> + <td> + <select name="priorityId"> + <option value="" selected >— Select Priority —</option> + <?php + if($priorities=Priority::getPriorities()) { + foreach($priorities as $id =>$name) { + echo sprintf('<option value="%d" %s>%s</option>', + $id, ($info['priorityId']==$id)?'selected="selected"':'',$name); + } + } + ?> + </select> + <font class="error">* <?=$errors['priorityId']?></font> + </td> + </tr> + <tr> + <td width="160" class="required"> + SLA: + </td> + <td> + <select name="slaId"> + <option value="" selected >— Select SLA —</option> + <?php + if($slas=SLA::getSLAs()) { + foreach($slas as $id =>$name) { + echo sprintf('<option value="%d" %s>%s</option>', + $id, ($info['slaId']==$id)?'selected="selected"':'',$name); + } + } + ?> + </select> + <font class="error">* <?=$errors['slaId']?></font> + </td> + </tr> + <tr> + <td width="160" class="required"> + Subject: + </td> + <td> + <input type="text" name="subject" size="60" value="<?=$info['subject']?>"> + <font class="error">* <?=$errors['subject']?></font> + </td> + </tr> + <tr> + <td width="160"> + Due Date: + </td> + <td> + <input id="duedate" name="duedate" value="<?php echo Format::htmlchars($info['duedate']); ?>" size="10" + onclick="event.cancelBubble=true;calendar(this);" autocomplete=OFF> + <a href="#" onclick="event.cancelBubble=true;calendar(getObj('duedate')); return false;"><img src='images/cal.png'border=0 alt=""></a> + + <?php + $min=$hr=null; + if($info['time']) + list($hr,$min)=explode(':',$info['time']); + echo Misc::timeDropdown($hr,$min,'time'); + ?> + <font class="error"> <?=$errors['duedate']?> <?php echo $errors['time']; ?></font> + <em>Time is based on your time zone (GM <?php echo $thisstaff->getTZoffset(); ?>)</em> + </td> + </tr> + <tr> + <th colspan="2"> + <em><strong>Internal Note</strong>: Reason for editing the ticket (required) <font class="error"> <?php echo $errors['note'];?></font></em> + </th> + </tr> + <tr> + <td colspan=2> + <textarea name="note" cols="21" rows="6" style="width:80%;"><?php echo $info['note']; ?></textarea> + </td> + </tr> + </tbody> +</table> +<p style="padding-left:250px;"> + <input type="submit" name="submit" value="Save"> + <input type="reset" name="reset" value="Reset"> + <input type="button" name="cancel" value="Cancel" onclick='window.location.href="tickets.php?id=<?php echo $ticket->getId(); ?>"'> +</p> +</form> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index d15eb6170a9a6b02cabcdf9e4cdba0093639b534..0b187713399f613dcf2d856cbc5da89086b7b47e 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -116,7 +116,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Subject: </td> <td> - <input type="text" name="subject" size="35" value="<?=$info['subject']?>"> + <input type="text" name="subject" size="55" value="<?=$info['subject']?>"> <font class="error">* <?=$errors['subject']?></font> </td> </tr> @@ -305,6 +305,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <p style="padding-left:250px;"> <input type="submit" name="submit" value="Open"> <input type="reset" name="reset" value="Reset"> - <input type="button" name="cancel" value="Cancel" onclick='window.location.href="departments.php"'> + <input type="button" name="cancel" value="Cancel" onclick='window.location.href="tickets.php"'> </p> </form> diff --git a/scp/tickets.php b/scp/tickets.php index 3d271e123d2e1e8ebd2e2353b6635a6c830d5f21..9fb1e9cf7f0612e8f600af2e911ae58e02f2c9f9 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -157,15 +157,15 @@ if($_POST && !$errors): $errors['note']='Error(s) occurred. Unable to post the note.'; } break; + case 'edit': case 'update': - $page='editticket.inc.php'; if(!$ticket || !$thisstaff->canEditTickets()) $errors['err']='Perm. Denied. You are not allowed to edit tickets'; - elseif($ticket->update($_POST,$errors)){ + elseif($ticket->update($_POST,$errors)) { $msg='Ticket updated successfully'; - $page='ticket.inc.php'; - }elseif(!$errors['err']) { - $errors['err']='Error(s) occured! Try again.'; + $_REQUEST['a'] = null; + } elseif(!$errors['err']) { + $errors['err']='Unable to update the ticket. Correct the errors below and try again!'; } break; case 'process': @@ -454,8 +454,8 @@ $inc = 'tickets.inc.php'; if($ticket) { $nav->setActiveSubMenu(-1); $inc = 'ticket-view.inc.php'; - if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) - $errors['err'] ='Work in progress... '; + if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) + $inc = 'ticket-edit.inc.php'; }else { $inc = 'tickets.inc.php'; if($_REQUEST['a']=='open' && $thisstaff->canCreateTickets())