Skip to content
Snippets Groups Projects
class.ticket.php 107 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-2013 osTicket
Jared Hancock's avatar
Jared Hancock committed
    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.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');
include_once(INCLUDE_DIR.'class.canned.php');
Jared Hancock's avatar
Jared Hancock committed
require_once(INCLUDE_DIR.'class.dynamic_forms.php');
require_once(INCLUDE_DIR.'class.user.php');
require_once(INCLUDE_DIR.'class.collaborator.php');
Peter Rotich's avatar
Peter Rotich committed
require_once(INCLUDE_DIR.'class.task.php');
require_once(INCLUDE_DIR.'class.faq.php');
class TicketModel extends VerySimpleModel {
    static $meta = array(
        'table' => TICKET_TABLE,
        'pk' => array('ticket_id'),
        'joins' => array(
            'user' => array(
                'constraint' => array('user_id' => 'User.id')
            ),
            'collaborators' => array(
                'reverse' => 'TicketCollaborator.ticket',
                'null' => true,
            ),
            'status' => array(
                'constraint' => array('status_id' => 'TicketStatus.id')
            ),
            'lock' => array(
                'reverse' => 'TicketLock.ticket',
                'list' => false,
                'null' => true,
            ),
            'dept' => array(
                'constraint' => array('dept_id' => 'Dept.id'),
            'sla' => array(
                'constraint' => array('sla_id' => 'SlaModel.id'),
                'null' => true,
            ),
Jared Hancock's avatar
Jared Hancock committed
                'constraint' => array('staff_id' => 'Staff.staff_id'),
                'null' => true,
            ),
            'team' => array(
                'constraint' => array('team_id' => 'Team.team_id'),
                'null' => true,
            ),
            'topic' => array(
                'constraint' => array('topic_id' => 'Topic.topic_id'),
                'null' => true,
            ),
            'cdata' => array(
                'reverse' => 'TicketCData.ticket',
                'list' => false,
            ),
        )
    );

    function getId() {
        return $this->ticket_id;
    }

    function getEffectiveDate() {
         return Format::datetime(max(
             strtotime($this->lastmessage),
             strtotime($this->closed),
             strtotime($this->reopened),
             strtotime($this->created)
    }

    function delete() {

        if (($ticket=Ticket::lookup($this->getId())) && @$ticket->delete())
            return true;

        return false;
    }

    static function registerCustomData(DynamicForm $form) {
        if (!isset(static::$meta['joins']['cdata+'.$form->id])) {
            $cdata_class = <<<EOF
class DynamicForm{$form->id} extends DynamicForm {
    static function getInstance() {
        static \$instance;
        if (!isset(\$instance))
            \$instance = static::lookup({$form->id});
        return \$instance;
    }
}
class TicketCdataForm{$form->id} {
    static \$meta = array(
        'view' => true,
        'pk' => array('ticket_id'),
        'joins' => array(
            'ticket' => array(
                'constraint' => array('ticket_id' => 'TicketModel.ticket_id'),
            ),
        )
    );
    static function getQuery(\$compiler) {
        return '('.DynamicForm{$form->id}::getCrossTabQuery('T', 'ticket_id').')';
    }
}
EOF;
            eval($cdata_class);
            static::$meta['joins']['cdata+'.$form->id] = array(
                'reverse' => 'TicketCdataForm'.$form->id.'.ticket',
                'null' => true,
            );
            // This may be necessary if the model has already been inspected
            if (static::$meta instanceof ModelMeta)
                static::$meta->processJoin(static::$meta['joins']['cdata+'.$form->id]);
        }
    }
}

class TicketCData extends VerySimpleModel {
    static $meta = array(
        'pk' => array('ticket_id'),
        'joins' => array(
            'ticket' => array(
                'constraint' => array('ticket_id' => 'TicketModel.ticket_id'),
            ),
            ':priority' => array(
                'constraint' => array('priority' => 'Priority.priority_id'),
                'null' => true,
            ),
        ),
    );
}
TicketCData::$meta['table'] = TABLE_PREFIX . 'ticket__cdata';

Jared Hancock's avatar
Jared Hancock committed

    var $id;
Jared Hancock's avatar
Jared Hancock committed

    var $lastMsgId;
    var $status;
Jared Hancock's avatar
Jared Hancock committed
    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
Jared Hancock's avatar
Jared Hancock committed
        $this->id = 0;
        $this->load($id);
    }
Jared Hancock's avatar
Jared Hancock committed
    function load($id=0) {

Peter Rotich's avatar
Peter Rotich committed
        if (!$id && !($id=$this->getId()))
Jared Hancock's avatar
Jared Hancock committed
            return false;

Peter Rotich's avatar
Peter Rotich committed
        $sql='SELECT  ticket.*, thread.id as thread_id, lock_id, dept.name as dept_name '
Peter Rotich's avatar
Peter Rotich committed
            .' ,count(distinct attach.id) as attachments'
Peter Rotich's avatar
Peter Rotich committed
            .' ,count(distinct task.id) as tasks'
Jared Hancock's avatar
Jared Hancock committed
            .' FROM '.TICKET_TABLE.' ticket '
            .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.id) '
            .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) '
Peter Rotich's avatar
Peter Rotich committed
            .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock
                ON ( ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) '
Peter Rotich's avatar
Peter Rotich committed
            .' LEFT JOIN '.TASK_TABLE.' task
                ON ( task.object_id = ticket.ticket_id AND task.object_type="T" ) '
Peter Rotich's avatar
Peter Rotich committed
            .' LEFT JOIN '.THREAD_TABLE.' thread
                ON ( thread.object_id = ticket.ticket_id AND thread.object_type="T" ) '
            .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry
                ON ( entry.thread_id = thread.id ) '
            .' LEFT JOIN '.ATTACHMENT_TABLE.' attach
                ON ( attach.object_id = entry.id AND attach.`type` = "H") '
Jared Hancock's avatar
Jared Hancock committed
            .' WHERE ticket.ticket_id='.db_input($id)
            .' GROUP BY ticket.ticket_id';

        //echo $sql;
Peter Rotich's avatar
Peter Rotich committed
        if (!($res=db_query($sql)) || !db_num_rows($res))
Jared Hancock's avatar
Jared Hancock committed
            return false;

Jared Hancock's avatar
Jared Hancock committed
        $this->id       = $this->ht['ticket_id'];
        $this->number   = $this->ht['number'];
        $this->_answers = array();
Jared Hancock's avatar
Jared Hancock committed
        $this->loadDynamicData();

Jared Hancock's avatar
Jared Hancock committed
        //Reset the sub classes (initiated ondemand)...good for reloads.
        $this->status= null;
Jared Hancock's avatar
Jared Hancock committed
        $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;
        $this->collaborators = null;
Jared Hancock's avatar
Jared Hancock committed
        return true;
    }
Jared Hancock's avatar
Jared Hancock committed
    function loadDynamicData() {
        if (!$this->_answers) {
            foreach (DynamicFormEntry::forTicket($this->getId(), true) as $form) {
                foreach ($form->getAnswers() as $answer) {
                    $tag = mb_strtolower($answer->getField()->get('name'))
                        ?: 'field.' . $answer->getField()->get('id');
                        $this->_answers[$tag] = $answer;
        return $this->_answers;
Jared Hancock's avatar
Jared Hancock committed
    function reload() {
        return $this->load();
    }
    function hasState($state) {
        return  (strcasecmp($this->getState(), $state)==0);
    }

Jared Hancock's avatar
Jared Hancock committed
    function isOpen() {
        return $this->hasState('open');
Jared Hancock's avatar
Jared Hancock committed
    }

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

    function isReopenable() {
        return $this->getStatus()->isReopenable();
    }

Jared Hancock's avatar
Jared Hancock committed
    function isClosed() {
         return $this->hasState('closed');
    }

    function isArchived() {
         return $this->hasState('archived');
    }

    function isDeleted() {
         return $this->hasState('deleted');
Jared Hancock's avatar
Jared Hancock committed
    }

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

    function isOverdue() {
Jared Hancock's avatar
Jared Hancock committed
    function isAnswered() {
       return ($this->ht['isanswered']);
    }

    function isLocked() {
        return null !== $this->getLock();
Jared Hancock's avatar
Jared Hancock committed
    }

    function checkStaffAccess($staff) {

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

        // Staff has access to the department.
        if (!$staff->showAssignedOnly()
                && $staff->canAccessDept($this->getDeptId()))
            return true;

        // Only consider assignment if the ticket is open
        if (!$this->isOpen())
            return false;

        // Check ticket access based on direct or team assignment
        if ($staff->getId() == $this->getStaffId()
                || ($this->getTeamId()
                    && $staff->isTeamMember($this->getTeamId())
        ))
            return true;

        // No access bro!
        return false;
    function checkUserAccess($user) {
        if (!$user || !($user instanceof EndUser))
        //Ticket Owner
        if ($user->getId() == $this->getUserId())
        //Collaborator?
        // 1) If the user was authorized via this ticket.
        if ($user->getTicketId() == $this->getId()
                && !strcasecmp($user->getUserType(), 'collaborator'))
            return true;

        // 2) Query the database to check for expanded access...
        if (Collaborator::lookup(array(
                        'userId' => $user->getId(),
                        'ticketId' => $this->getId())))
Jared Hancock's avatar
Jared Hancock committed
    //Getters
Jared Hancock's avatar
Jared Hancock committed
        return  $this->id;
    }

    function getOwnerId() {
        return $this->ht['user_id'];
    }

    function getOwner() {

        if (!isset($this->owner)
                && ($u=User::lookup($this->getOwnerId())))
            $this->owner = new TicketOwner(new EndUser($u), $this);

        return $this->owner;
Jared Hancock's avatar
Jared Hancock committed
    function getEmail(){
        if ($o = $this->getOwner())
            return $o->getEmail();
        return null;
    }

    function getReplyToEmail() {
        //TODO: Determine the email to use (once we enable multi-email support)
        return $this->getEmail();
    function getAuthToken() {
        # XXX: Support variable email address (for CCs)
        return md5($this->getId() . strtolower($this->getEmail()) . SECRET_SALT);
Jared Hancock's avatar
Jared Hancock committed
    function getName(){
        if ($o = $this->getOwner())
            return $o->getName();
Jared Hancock's avatar
Jared Hancock committed
    }

    function getSubject() {
        return (string) $this->_answers['subject'];
Jared Hancock's avatar
Jared Hancock committed
    }

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

        if(!$this->ht['helptopic'] && ($topic=$this->getTopic()))
            $this->ht['helptopic'] = $topic->getFullName();

    function getCreateDate() {
        return $this->ht['created'];
Jared Hancock's avatar
Jared Hancock committed
    }

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

    function getReopenDate() {

    function getUpdateDate() {
        return $this->ht['updated'];
    function getEffectiveDate() {
        return $this->ht['lastupdate'];
    }

    function getDueDate() {
        return $this->ht['duedate'];
    function getSLADueDate() {
        if ($sla = $this->getSLA()) {
            $dt = new DateTime($this->getCreateDate());

            return $dt
                ->add(new DateInterval('PT' . $sla->getGracePeriod() . 'H'))
                ->format('Y-m-d H:i:s');
        }
    }

    function updateEstDueDate() {
        $estimatedDueDate = $this->getEstDueDate();
        if ($estimatedDueDate != $this->ht['est_duedate']) {
            $sql = 'UPDATE '.TICKET_TABLE.' SET `est_duedate`='.db_input($estimatedDueDate)
                .' WHERE `ticket_id`='.db_input($this->getId());
            db_query($sql);
        }
    }

    function getEstDueDate() {

        if(($duedate=$this->getDueDate()))
            return $duedate;

        //return sla due date (If ANY)
        return $this->getSLADueDate();
    }

    function getCloseDate() {
        return $this->ht['closed'];
    function getStatusId() {
        return $this->ht['status_id'];
    }


        if (!$this->status && $this->getStatusId())
            $this->status = TicketStatus::lookup($this->getStatusId());

        return $this->status;
    }

    function getState() {

        if (!$this->getStatus())
            return '';

        return $this->getStatus()->getState();

    function getDeptId() {
       return $this->ht['dept_id'];

    function getDeptName() {

        if(!$this->ht['dept_name'] && ($dept = $this->getDept()))
            $this->ht['dept_name'] = $dept->getName();

       return $this->ht['dept_name'];
Jared Hancock's avatar
Jared Hancock committed
    }

    function getPriorityId() {
        if (($a = $this->_answers['priority'])
                && ($b = $a->getValue()))
            return $b->getId();
        return $cfg->getDefaultPriorityId();
    function getPriority() {
        if (($a = $this->_answers['priority']) && ($b = $a->getValue()))
            return $b->getDesc();
        return '';
Jared Hancock's avatar
Jared Hancock committed
    function getPhoneNumber() {
        return (string)$this->getOwner()->getPhoneNumber();
Jared Hancock's avatar
Jared Hancock committed
    }

    function getSource() {
        return $this->ht['source'];
    }
Jared Hancock's avatar
Jared Hancock committed
    function getIP() {
        return $this->ht['ip_address'];
    }

Peter Rotich's avatar
Peter Rotich committed
    function getHashtable() {
        return $this->ht;
    }

    function getUpdateInfo() {
        global $cfg;
Peter Rotich's avatar
Peter Rotich committed

        $info=array('source'    =>  $this->getSource(),
Peter Rotich's avatar
Peter Rotich committed
                    'topicId'   =>  $this->getTopicId(),
                    'slaId' =>  $this->getSLAId(),
                    'user_id' => $this->getOwnerId(),
                    'duedate'   =>  $this->getDueDate()
                        ? Format::date($this->getDueDate())
                    'time'  =>  $this->getDueDate()?(Format::date($this->getDueDate(), true, 'HH:mm')):'',
Peter Rotich's avatar
Peter Rotich committed
                    );
Peter Rotich's avatar
Peter Rotich committed
        return $info;
    }

Jared Hancock's avatar
Jared Hancock committed
        return $this->tlock;
    }
Jared Hancock's avatar
Jared Hancock committed
    function acquireLock($staffId, $lockTime) {
Jared Hancock's avatar
Jared Hancock committed
        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.
Jared Hancock's avatar
Jared Hancock committed
            return $lock;
        }
        //No lock on the ticket or it is expired
        $this->tlock = TicketLock::acquire($this->getId(), $staffId, $lockTime); //Create a new lock..
Jared Hancock's avatar
Jared Hancock committed
        //load and return the newly created lock if any!
        return $this->tlock;
        if(!$this->dept)
            if(!($this->dept = Dept::lookup($this->getDeptId())))
                $this->dept = $cfg->getDefaultDept();
Jared Hancock's avatar
Jared Hancock committed

        return $this->dept;
    }
    function getUserId() {
        return $this->getOwnerId();
    }
    function getUser() {
        if(!isset($this->user) && $this->getOwner())
            $this->user = new EndUser($this->getOwner());
        return $this->user;

    function getStaffId() {
        return $this->ht['staff_id'];
Jared Hancock's avatar
Jared Hancock committed

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

        return $this->staff;
    }

    function getTeamId() {
        return $this->ht['team_id'];
Jared Hancock's avatar
Jared Hancock committed

        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

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

        return $this->topic;
    }

Jared Hancock's avatar
Jared Hancock committed
    function getSLAId() {
Jared Hancock's avatar
Jared Hancock committed
    }

    function getSLA() {

        if(!$this->sla && $this->getSLAId())
Peter Rotich's avatar
Peter Rotich committed
            $this->sla = SLA::lookup($this->getSLAId());
Jared Hancock's avatar
Jared Hancock committed

        return $this->sla;
    }

    function getLastRespondent() {

Peter Rotich's avatar
Peter Rotich committed
        if (!isset($this->lastrespondent)) {
Peter Rotich's avatar
Peter Rotich committed
            $sql ='SELECT resp.staff_id '
                 .' FROM '.THREAD_ENTRY_TABLE.' resp '
                 .' LEFT JOIN '.THREAD_TABLE.' t ON( t.id=resp.thread_id) '
                 .' LEFT JOIN '.STAFF_TABLE. ' s ON(s.staff_id=resp.staff_id) '
                 .' WHERE  t.object_id='.db_input($this->getId())
                 .'     AND t.object_type="T" AND resp.staff_id>0 AND  resp.`type`="R" '
                 .' ORDER BY resp.created DESC LIMIT 1';

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

            list($id)=db_fetch_row($res);
Peter Rotich's avatar
Peter Rotich committed
            $this->lastrespondent = Staff::lookup($id);
        }
Peter Rotich's avatar
Peter Rotich committed
        return $this->lastrespondent;
Jared Hancock's avatar
Jared Hancock committed

    }

    function getLastMessageDate() {
Jared Hancock's avatar
Jared Hancock committed
    }

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

    function getLastResponseDate() {
Jared Hancock's avatar
Jared Hancock committed
    }

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

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

    function getLastMessage() {
        if (!isset($this->last_message)) {
            if ($this->getLastMsgId())
                $this->last_message = MessageThreadEntry::lookup(
                    $this->getLastMsgId(), $this->getThreadId());
            if (!$this->last_message)
                $this->last_message = $this->getThread()->getLastMessage();
        return $this->last_message;
Peter Rotich's avatar
Peter Rotich committed
    function getNumTasks() {
        return $this->ht['tasks'];
    }

Peter Rotich's avatar
Peter Rotich committed
    function getThreadId() {
        return $this->ht['thread_id'];
    }

Peter Rotich's avatar
Peter Rotich committed
        if (!$this->thread && $this->getThreadId())
            $this->thread = TicketThread::lookup($this->getThreadId());
Jared Hancock's avatar
Jared Hancock committed
    }

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

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

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

    function getNumNotes() {
        return $this->getThread()->getNumNotes();
    function getMessages() {
        return $this->getThreadEntries('M');
    function getResponses() {
        return $this->getThreadEntries('R');
    function getNotes() {
        return $this->getThreadEntries('N');
    function getClientThread() {
        return $this->getThreadEntries(array('M', 'R'));
    function getThreadEntry($id) {
        return $this->getThread()->getEntry($id);
    function getThreadEntries($type, $order='') {
        return $this->getThread()->getEntries(
                array( 'type' => $type, 'order' => $order));
    //Collaborators
    function getNumCollaborators() {
Peter Rotich's avatar
Peter Rotich committed
        return count($this->getCollaborators());
    function getNumActiveCollaborators() {

        if (!isset($this->ht['active_collaborators']))
            $this->ht['active_collaborators'] = count($this->getActiveCollaborators());

        return $this->ht['active_collaborators'];
    }

    function getActiveCollaborators() {
        return $this->getCollaborators(array('isactive'=>1));
    }


    function getCollaborators($criteria=array()) {

Peter Rotich's avatar
Peter Rotich committed
        if ($criteria)
            return Collaborator::forTicket($this->getId(), $criteria);

Peter Rotich's avatar
Peter Rotich committed
        if (!isset($this->collaborators))
            $this->collaborators = Collaborator::forTicket($this->getId());

        return $this->collaborators;
    }

    //UserList of recipients  (owner + collaborators)
    function getRecipients() {

        if (!isset($this->recipients)) {
            $list = new UserList();
            $list->add($this->getOwner());
            if ($collabs = $this->getActiveCollaborators()) {
                foreach ($collabs as $c)
                    $list->add($c);
            }
            $this->recipients = $list;
        }

        return $this->recipients;
    }

    function hasClientEditableFields() {
        $forms = DynamicFormEntry::forTicket($this->getId());
        foreach ($forms as $form) {
            foreach ($form->getFields() as $field) {
                if ($field->isEditableToUsers())
                    return true;
            }
        }
    }
    function getMissingRequiredFields() {
        $returnArray = array();
        $forms=DynamicFormEntry::forTicket($this->getId());
        foreach ($forms as $form) {
            foreach ($form->getFields() as $field) {
                if ($field->isRequiredForClose()) {
                    if (!($field->answer->get('value'))) {
                        array_push($returnArray, $field->get('label'));
                    }
                }
            }
        }
        return $returnArray;
    }

    function getMissingRequiredField() {
        $fields = $this->getMissingRequiredFields();
        return $fields[0];
    }

    function addCollaborator($user, $vars, &$errors) {
        if (!$user || $user->getId()==$this->getOwnerId())
Peter Rotich's avatar
Peter Rotich committed
            return null;
                'ticketId' => $this->getId(),
                'userId' => $user->getId()), $vars);
        if (!($c=Collaborator::add($vars, $errors)))
            return null;

        $this->collaborators = null;
        $this->recipients = null;
        return $c;
    }

    //XXX: Ugly for now
    function updateCollaborators($vars, &$errors) {

        //Deletes
        if($vars['del'] && ($ids=array_filter($vars['del']))) {
            $collabs = array();
            foreach ($ids as $k => $cid) {
                if (($c=Collaborator::lookup($cid))
                        && $c->getTicketId() == $this->getId()
                        && $c->remove())
Peter Rotich's avatar
Peter Rotich committed
                     $collabs[] = $c;
            $this->logNote(_S('Collaborators Removed'),
Peter Rotich's avatar
Peter Rotich committed
                    implode("<br>", $collabs), $thisstaff, false);
        }

        //statuses
        $cids = null;
        if($vars['cid'] && ($cids=array_filter($vars['cid']))) {
            $sql='UPDATE '.TICKET_COLLABORATOR_TABLE
                .' SET updated=NOW(), isactive=1 '
                .' WHERE ticket_id='.db_input($this->getId())
                .' AND id IN('.implode(',', db_input($cids)).')';
            db_query($sql);
        }

        $sql='UPDATE '.TICKET_COLLABORATOR_TABLE
            .' SET updated=NOW(), isactive=0 '
            .' WHERE ticket_id='.db_input($this->getId());
        if($cids)
            $sql.=' AND id NOT IN('.implode(',', db_input($cids)).')';

        db_query($sql);

        unset($this->ht['active_collaborators']);
        $this->collaborators = null;

        return true;
    }

Jared Hancock's avatar
Jared Hancock committed
    /* -------------------- Setters --------------------- */
    function setLastMsgId($msgid) {
        return $this->lastMsgId=$msgid;
    }
    function setLastMessage($message) {
        $this->last_message = $message;
        $this->setLastMsgId($message->getId());
    }
Jared Hancock's avatar
Jared Hancock committed

    //DeptId can NOT be 0. No orphans please!
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;

Jared Hancock's avatar
Jared Hancock committed
        $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());
    }
Jared Hancock's avatar
Jared Hancock committed
    //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 = null;
        $this->ht['staff_id'] = $staffId;

Peter Rotich's avatar
Peter Rotich committed
        return true;
Jared Hancock's avatar
Jared Hancock committed
    }

    function setSLAId($slaId) {
        if ($slaId == $this->getSLAId()) return true;
        $rv = db_query(
Jared Hancock's avatar
Jared Hancock committed
             'UPDATE '.TICKET_TABLE.' SET sla_id='.db_input($slaId)
            .' WHERE ticket_id='.db_input($this->getId()))
            && db_affected_rows();
        if ($rv) {
            $this->ht['sla_id'] = $slaId;
            $this->sla = null;
        }
        return $rv;
Jared Hancock's avatar
Jared Hancock committed
    }
    /**
     * Selects the appropriate service-level-agreement plan for this ticket.
     * When tickets are transfered between departments, the SLA of the new
Jared Hancock's avatar
Jared Hancock committed
     * department should be applied to the ticket. This would be useful,
Jared Hancock's avatar
Jared Hancock committed
     * 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 overridden if it was originally set via an
Jared Hancock's avatar
Jared Hancock committed
        #     email filter? This method doesn't consider such a case
Jared Hancock's avatar
Jared Hancock committed
            $slaId = $trump;
        } elseif ($this->getDept() && $this->getDept()->getSLAId()) {
Jared Hancock's avatar
Jared Hancock committed
            $slaId = $this->getDept()->getSLAId();
        } elseif ($this->getTopic() && $this->getTopic()->getSLAId()) {
Jared Hancock's avatar
Jared Hancock committed
            $slaId = $this->getTopic()->getSLAId();
        } else {
            $slaId = $cfg->getDefaultSLAId();
        }
Jared Hancock's avatar
Jared Hancock committed
        return ($slaId && $this->setSLAId($slaId)) ? $slaId : false;
    }