Skip to content
Snippets Groups Projects
class.ticket.php 119 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')
            ),
            'status' => array(
                'constraint' => array('status_id' => 'TicketStatus.id')
            ),
            'lock' => array(
                'constraint' => array('lock_id' => 'Lock.lock_id'),
                '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,
            ),
                'reverse' => 'Thread.ticket',
                'list' => false,
                'null' => true,
            ),
            'cdata' => array(
                'reverse' => 'TicketCData.ticket',
                'list' => false,
            ),
        )
    );

    const PERM_CREATE   = 'ticket.create';
    const PERM_EDIT     = 'ticket.edit';
    const PERM_ASSIGN   = 'ticket.assign';
    const PERM_TRANSFER = 'ticket.transfer';
    const PERM_REPLY    = 'ticket.reply';
    const PERM_CLOSE    = 'ticket.close';
    const PERM_DELETE   = 'ticket.delete';


    static protected $perms = array(
            self::PERM_CREATE => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Create',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to open tickets on behalf of users'),
            self::PERM_EDIT => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Edit',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to edit tickets'),
            self::PERM_ASSIGN => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Assign',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to assign tickets to agents or teams'),
            self::PERM_TRANSFER => array(
Peter Rotich's avatar
Peter Rotich committed

                'title' =>
                /* @trans */ 'Transfer',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to transfer tickets between departments'),
            self::PERM_REPLY => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Post Reply',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to post a ticket reply'),
            self::PERM_CLOSE => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Close',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to close tickets'),
            self::PERM_DELETE => array(
Peter Rotich's avatar
Peter Rotich committed
                'title' =>
                /* @trans */ 'Delete',
Peter Rotich's avatar
Peter Rotich committed
                'desc'  =>
                /* @trans */ 'Ability to delete tickets'),
            );

    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]);
        }
    }

    static function getPermissions() {
        return self::$perms;
    }
RolePermission::register(/* @trans */ 'Tickets', TicketModel::getPermissions(), true);
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';

class Ticket
implements RestrictedAccess, Threadable, TemplateVariable {
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;

        $sql='SELECT  ticket.*, thread.id as thread_id, ticket.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.flags & 1 = 1) '
            .' LEFT JOIN '.LOCK_TABLE.' tlock
                ON ( ticket.lock_id=tlock.lock_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->field->name)
                        ?: 'field.' . $answer->field->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();
    function checkStaffPerm($staff, $perm=null) {
        // Must be a valid staff
        if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
Jared Hancock's avatar
Jared Hancock committed
            return false;

        // Check access based on department or assignment
        if (!(!$staff->showAssignedOnly()
                    && $staff->canAccessDept($this->getDeptId()))
                // only open tickets can be considered assigned
                && $this->isOpen()
                && $staff->getId() != $this->getStaffId()
                && !$staff->isTeamMember($this->getTeamId()))
        // At this point staff has view access unless a specific permission is
        // requested
        if ($perm === null)
        // Permission check requested -- get role.
        if (!($role=$staff->getRole($this->getDeptId())))
            return false;

        // Check permission based on the effective role
        return $role->hasPerm($perm);
    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(
                        'user_id' => $user->getId(),
                        'thread_id' => $this->getThreadId())))
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();
    // Deprecated
    function getOldAuthToken() {
        # 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'];
    }

    /**
     * setStatusId
     *
     * Forceably set the ticket status ID to the received status ID. No
     * checks are made. Use ::setStatus() to change the ticket status
     */
    // XXX: Use ::setStatus to change the status. This can be used as a
    //      fallback if the logic in ::setStatus fails.
    function setStatusId($id) {
        $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '.
Peter Rotich's avatar
Peter Rotich committed
               ' ,status_id='.db_input($id) .
               ' WHERE ticket_id='.db_input($this->getId());

Peter Rotich's avatar
Peter Rotich committed
        return (db_query($sql) && db_affected_rows());

        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->getFullName();
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;
    }

        if (!isset($this->tlock) && $this->ht['lock_id'])
            $this->tlock = Lock::lookup($this->ht['lock_id']);

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 = Lock::acquire($staffId, $lockTime); //Create a new lock..

        if ($this->tlock) {
            $sql = 'UPDATE '.TICKET_TABLE.' SET `lock_id` = '
                .db_input($this->tlock->getId())
                .' WHERE `ticket_id` = '. db_input($this->getId());
            db_query($sql);
        }

Jared Hancock's avatar
Jared Hancock committed
        //load and return the newly created lock if any!
        return $this->tlock;
    function releaseLock($staffId=false) {
        if (!($lock = $this->getLock()))
            return false;

        if ($staffId && $lock->staff_id != $staffId)
            return false;

        if (!$lock->delete())
            return false;

        $sql = 'UPDATE '.TICKET_TABLE.' SET `lock_id` = 0 WHERE `ticket_id` = '
            . db_input($this->getId());
        return ($res = db_query($sql)) && db_affected_rows($res);
    }

        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->getClientThread()->count();
Jared Hancock's avatar
Jared Hancock committed
    }

    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(array('M'));
    function getResponses() {
        return $this->getThreadEntries(array('R'));
    function getNotes() {
        return $this->getThreadEntries(array('N'));
    function getClientThread() {
        return $this->getThreadEntries(array('M', 'R'));
    function getThreadEntry($id) {
        return $this->getThread()->getEntry($id);
    function getThreadEntries($type=false) {
Peter Rotich's avatar
Peter Rotich committed
        $entries = $this->getThread()->getEntries();
Peter Rotich's avatar
Peter Rotich committed
            $entries->filter(array('type__in' => $type));

        return $entries;
    //UserList of recipients  (owner + collaborators)
    function getRecipients() {

        if (!isset($this->recipients)) {
            $list = new UserList();
            $list->add($this->getOwner());
            if ($collabs = $this->getThread()->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;
                'threadId' => $this->getThreadId(),
                'userId' => $user->getId()), $vars);
        if (!($c=Collaborator::add($vars, $errors)))
            return null;

        $this->collaborators = null;
        $this->recipients = null;
        $this->logEvent('collab', array('add' => array($c->toString())));

        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()
                     $collabs[] = (string) $c;
            $this->logEvent('collab', array('del' => $collabs));
        }

        //statuses
        $cids = null;
        if($vars['cid'] && ($cids=array_filter($vars['cid']))) {
            $this->getThread()->collaborators->filter(array(
                'thread_id' => $this->getThreadId(),
                'id__in' => $cids
            ))->update(array(
                'updated' => SqlFunction::NOW(),
                'isactive' => 1,
            ));
        if ($cids) {
            $this->getThread()->collaborators->filter(array(
                'thread_id' => $this->getThreadId(),
                Q::not(array('id__in' => $cids))
            ))->update(array(
                'updated' => SqlFunction::NOW(),
                'isactive' => 0,
            ));
        }
        unset($this->ht['active_collaborators']);
        $this->collaborators = null;

        return true;
    }

    function getAuthToken($user, $algo=1) {

        //Format: // <user type><algo id used>x<pack of uid & tid><hash of the algo>