Skip to content
Snippets Groups Projects
class.ticket.php 78.7 KiB
Newer Older
Jared Hancock's avatar
Jared Hancock committed
<?php
/*********************************************************************
    class.ticket.php

    The most important class! Don't play with fire please.

    Peter Rotich <peter@osticket.com>
    Copyright (c)  2006-2012 osTicket
    http://www.osticket.com

    Released under the GNU General Public License WITHOUT ANY WARRANTY.
    See LICENSE.TXT for details.

    vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
include_once(INCLUDE_DIR.'class.thread.php');
Jared Hancock's avatar
Jared Hancock committed
include_once(INCLUDE_DIR.'class.staff.php');
include_once(INCLUDE_DIR.'class.client.php');
Jared Hancock's avatar
Jared Hancock committed
include_once(INCLUDE_DIR.'class.team.php');
include_once(INCLUDE_DIR.'class.email.php');
include_once(INCLUDE_DIR.'class.dept.php');
include_once(INCLUDE_DIR.'class.topic.php');
include_once(INCLUDE_DIR.'class.lock.php');
include_once(INCLUDE_DIR.'class.file.php');
include_once(INCLUDE_DIR.'class.attachment.php');
include_once(INCLUDE_DIR.'class.pdf.php');
Jared Hancock's avatar
Jared Hancock committed
include_once(INCLUDE_DIR.'class.banlist.php');
include_once(INCLUDE_DIR.'class.template.php');
include_once(INCLUDE_DIR.'class.variable.php');
Jared Hancock's avatar
Jared Hancock committed
include_once(INCLUDE_DIR.'class.priority.php');
Peter Rotich's avatar
Peter Rotich committed
include_once(INCLUDE_DIR.'class.sla.php');
Jared Hancock's avatar
Jared Hancock committed

    var $id;
    var $extid;
    var $email;
    var $status;
    var $created;
    var $reopened;
    var $updated;
    var $lastrespdate;
    var $lastmsgdate;
    var $duedate;
    var $priority;
    var $priority_id;
    var $fullname;
    var $staff_id;
    var $team_id;
    var $dept_id;
    var $topic_id;
    var $dept_name;
    var $subject;
    var $helptopic;
    var $overdue;

    var $lastMsgId;
    
    var $dept;  //Dept obj
    var $sla;   // SLA obj
    var $staff; //Staff obj
    var $client; //Client Obj
Jared Hancock's avatar
Jared Hancock committed
    var $team;  //Team obj
    var $topic; //Topic obj
    var $tlock; //TicketLock obj
    
    function Ticket($id){
        $this->id = 0;
        $this->load($id);
    }
    
    function load($id=0) {

        if(!$id && !($id=$this->getId()))
            return false;

        //TODO: delete helptopic field in ticket table.
       
        $sql='SELECT  ticket.*, lock_id, dept_name, priority_desc '
Jared Hancock's avatar
Jared Hancock committed
            .' ,count(attach.attach_id) as attachments '
            .' ,count(DISTINCT message.id) as messages '
            .' ,count(DISTINCT response.id) as responses '
            .' ,count(DISTINCT note.id) as notes '
Jared Hancock's avatar
Jared Hancock committed
            .' FROM '.TICKET_TABLE.' ticket '
            .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) '
            .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON ('
                .'ticket.priority_id=pri.priority_id) '
            .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON ('
                .'ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) '
            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach ON ('
                .'ticket.ticket_id=attach.ticket_id) '
            .' LEFT JOIN '.TICKET_THREAD_TABLE.' message ON ('
                ."ticket.ticket_id=message.ticket_id AND message.thread_type = 'M') "
            .' LEFT JOIN '.TICKET_THREAD_TABLE.' response ON ('
                ."ticket.ticket_id=response.ticket_id AND response.thread_type = 'R') "
            .' LEFT JOIN '.TICKET_THREAD_TABLE.' note ON ( '
                ."ticket.ticket_id=note.ticket_id AND note.thread_type = 'N') "
Jared Hancock's avatar
Jared Hancock committed
            .' WHERE ticket.ticket_id='.db_input($id)
            .' GROUP BY ticket.ticket_id';

        //echo $sql;
        if(!($res=db_query($sql)) || !db_num_rows($res))
            return false;

        
        $this->ht=db_fetch_array($res);
        
        $this->id       = $this->ht['ticket_id'];
        $this->extid    = $this->ht['ticketID'];
         
        $this->email    = $this->ht['email'];
        $this->fullname = $this->ht['name'];
        $this->status   = $this->ht['status'];
        $this->created  = $this->ht['created'];
        $this->reopened = $this->ht['reopened'];
        $this->updated  = $this->ht['updated'];
        $this->duedate  = $this->ht['duedate'];
        $this->closed   = $this->ht['closed'];
        $this->lastmsgdate  = $this->ht['lastmessagedate'];
        $this->lastrespdate = $this->ht['lastresponsedate'];
        
        $this->lock_id  = $this->ht['lock_id'];
        $this->priority_id = $this->ht['priority_id'];
        $this->priority = $this->ht['priority_desc'];
        $this->staff_id = $this->ht['staff_id'];
        $this->team_id = $this->ht['team_id']; 
        $this->dept_id  = $this->ht['dept_id'];
        $this->dept_name = $this->ht['dept_name'];
        $this->sla_id = $this->ht['sla_id'];
        $this->topic_id = $this->ht['topic_id'];
        $this->subject = $this->ht['subject'];
        $this->overdue = $this->ht['isoverdue'];
        
        //Reset the sub classes (initiated ondemand)...good for reloads.
        $this->staff = null;
        $this->client = null;
Jared Hancock's avatar
Jared Hancock committed
        $this->team  = null;
        $this->dept = null;
        $this->sla = null;
        $this->tlock = null;
        $this->stats = null;
        $this->topic = null;
        
        return true;
    }
        
    function reload() {
        return $this->load();
    }
    
    function isOpen() {
        return (strcasecmp($this->getStatus(),'Open')==0);
    }

    function isReopened() {
        return ($this->getReopenDate());
    }

    function isClosed() {
        return (strcasecmp($this->getStatus(),'Closed')==0);
    }

    function isAssigned() {
        return ($this->isOpen() && ($this->getStaffId() || $this->getTeamId()));
    }

    function isOverdue() {
        return ($this->overdue);
    }
    
    function isAnswered() {
       return ($this->ht['isanswered']);
    }

    function isLocked() {
        return ($this->getLockId());
    }

    function checkStaffAccess($staff) {

        if(!is_object($staff) && !($staff=Staff::lookup($staff)))
            return false;

        return ((!$staff->showAssignedOnly() && $staff->canAccessDept($this->getDeptId()))
Jared Hancock's avatar
Jared Hancock committed
                 || ($this->getTeamId() && $staff->isTeamMember($this->getTeamId()))
                 || $staff->getId()==$this->getStaffId());
    }

    function checkClientAccess($client) {
        global $cfg;

        if(!is_object($client) && !($client=Client::lookup($client)))
            return false;

        if(!strcasecmp($client->getEmail(),$this->getEmail()))
            return true;

Jared Hancock's avatar
Jared Hancock committed
        return ($cfg && $cfg->showRelatedTickets() 
            && $client->getTicketId()==$this->getExtId());
Jared Hancock's avatar
Jared Hancock committed
    //Getters
Jared Hancock's avatar
Jared Hancock committed
        return  $this->id;
    }

Jared Hancock's avatar
Jared Hancock committed
        return  $this->extid;
    }

    function getNumber() {
        return $this->getExtId();
    }
Jared Hancock's avatar
Jared Hancock committed
   
    function getEmail(){
        return $this->email;
    }

    function getAuthToken() {
        # XXX: Support variable email address (for CCs)
        return md5($this->getId() . $this->getEmail() . SECRET_SALT);
    }

Jared Hancock's avatar
Jared Hancock committed
    function getName(){
        return $this->fullname;
    }

    function getSubject() {
        return $this->subject;
    }

    /* Help topic title  - NOT object -> $topic */
    function getHelpTopic() {

        if(!$this->helpTopic && ($topic=$this->getTopic()))
            $this->helpTopic = $topic->getName();
            
        return $this->helpTopic;
Jared Hancock's avatar
Jared Hancock committed
    }
   
    function getCreateDate(){
        return $this->created;
    }

    function getOpenDate() {
        return $this->getCreateDate();
    }

    function getReopenDate() {
        return $this->reopened;
    }
    
    function getUpdateDate(){
        return $this->updated;
    }

    function getDueDate(){
        return $this->duedate;
    }

    function getCloseDate(){
        return $this->closed;
    }

    function getStatus(){
        return $this->status;
    }
   
    function getDeptId(){
       return $this->dept_id;
    }
   
    function getDeptName(){
       return $this->dept_name;
    }

    function getPriorityId() {
        return $this->priority_id;
    }
    
    function getPriority() {
        return $this->priority;
    }
     
    function getPhone() {
        return $this->ht['phone'];
    }

    function getPhoneExt() {
        return $this->ht['phone_ext'];
    }

    function getPhoneNumber() {
        $phone=Format::phone($this->getPhone());
        if(($ext=$this->getPhoneExt()))
            $phone.=" $ext";

        return $phone;
    }

    function getSource() {
        return $this->ht['source'];
    }
    
    function getIP() {
        return $this->ht['ip_address'];
    }

Peter Rotich's avatar
Peter Rotich committed
    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;
    }

Jared Hancock's avatar
Jared Hancock committed
    function getLockId() {
        return $this->lock_id;
    }
    
    function getLock(){
        
        if(!$this->tlock && $this->getLockId())
            $this->tlock= TicketLock::lookup($this->getLockId(),$this->getId());
        
        return $this->tlock;
    }
    
    function acquireLock($staffId, $lockTime) {
       
        if(!$staffId or !$lockTime) //Lockig disabled?
            return null;

        //Check if the ticket is already locked.
        if(($lock=$this->getLock()) && !$lock->isExpired()) {
            if($lock->getStaffId()!=$staffId) //someone else locked the ticket.
                return null;

            //Lock already exits...renew it
            $lock->renew($lockTime); //New clock baby.
            
            return $lock;
        }
        //No lock on the ticket or it is expired
        $this->tlock=null; //clear crap
        $this->lock_id=TicketLock::acquire($this->getId(), $staffId, $lockTime); //Create a new lock..
        //load and return the newly created lock if any!
        return $this->getLock();
    }
    
    function getDept(){
        
        if(!$this->dept && $this->getDeptId())
            $this->dept= Dept::lookup($this->getDeptId());

        return $this->dept;
    }

    function getClient() {

        if(!$this->client)
            $this->client = Client::lookup($this->getExtId(), $this->getEmail());

        return $this->client;
    }
Jared Hancock's avatar
Jared Hancock committed
    
    function getStaffId(){
        return $this->staff_id;
    }

    function getStaff(){

        if(!$this->staff && $this->getStaffId())
            $this->staff= Staff::lookup($this->getStaffId());

        return $this->staff;
    }

    function getTeamId(){
        return $this->team_id;
    }

    function getTeam(){

        if(!$this->team && $this->getTeamId())
            $this->team = Team::lookup($this->getTeamId());

        return $this->team;
    }

    function getAssignee() {

        if($staff=$this->getStaff())
            return $staff->getName();

        if($team=$this->getTeam())
            return $team->getName();

        return '';
    }

Peter Rotich's avatar
Peter Rotich committed
    function getAssignees() {
     
        $assignees=array();
Peter Rotich's avatar
Peter Rotich committed
        if($staff=$this->getStaff())
            $assignees[] = $staff->getName();
Peter Rotich's avatar
Peter Rotich committed
                       
        if($team=$this->getTeam())
            $assignees[] = $team->getName();
Peter Rotich's avatar
Peter Rotich committed

        return $assignees;
    }
    function getAssigned($glue='/') {
        $assignees = $this->getAssignees();
        return $assignees?implode($glue, $assignees):'';
    }

    function getTopicId() {
Jared Hancock's avatar
Jared Hancock committed
        return $this->topic_id;
    }

    function getTopic() { 

        if(!$this->topic && $this->getTopicId())
            $this->topic = Topic::lookup($this->getTopicId());
Jared Hancock's avatar
Jared Hancock committed

        return $this->topic;
    }

 
    function getSLAId() {
        return $this->sla_id;
    }

    function getSLA() {

        if(!$this->sla && $this->getSLAId())
            $this->sla = SLA::lookup($this->getSLAId);

        return $this->sla;
    }

    function getLastRespondent() {

        $sql ='SELECT  resp.staff_id '
             .' FROM '.TICKET_THREAD_TABLE.' resp '
Jared Hancock's avatar
Jared Hancock committed
             .' LEFT JOIN '.STAFF_TABLE. ' USING(staff_id) '
             .' WHERE  resp.ticket_id='.db_input($this->getId()).' AND resp.staff_id>0 '
             .'   AND  resp.thread_type="R"'
Jared Hancock's avatar
Jared Hancock committed
             .' ORDER BY resp.created DESC LIMIT 1';

        if(!($res=db_query($sql)) || !db_num_rows($res))
            return null;
            
        list($id)=db_fetch_row($res);

        return Staff::lookup($id);

    }

    function getLastMessageDate() {

        if($this->lastmsgdate)
            return $this->lastmsgdate;

        //for old versions...XXX: still needed????
        $sql='SELECT created FROM '.TICKET_THREAD_TABLE
Jared Hancock's avatar
Jared Hancock committed
            .' WHERE ticket_id='.db_input($this->getId())
            ."   AND thread_type = 'M'"
Jared Hancock's avatar
Jared Hancock committed
            .' ORDER BY created DESC LIMIT 1';
        if(($res=db_query($sql)) && db_num_rows($res))
            list($this->lastmsgdate)=db_fetch_row($res);

        return $this->lastmsgdate;
    }

    function getLastMsgDate() {
        return $this->getLastMessageDate();
    }

    function getLastResponseDate() {
               
        if($this->lastrespdate)
            return $this->lastrespdate;

        $sql='SELECT created FROM '.TICKET_THREAD_TABLE
Jared Hancock's avatar
Jared Hancock committed
            .' WHERE ticket_id='.db_input($this->getId())
            .'   AND thread_type="R"'
Jared Hancock's avatar
Jared Hancock committed
            .' ORDER BY created DESC LIMIT 1';
        if(($res=db_query($sql)) && db_num_rows($res))
            list($this->lastrespdate)=db_fetch_row($res);

        return $this->lastrespdate;
    }

    function getLastRespDate() {
        return $this->getLastResponseDate();
    }

        
    function getLastMsgId() {
        return $this->lastMsgId;
    }

    function getRelatedTicketsCount(){

        $sql='SELECT count(*)  FROM '.TICKET_TABLE
            .' WHERE email='.db_input($this->getEmail());

        return db_result(db_query($sql));
Jared Hancock's avatar
Jared Hancock committed
    }

    function getThreadCount() {
        return $this->getNumMessages() + $this->getNumResponses();
    }

    function getNumMessages() {
        return $this->ht['messages'];
    }

    function getNumResponses() {
        return $this->ht['responses'];
    }

    function getNumNotes() {
        return $this->ht['notes'];
    }

    function getMessages() {
        return $this->getThreadByType('M');
    }
    function getResponses($msgId=0) {
        return $this->getThreadByType('R', $msgId);
    function getNotes() {
        return $this->getThreadByType('N');
    function getClientThread() {
        return $this->getThreadWithoutNotes();
    function getThreadWithNotes() {
        return $this->getThread(true);
    }
    
    function getThreadWithoutNotes() {
        return $this->getThread(false);
    }

    function getThread($includeNotes=false, $order='') {

        $treadtypes=array('M', 'R'); // messages and responses.
        if($includeNotes) //Include notes??
            $treadtypes[] = 'N';

        return $this->getThreadByType($treadtypes, $order);
        
    function getThreadByType($type, $order='ASC') {
        if(!$order || !in_array($order, array('DESC','ASC')))
            $order='ASC';
        $sql='SELECT thread.* '
            .' ,count(DISTINCT attach.attach_id) as attachments '
            .' FROM '.TICKET_THREAD_TABLE.' thread '
Jared Hancock's avatar
Jared Hancock committed
            .' LEFT JOIN '.TICKET_ATTACHMENT_TABLE.' attach 
                ON (thread.ticket_id=attach.ticket_id 
                        AND thread.id=attach.ref_id 
                        AND thread.thread_type=attach.ref_type) '
            .' WHERE  thread.ticket_id='.db_input($this->getId());

        if($type && is_array($type))
            $sql.=" AND thread.thread_type IN('".implode("','", $type)."')";
            $sql.=' AND thread.thread_type='.db_input($type);
        $sql.=' GROUP BY thread.id '
             .' ORDER BY thread.created '.$order;

        $thread=array();
Jared Hancock's avatar
Jared Hancock committed
        if(($res=db_query($sql)) && db_num_rows($res))
            while($rec=db_fetch_array($res))
                $thread[] = $rec;

        return $thread;
Jared Hancock's avatar
Jared Hancock committed
    }

    function getAttachments($refId=0, $type=null) {

        if($refId && !$type)
            return NULL;

        //XXX: inner join the file table instead?
        $sql='SELECT a.attach_id, f.id as file_id, f.size, f.hash as file_hash, f.name '
            .' FROM '.FILE_TABLE.' f '
            .' INNER JOIN '.TICKET_ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
            .' WHERE a.ticket_id='.db_input($this->getId());
       
        if($refId) 
            $sql.=' AND a.ref_id='.db_input($refId);

        if($type)
            $sql.=' AND a.ref_type='.db_input($type);

        $attachments = array();
        if(($res=db_query($sql)) && db_num_rows($res)) {
            while($rec=db_fetch_array($res))
                $attachments[] = $rec;
        }

        return $attachments;
    }

    function getAttachmentsLinks($refId, $type, $separator=' ',$target='') {

        $str='';
        foreach($this->getAttachments($refId, $type) as $attachment ) {
            /* The has here can be changed  but must match validation in attachment.php */
            $hash=md5($attachment['file_id'].session_id().$attachment['file_hash']); 
            if($attachment['size'])
                $size=sprintf('<em>(%s)</em>', Format::file_size($attachment['size']));
Jared Hancock's avatar
Jared Hancock committed
                
            $str.=sprintf('<a class="Icon file" href="attachment.php?id=%d&h=%s" target="%s">%s</a>%s&nbsp;%s',
                    $attachment['attach_id'], $hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
        }

        return $str;
    }

    /* -------------------- Setters --------------------- */
    function setLastMsgId($msgid) {
        return $this->lastMsgId=$msgid;
    }

    function setPriority($priorityId) {

        //XXX: what happens to SLA priority???
        
        if(!$priorityId || $priorityId==$this->getPriorityId()) 
            return ($priorityId);
        
        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW() '
            .', priority_id='.db_input($priorityId)
            .' WHERE ticket_id='.db_input($this->getId());

Jared Hancock's avatar
Jared Hancock committed
        return (($res=db_query($sql)) && db_affected_rows($res));
Jared Hancock's avatar
Jared Hancock committed
    }

    //DeptId can NOT be 0. No orphans please!
    function setDeptId($deptId){
Jared Hancock's avatar
Jared Hancock committed
        //Make sure it's a valid department//
        if(!($dept=Dept::lookup($deptId)) || $dept->getId()==$this->getDeptId())
Jared Hancock's avatar
Jared Hancock committed
            return false;

      
        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), dept_id='.db_input($deptId)
            .' WHERE ticket_id='.db_input($this->getId());

        return (db_query($sql) && db_affected_rows());
    }
 
    //Set staff ID...assign/unassign/release (id can be 0)
Peter Rotich's avatar
Peter Rotich committed
    function setStaffId($staffId) {

        if(!is_numeric($staffId)) return false;
        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), staff_id='.db_input($staffId)
            .' WHERE ticket_id='.db_input($this->getId());
Peter Rotich's avatar
Peter Rotich committed
        if (!db_query($sql)  || !db_affected_rows())
            return false;
        
        $this->staff_id = $staffId;
        return true;
Jared Hancock's avatar
Jared Hancock committed
    }

    function setSLAId($slaId) {
        if ($slaId == $this->getSLAId()) return true;
        return db_query(
             'UPDATE '.TICKET_TABLE.' SET sla_id='.db_input($slaId)
            .' WHERE ticket_id='.db_input($this->getId()))
            && db_affected_rows();
    }
    /**
     * Selects the appropriate service-level-agreement plan for this ticket.
     * When tickets are transfered between departments, the SLA of the new
     * department should be applied to the ticket. This would be usefule,
     * for instance, if the ticket is transferred to a different department
     * which has a shorter grace period, the ticket should be considered
     * overdue in the shorter window now that it is owned by the new
     * department.
     *
     * $trump - if received, should trump any other possible SLA source.
     *          This is used in the case of email filters, where the SLA
     *          specified in the filter should trump any other SLA to be
     *          considered.
     */
    function selectSLAId($trump=null) {
        global $cfg;
        # XXX Should the SLA be overwritten if it was originally set via an
        #     email filter? This method doesn't consider such a case
        if ($trump !== null) {
            $slaId = $trump;
        } elseif ($this->getDept()->getSLAId()) {
            $slaId = $this->getDept()->getSLAId();
        } elseif ($this->getTopicId() && $this->getTopic()) {
            $slaId = $this->getTopic()->getSLAId();
        } else {
            $slaId = $cfg->getDefaultSLAId();
        }
        return ($slaId && $this->setSLAId($slaId)) ? $slaId : false;
    }

    //Set team ID...assign/unassign/release (id can be 0)
Peter Rotich's avatar
Peter Rotich committed
    function setTeamId($teamId) {
        
        if(!is_numeric($teamId)) return false;
      
        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), team_id='.db_input($teamId)
            .' WHERE ticket_id='.db_input($this->getId());
Peter Rotich's avatar
Peter Rotich committed
        return (db_query($sql)  && db_affected_rows());
Jared Hancock's avatar
Jared Hancock committed
    }

    //Status helper.
    function setStatus($status) {

        if(strcasecmp($this->getStatus(),$status)==0)
            return true; //No changes needed.

        switch(strtolower($status)) {
            case 'open':
                return $this->reopen();
                break;
            case 'closed':
                return $this->close();
                break;
        }

        return false;
    }

    function setState($state, $alerts=false) {

        switch(strtolower($state)) {
            case 'open':
                return $this->setStatus('open');
                break;
            case 'closed':
                return $this->setStatus('closed');
                break;
            case 'answered':
                return $this->setAnsweredState(1);
                break;
            case 'unanswered':
                return $this->setAnsweredState(0);
                break;
            case 'overdue':
                return $this->markOverdue();
                break;
        }

        return false;
    }




    function setAnsweredState($isanswered) {

        $sql='UPDATE '.TICKET_TABLE.' SET isanswered='.db_input($isanswered)
            .' WHERE ticket_id='.db_input($this->getId());

        return (db_query($sql) && db_affected_rows());
    }

    //Close the ticket
    function close(){
        global $thisstaff;
        
        $sql='UPDATE '.TICKET_TABLE.' SET closed=NOW(), isoverdue=0, duedate=NULL, updated=NOW(), status='.db_input('closed');
        
        if($thisstaff) //Give the closing  staff credit. 
            $sql.=', staff_id='.db_input($thisstaff->getId());

        $sql.=' WHERE ticket_id='.db_input($this->getId());

        $this->logEvent('closed');
Jared Hancock's avatar
Jared Hancock committed
        return (db_query($sql) && db_affected_rows());
    }

    //set status to open on a closed ticket.
    function reopen($isanswered=0){

        $sql='UPDATE '.TICKET_TABLE.' SET updated=NOW(), reopened=NOW() '
            .' ,status='.db_input('open')
            .' ,isanswered='.db_input($isanswered)
            .' WHERE ticket_id='.db_input($this->getId());

        //TODO: log reopen event here 

        $this->logEvent('reopened', 'closed');
Jared Hancock's avatar
Jared Hancock committed
        return (db_query($sql) && db_affected_rows());
    }

    function onNewTicket($message, $autorespond=true, $alertstaff=true) {
        global $cfg;

        //Log stuff here...
        
        if(!$autorespond && !$alertstaff) return true; //No alerts to send.

        /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/
        
        $this->reload(); //get the new goodies.
        $dept= $this->getDept();

        if(!$dept || !($tpl = $dept->getTemplate()))
            $tpl= $cfg->getDefaultTemplate();
        
        if(!$tpl) return false;  //bail out...missing stuff.

        if(!$dept || !($email=$dept->getAutoRespEmail()))
            $email =$cfg->getDefaultEmail();

        //Send auto response - if enabled.
        if($autorespond && $email && $cfg->autoRespONNewTicket() 
                && $dept->autoRespONNewTicket() 
                &&  ($msg=$tpl->getAutoRespMsgTemplate())) {
            
            $msg = $this->replaceVars($msg, 
                    array('message' => $message,
                          'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')
                    );

Jared Hancock's avatar
Jared Hancock committed
            if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
                $msg['body'] ="\n$tag\n\n".$msg['body'];
Jared Hancock's avatar
Jared Hancock committed
            
            //TODO: add auto flags....be nice to mail servers and sysadmins!!
            $email->send($this->getEmail(), $msg['subj'], $msg['body']);
Jared Hancock's avatar
Jared Hancock committed
        }
        
        if(!($email=$cfg->getAlertEmail()))
            $email =$cfg->getDefaultEmail();
          
        //Send alert to out sleepy & idle staff.
        if($alertstaff && $email
                && $cfg->alertONNewTicket() 
                && ($msg=$tpl->getNewTicketAlertMsgTemplate())) {
                    
            $msg = $this->replaceVars($msg, array('message' => $message));

Jared Hancock's avatar
Jared Hancock committed
            $recipients=$sentlist=array();
            //Alert admin??
            if($cfg->alertAdminONNewTicket()) {
                $alert = str_replace('%{recipient}', 'Admin', $msg['body']);
                $email->send($cfg->getAdminEmail(), $msg['subj'], $alert);
Jared Hancock's avatar
Jared Hancock committed
                $sentlist[]=$cfg->getAdminEmail();
            }
              
            //Only alerts dept members if the ticket is NOT assigned.
            if($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
Peter Rotich's avatar
Peter Rotich committed
                if(($members=$dept->getMembers()))
Jared Hancock's avatar
Jared Hancock committed
                    $recipients=array_merge($recipients, $members);
            }
            
            if($cfg->alertDeptManagerONNewTicket() && $dept && ($manager=$dept->getManager()))
                $recipients[]= $manager;
               
            foreach( $recipients as $k=>$staff){
                if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(),$sentlist)) continue;
                $alert = str_replace('%{recipient}', $staff->getFirstName(), $msg['body']);
                $email->send($staff->getEmail(), $msg['subj'], $alert);
Peter Rotich's avatar
Peter Rotich committed
                $sentlist[] = $staff->getEmail();
    function onOpenLimit($sendNotice=true) {

        //Log the limit notice as a warning for admin.
        $msg=sprintf('Max open tickets (%d) reached  for %s ', $cfg->getMaxOpenTickets(), $this->getEmail());
        $ost->logWarning('Max. Open Tickets Limit ('.$this->getEmail().')', $msg);
        if(!$sendNotice || !$cfg->sendOverLimitNotice()) return true;

        //Send notice to user.
        $dept = $this->getDept();
                    
        if(!$dept || !($tpl=$dept->getTemplate()))
            $tpl=$cfg->getDefaultTemplate();
            
        if(!$dept || !($email=$dept->getAutoRespEmail()))
            $email=$cfg->getDefaultEmail();

        if($tpl && ($msg=$tpl->getOverlimitMsgTemplate()) && $email) {
            
            $msg = $this->replaceVars($msg, 
                        array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
            
            $email->send($this->getEmail(), $msg['subj'], $msg['body']);
        }

        $client= $this->getClient();
        
        //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think.
        $alert='Max. open tickets reached for '.$this->getEmail()."\n"
              .'Open ticket: '.$client->getNumOpenTickets()."\n"
              .'Max Allowed: '.$cfg->getMaxOpenTickets()."\n\nNotice sent to the user.";
        $ost->alertAdmin('Overlimit Notice', $alert);
Jared Hancock's avatar
Jared Hancock committed
    function onResponse(){
        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1,lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
    }

    function onMessage($autorespond=true, $alert=true){
        global $cfg;

        db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastmessage=NOW() WHERE ticket_id='.db_input($this->getId()));
            
        //auto-assign to closing staff or last respondent 
        if(!($staff=$this->getStaff()) || !$staff->isAvailable()) {
            if($cfg->autoAssignReopenedTickets() && ($lastrep=$this->getLastRespondent()) && $lastrep->isAvailable()) {
                $this->setStaffId($lastrep->getId()); //direct assignment;
            } else {
                $this->setStaffId(0); //unassign - last respondent is not available.
            }
        }

        if($this->isClosed()) $this->reopen(); //reopen..

       /**********   double check auto-response  ************/
        if($autorespond && (Email::getIdByEmail($this->getEmail())))
            $autorespond=false;
        elseif($autorespond && ($dept=$this->getDept()))
            $autorespond=$dept->autoRespONNewMessage();


        if(!$autorespond && !$cfg->autoRespONNewMessage()) return;  //no autoresp or alerts.

        $this->reload();


        if(!$dept || !($tpl = $dept->getTemplate()))
            $tpl = $cfg->getDefaultTemplate();

        if(!$dept || !($email = $dept->getAutoRespEmail()))
            $email = $cfg->getDefaultEmail();
      
Jared Hancock's avatar
Jared Hancock committed
        //If enabled...send confirmation to user. ( New Message AutoResponse)
        if($email && $tpl && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) {

            $msg = $this->replaceVars($msg,
                            array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
Jared Hancock's avatar
Jared Hancock committed

            //Reply separator tag.
            if($cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()))
                $msg['body'] ="\n$tag\n\n".$msg['body'];
        
            $email->send($this->getEmail(), $msg['subj'], $msg['body']);
    function onAssign($assignee, $comments, $alert=true) {
Jared Hancock's avatar
Jared Hancock committed
        global $cfg, $thisstaff;
Jared Hancock's avatar
Jared Hancock committed

        if($this->isClosed()) $this->reopen(); //Assigned tickets must be open - otherwise why assign?

        //Assignee must be an object of type Staff or Team
        if(!$assignee || !is_object($assignee)) return false;