Skip to content
Snippets Groups Projects
class.ticket.php 119 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 '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>
            $authtoken = sprintf('%s%dx%s',
                    ($user->getId() == $this->getOwnerId() ? 'o' : 'c'),
                    $algo,
                    Base32::encode(pack('VV',$user->getId(), $this->getId())));
    
            switch($algo) {
                case 1:
                    $authtoken .= substr(base64_encode(
                                md5($user->getId().$this->getCreateDate().$this->getId().SECRET_SALT, true)), 8);
                    break;
                default:
                    return null;
            }
    
            return $authtoken;
        }
    
    
        function sendAccessLink($user) {
            global $ost;
    
            if (!($email = $ost->getConfig()->getDefaultEmail())
                || !($content = Page::lookupByType('access-link')))
                return;
    
            $vars = array(
                'url' => $ost->getConfig()->getBaseUrl(),
                'ticket' => $this,
                'user' => $user,
                'recipient' => $user,
            );
    
            $lang = $user->getLanguage(UserAccount::LANG_MAILOUTS);
            $msg = $ost->replaceTemplateVariables(array(
                'subj' => $content->getLocalName($lang),
                'body' => $content->getLocalBody($lang),
            ), $vars);
    
            $email->send($user, Format::striptags($msg['subj']),
                $msg['body']);
        }
    
    
    
    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;
        }
    
        //Set team ID...assign/unassign/release (id can be 0)
    
    Peter Rotich's avatar
    Peter Rotich committed
        function setTeamId($teamId) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            if(!is_numeric($teamId)) return false;
    
    Peter Rotich's avatar
    Peter Rotich committed
            $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, $comments='', &$errors=array(), $set_closing_agent=true) {
    
            global $thisstaff;
    
            if ($thisstaff && !($role=$thisstaff->getRole($this->getDeptId())))
    
            if ($status && is_numeric($status))
                $status = TicketStatus::lookup($status);
    
            if (!$status || !$status instanceof TicketStatus)
                return false;
    
    
            // Double check permissions (when changing status)
            if ($role && $this->getStatusId()) {
                switch ($status->getState()) {
                case 'closed':
                    if (!($role->hasPerm(TicketModel::PERM_CLOSE)))
                        return false;
                    break;
                case 'deleted':
                    // XXX: intercept deleted status and do hard delete
                    if ($role->hasPerm(TicketModel::PERM_DELETE))
                        return $this->delete($comments);
                    // Agent doesn't have permission to delete  tickets
    
            if ($this->getStatusId() == $status->getId())
                return true;
    
            $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '.
                   ' ,status_id='.db_input($status->getId());
    
    
            //TODO: move this up.
    
            $ecb = null;
            switch($status->getState()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                case 'closed':
    
                    if ($this->getMissingRequiredFields()) {
                        $errors['err'] = sprintf(__(
                            'This ticket is missing data on %s one or more required fields %s and cannot be closed'),
                        '', '');
                        return false;
                    }
    
                    $sql.=', closed=NOW(), lastupdate=NOW(), duedate=NULL ';
    
                    if ($thisstaff && $set_closing_agent)
    
                        $sql.=', staff_id='.db_input($thisstaff->getId());
    
    
                    $ecb = function($t) {
                        $t->reload();
                        $t->logEvent('closed');
                        $t->deleteDrafts();
                    };
                    break;
                case 'open':
    
                    // TODO: check current status if it allows for reopening
    
                    if ($this->isClosed()) {
    
                        $sql .= ',closed=NULL, lastupdate=NOW(), reopened=NOW() ';
    
                        $ecb = function ($t) {
                            $t->logEvent('reopened', 'closed');
                        };
                    }
    
    
                    // If the ticket is not open then clear answered flag
                    if (!$this->isOpen())
                        $sql .= ', isanswered = 0 ';
    
    Jared Hancock's avatar
    Jared Hancock committed
                    break;
    
            $sql.=' WHERE ticket_id='.db_input($this->getId());
    
            if (!db_query($sql) || !db_affected_rows())
                return false;
    
    
            // Log status change b4 reload — if currently has a status. (On new
            // ticket, the ticket is opened and thereafter the status is set to
            // the requested status).
            if ($current_status = $this->getStatus()) {
    
                $note = sprintf(__('Status changed from %1$s to %2$s by %3$s'),
    
                        $this->getStatus(),
                        $status,
                        $thisstaff ?: 'SYSTEM');
    
                $alert = false;
                if ($comments) {
                    $note .= sprintf('<hr>%s', $comments);
                    // Send out alerts if comments are included
                    $alert = true;
                }
    
                $this->logNote(__('Status Changed'), $note, $thisstaff, $alert);
    
            // Log events via callback
            if ($ecb) $ecb($this);
    
            return true;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        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;
    
    Peter Rotich's avatar
    Peter Rotich committed
                case 'notdue':
                    return $this->clearOverdue();
                    break;
                case 'unassined':
                    return $this->unassign();
    
    Jared Hancock's avatar
    Jared Hancock committed
            }
    
            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());
        }
    
    
        function reopen() {
    
            global $cfg;
    
            if (!$this->isClosed())
                return false;
    
            // Set status to open based on current closed status settings
            // If the closed status doesn't have configured "reopen" status then use the
            // the default ticket status.
            if (!($status=$this->getStatus()->getReopenStatus()))
                $status = $cfg->getDefaultTicketStatusId();
    
            return $status ? $this->setStatus($status, 'Reopened') : false;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function onNewTicket($message, $autorespond=true, $alertstaff=true) {
            global $cfg;
    
            //Log stuff here...
    
    Jared Hancock's avatar
    Jared Hancock committed
            if(!$autorespond && !$alertstaff) return true; //No alerts to send.
    
            /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->reload(); //get the new goodies.
    
            if(!$cfg
                    || !($dept=$this->getDept())
                    || !($tpl = $dept->getTemplate())
                    || !($email=$dept->getAutoRespEmail())) {
                    return false;  //bail out...missing stuff.
            }
    
            $options = array();
            if ($message instanceof ThreadEntry) {
                $options += array(
                    'inreplyto'=>$message->getEmailMessageId(),
                    'references'=>$message->getEmailReferences(),
                    'thread'=>$message
                );
            }
    
            else {
                $options += array(
                    'thread' => $this->getThread(),
                );
            }
    
    Jared Hancock's avatar
    Jared Hancock committed
            //Send auto response - if enabled.
    
            if($autorespond
                    && $cfg->autoRespONNewTicket()
    
    Jared Hancock's avatar
    Jared Hancock committed
                    &&  ($msg=$tpl->getAutoRespMsgTemplate())) {
    
                $msg = $this->replaceVars($msg->asArray(),
    
                        array('message' => $message,
    
                              'recipient' => $this->getOwner(),
    
                              'signature' => ($dept && $dept->isPublic())?$dept->getSignature():'')
                        );
    
    
                $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body'],
    
    Jared Hancock's avatar
    Jared Hancock committed
            //Send alert to out sleepy & idle staff.
    
                    && ($email=$dept->getAlertEmail())
    
    Jared Hancock's avatar
    Jared Hancock committed
                    && ($msg=$tpl->getNewTicketAlertMsgTemplate())) {
    
                $msg = $this->replaceVars($msg->asArray(), array('message' => $message));
    
    Jared Hancock's avatar
    Jared Hancock committed
                $recipients=$sentlist=array();
    
                //Exclude the auto responding email just incase it's from staff member.
    
                if ($message instanceof ThreadEntry && $message->isAutoReply())
    
                    $sentlist[] = $this->getEmail();
    
    
    Jared Hancock's avatar
    Jared Hancock committed
                //Alert admin??
                if($cfg->alertAdminONNewTicket()) {
    
                    $alert = $this->replaceVars($msg, array('recipient' => 'Admin'));
                    $email->sendAlert($cfg->getAdminEmail(), $alert['subj'], $alert['body'], null, $options);
    
    Jared Hancock's avatar
    Jared Hancock committed
                    $sentlist[]=$cfg->getAdminEmail();
                }
    
    Jared Hancock's avatar
    Jared Hancock committed
                //Only alerts dept members if the ticket is NOT assigned.
                if($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
    
                    if(($members=$dept->getMembersForAlerts()))
    
    Jared Hancock's avatar
    Jared Hancock committed
                        $recipients=array_merge($recipients, $members);
                }
    
    Jared Hancock's avatar
    Jared Hancock committed
                if($cfg->alertDeptManagerONNewTicket() && $dept && ($manager=$dept->getManager()))
                    $recipients[]= $manager;
    
                // Account manager
                if ($cfg->alertAcctManagerONNewMessage()
                        && ($org = $this->getOwner()->getOrganization())
    
                        && ($acct_manager = $org->getAccountManager())) {
    
                    if ($acct_manager instanceof Team)
                        $recipients = array_merge($recipients, $acct_manager->getMembers());
                    else
                        $recipients[] = $acct_manager;
                }
    
    
                foreach( $recipients as $k=>$staff) {
                    if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
    
                    $alert = $this->replaceVars($msg, array('recipient' => $staff));
    
                    $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $sentlist[] = $staff->getEmail();
    
    Jared Hancock's avatar
    Jared Hancock committed
            return true;
        }
    
    
        function onOpenLimit($sendNotice=true) {
    
    
            //Log the limit notice as a warning for admin.
    
            $msg=sprintf(_S('Maximum open tickets (%1$d) reached for %2$s'),
                $cfg->getMaxOpenTickets(), $this->getEmail());
            $ost->logWarning(sprintf(_S('Maximum Open Tickets Limit (%s)'),$this->getEmail()),
                $msg);
    
            if(!$sendNotice || !$cfg->sendOverLimitNotice())
                return true;
    
    
            //Send notice to user.
    
            if(($dept = $this->getDept())
                && ($tpl=$dept->getTemplate())
                && ($msg=$tpl->getOverlimitMsgTemplate())
                && ($email=$dept->getAutoRespEmail())) {
    
                $msg = $this->replaceVars($msg->asArray(),
    
                            array('signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
    
                $email->sendAutoReply($this->getOwner(), $msg['subj'], $msg['body']);
    
            $user = $this->getOwner();
    
            //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think.
    
            $alert=sprintf(__('Maximum open tickets reached for %s.'), $this->getEmail())."\n"
                  .sprintf(__('Open tickets: %d'), $user->getNumOpenTickets())."\n"
                  .sprintf(__('Max allowed: %d'), $cfg->getMaxOpenTickets())
                  ."\n\n".__("Notice sent to the user.");
    
            $ost->alertAdmin(__('Overlimit Notice'), $alert);
    
    Peter Rotich's avatar
    Peter Rotich committed
        function onResponse($response, $options=array()) {
    
            db_query('UPDATE '.TICKET_TABLE.' SET isanswered=1, lastresponse=NOW(), updated=NOW() WHERE ticket_id='.db_input($this->getId()));
            $this->reload();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $vars = array_merge($options,
                    array(
                        'activity' => _S('New Response'),
                        'threadentry' => $response));
    
            $this->onActivity($vars);
    
        /*
         * Notify collaborators on response or new message
         *
         */
    
    
        function  notifyCollaborators($entry, $vars = array()) {
    
            if (!$entry instanceof ThreadEntry
    
                    || !($recipients=$this->getRecipients())
    
                    || !($dept=$this->getDept())
                    || !($tpl=$dept->getTemplate())
                    || !($msg=$tpl->getActivityNoticeMsgTemplate())
                    || !($email=$dept->getEmail()))
                return;
    
    
            //Who posted the entry?
    
            if ($entry instanceof Message) {
                $poster = $entry->getUser();
    
                // Skip the person who sent in the message
    
                $skip[$entry->getUserId()] = 1;
                // Skip all the other recipients of the message
    
                foreach ($entry->getAllEmailRecipients() as $R) {
    
                    foreach ($recipients as $R2) {
                        if ($R2->getEmail() == ($R->mailbox.'@'.$R->hostname)) {
                            $skip[$R2->getUserId()] = true;
                            break;
                        }
                    }
                }
    
                $poster = $entry->getStaff();
    
                // Skip the ticket owner
    
                $skip[$this->getUserId()] = 1;
    
            $vars = array_merge($vars, array(
                        'message' => (string) $entry,
    
                        'poster' => $poster ?: _S('A collaborator'),
    
            $msg = $this->replaceVars($msg->asArray(), $vars);
    
            $attachments = $cfg->emailAttachments()?$entry->getAttachments():array();
    
            $options = array('inreplyto' => $entry->getEmailMessageId(),
                             'thread' => $entry);
    
            foreach ($recipients as $recipient) {
    
                // Skip folks who have already been included on this part of
                // the conversation
                if (isset($skip[$recipient->getUserId()]))
                    continue;
    
                $notice = $this->replaceVars($msg, array('recipient' => $recipient));
    
                $email->send($recipient, $notice['subj'], $notice['body'], $attachments,
    
        function onMessage($message, $autorespond=true) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    
            db_query('UPDATE '.TICKET_TABLE.' SET isanswered=0,lastupdate=NOW(),lastmessage=NOW() WHERE ticket_id='.db_input($this->getId()));
    
            // Auto-assign to closing staff or last respondent
            // If the ticket is closed and auto-claim is not enabled then put the
            // ticket back to unassigned pool.
            if ($this->isClosed() && !$cfg->autoClaimTickets()) {
                $this->setStaffId(0);
            } elseif(!($staff=$this->getStaff()) || !$staff->isAvailable()) {
                // Ticket has no assigned staff -  if auto-claim is enabled then
                // try assigning it to the last respondent (if available)
                // otherwise leave the ticket unassigned.
                if ($cfg->autoClaimTickets() //Auto claim is enabled.
                        && ($lastrep=$this->getLastRespondent())
                        && $lastrep->isAvailable()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                    $this->setStaffId($lastrep->getId()); //direct assignment;
                } else {
                    $this->setStaffId(0); //unassign - last respondent is not available.
                }
            }
    
    
            // Reopen if closed AND reopenable
    
            // We're also checking autorespond flag because we don't want to
            // reopen closed tickets on auto-reply from end user. This is not to
            // confused with autorespond on new message setting
            if ($autorespond && $this->isClosed() && $this->isReopenable())
    
            // Figure out the user
            if ($this->getOwnerId() == $message->getUserId())
                $user = new TicketOwner(
                        User::lookup($message->getUserId()), $this);
            else
                $user = Collaborator::lookup(array(
    
                        'user_id' => $message->getUserId(),
                        'thread_id' => $this->getThreadId()));
    
    
            /**********   double check auto-response  ************/
            if (!$user)
    
                $autorespond=false;
            elseif ($autorespond && (Email::getIdByEmail($user->getEmail())))
    
    Jared Hancock's avatar
    Jared Hancock committed
                $autorespond=false;
    
            elseif ($autorespond && ($dept=$this->getDept()))
    
    Jared Hancock's avatar
    Jared Hancock committed
                $autorespond=$dept->autoRespONNewMessage();
    
    
    
            if(!$autorespond
                    || !$cfg->autoRespONNewMessage()
                    || !$message) return;  //no autoresp or alerts.
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->reload();
    
            $dept = $this->getDept();
            $email = $dept->getAutoRespEmail();
    
    Jared Hancock's avatar
    Jared Hancock committed
            //If enabled...send confirmation to user. ( New Message AutoResponse)
    
            if($email
                    && ($tpl=$dept->getTemplate())
                    && ($msg=$tpl->getNewMessageAutorepMsgTemplate())) {
    
                $msg = $this->replaceVars($msg->asArray(),
    
                                array(
                                    'recipient' => $user,
                                    'signature' => ($dept && $dept->isPublic())?$dept->getSignature():''));
    
                    'inreplyto'=>$message->getEmailMessageId(),
                    'thread'=>$message);
    
                $email->sendAutoReply($user, $msg['subj'], $msg['body'],
    
    Peter Rotich's avatar
    Peter Rotich committed
        function onActivity($vars, $alert=true) {
            global $cfg, $thisstaff;
    
            //TODO: do some shit
    
            if (!$alert // Check if alert is enabled
                    || !$cfg->alertONNewActivity()
                    || !($dept=$this->getDept())
                    || !($email=$cfg->getAlertEmail())
                    || !($tpl = $dept->getTemplate())
                    || !($msg=$tpl->getNoteAlertMsgTemplate()))
                return;
    
            // Alert recipients
            $recipients=array();
    
            //Last respondent.
            if ($cfg->alertLastRespondentONNewActivity())
                $recipients[] = $this->getLastRespondent();
    
            // Assigned staff / team
            if ($cfg->alertAssignedONNewActivity()) {
    
                if (isset($vars['assignee'])
                        && $vars['assignee'] instanceof Staff)
                     $recipients[] = $vars['assignee'];
                elseif ($this->isOpen() && ($assignee = $this->getStaff()))
                    $recipients[] = $assignee;
    
                if ($team = $this->getTeam())
                    $recipients = array_merge($recipients, $team->getMembers());
            }
    
            // Dept manager
            if ($cfg->alertDeptManagerONNewActivity() && $dept && $dept->getManagerId())
                $recipients[] = $dept->getManager();
    
            $options = array();
            $staffId = $thisstaff ? $thisstaff->getId() : 0;
            if ($vars['threadentry'] && $vars['threadentry'] instanceof ThreadEntry) {
                $options = array(
                    'inreplyto' => $vars['threadentry']->getEmailMessageId(),
                    'references' => $vars['threadentry']->getEmailReferences(),
                    'thread' => $vars['threadentry']);
    
                // Activity details
                if (!$vars['comments'])
                    $vars['comments'] = $vars['threadentry'];
    
                // Staff doing the activity
                $staffId = $vars['threadentry']->getStaffId() ?: $staffId;
            }
    
            $msg = $this->replaceVars($msg->asArray(),
                    array(
    
                        'note' => $vars['threadentry'], // For compatibility
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'activity' => $vars['activity'],
                        'comments' => $vars['comments']));
    
            $isClosed = $this->isClosed();
            $sentlist=array();
            foreach ($recipients as $k=>$staff) {
                if (!is_object($staff)
                        // Don't bother vacationing staff.
                        || !$staff->isAvailable()
                        // No need to alert the poster!
                        || $staffId == $staff->getId()
                        // No duplicates.
                        || isset($sentlist[$staff->getEmail()])
                        // Make sure staff has access to ticket
    
                        || ($isClosed && !$this->checkStaffPerm($staff))
    
    Peter Rotich's avatar
    Peter Rotich committed
                        )
                    continue;
                $alert = $this->replaceVars($msg, array('recipient' => $staff));
                $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                $sentlist[$staff->getEmail()] = 1;
            }
    
        }
    
    
        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;
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->reload();
    
    
            $comments = $comments ?: _S('Ticket assignment');
            $assigner = $thisstaff ?: _S('SYSTEM (Auto Assignment)');
    
            //Log an internal note - no alerts on the internal note.
    
            $note = $this->logNote(
                sprintf(_S('Ticket Assigned to %s'), $assignee->getName()),
    
                $comments, $assigner, false);
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            //See if we need to send alerts
            if(!$alert || !$cfg->alertONAssignment()) return true; //No alerts!
    
            $dept = $this->getDept();
    
            if(!$dept
                    || !($tpl = $dept->getTemplate())
    
                    || !($email = $dept->getAlertEmail()))
    
    
            //recipients
            $recipients=array();
    
            if ($assignee instanceof Staff) {
                if ($cfg->alertStaffONAssignment())
    
                    $recipients[] = $assignee;
    
            } elseif (($assignee instanceof Team) && $assignee->alertsEnabled()) {
                if ($cfg->alertTeamMembersONAssignment() && ($members=$assignee->getMembers()))
    
                    $recipients = array_merge($recipients, $members);
    
                elseif ($cfg->alertTeamLeadONAssignment() && ($lead=$assignee->getTeamLead()))
    
                    $recipients[] = $lead;
            }
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            //Get the message template
    
            if ($recipients
                    && ($msg=$tpl->getAssignedAlertMsgTemplate())) {
    
                $msg = $this->replaceVars($msg->asArray(),
    
                            array('comments' => $comments,
                                  'assignee' => $assignee,
                                  'assigner' => $assigner
    
    Jared Hancock's avatar
    Jared Hancock committed
                //Send the alerts.
                $sentlist=array();
    
                    'inreplyto'=>$note->getEmailMessageId(),
    
                    'references'=>$note->getEmailReferences(),
                    'thread'=>$note);
    
                foreach( $recipients as $k=>$staff) {
    
                    if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
    
                    $alert = $this->replaceVars($msg, array('recipient' => $staff));
    
                    $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $sentlist[] = $staff->getEmail();
    
    Jared Hancock's avatar
    Jared Hancock committed
       function onOverdue($whine=true, $comments="") {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    
            if($whine && ($sla=$this->getSLA()) && !$sla->alertOnOverdue())
                $whine = false;
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            //check if we need to send alerts.
    
            if(!$whine
                    || !$cfg->alertONOverdueTicket()
                    || !($dept = $this->getDept()))
    
    Jared Hancock's avatar
    Jared Hancock committed
                return true;
    
            //Get the message template
    
            if(($tpl = $dept->getTemplate())
                    && ($msg=$tpl->getOverdueAlertMsgTemplate())
    
                    && ($email = $dept->getAlertEmail())) {
    
                $msg = $this->replaceVars($msg->asArray(),
                    array('comments' => $comments));
    
    Jared Hancock's avatar
    Jared Hancock committed
    
                //recipients
                $recipients=array();
                //Assigned staff or team... if any
    
                if($this->isAssigned() && $cfg->alertAssignedONOverdueTicket()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                    if($this->getStaffId())
                        $recipients[]=$this->getStaff();
                    elseif($this->getTeamId() && ($team=$this->getTeam()) && ($members=$team->getMembers()))
                        $recipients=array_merge($recipients, $members);
                } elseif($cfg->alertDeptMembersONOverdueTicket() && !$this->isAssigned()) {
                    //Only alerts dept members if the ticket is NOT assigned.
    
                    if ($members = $dept->getMembersForAlerts())
                        $recipients = array_merge($recipients, $members);
    
    Jared Hancock's avatar
    Jared Hancock committed
                }
                //Always alert dept manager??
                if($cfg->alertDeptManagerONOverdueTicket() && $dept && ($manager=$dept->getManager()))
                    $recipients[]= $manager;
    
                $sentlist=array();
    
                foreach( $recipients as $k=>$staff) {
                    if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
    
                    $alert = $this->replaceVars($msg, array('recipient' => $staff));
    
                    $email->sendAlert($staff, $alert['subj'], $alert['body'], null);
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $sentlist[] = $staff->getEmail();
    
        //ticket obj as variable = ticket number.
        function asVar() {
           return $this->getNumber();
    
        function getVar($tag) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    
            if($tag && is_callable(array($this, 'get'.ucfirst($tag))))
    
                return call_user_func(array($this, 'get'.ucfirst($tag)));
    
            switch(mb_strtolower($tag)) {
    
                case 'phone_number':
                    return $this->getPhoneNumber();
                    break;
                case 'auth_token':
    
                    return $this->getOldAuthToken();
    
                    break;
                case 'client_link':
    
                    return sprintf('%s/view.php?t=%s',
                            $cfg->getBaseUrl(), $this->getNumber());
    
                    break;
                case 'staff_link':
                    return sprintf('%s/scp/tickets.php?id=%d', $cfg->getBaseUrl(), $this->getId());
                    break;
                case 'create_date':
    
                    return Format::datetime($this->getCreateDate(), true, 'UTC');
    
                    break;
                 case 'due_date':
                    $duedate ='';
    
                    if($this->getEstDueDate())
    
                        $duedate = Format::datetime($this->getEstDueDate(), true, 'UTC');
    
    
                    return $duedate;
                    break;
    
    M. Hagen's avatar
    M. Hagen committed
                case 'close_date':
    
                    $closedate ='';
                    if($this->isClosed())
    
                        $closedate = Format::datetime($this->getCloseDate(), true, 'UTC');
    
    
                    return $closedate;
                    break;
    
                case 'user':
                    return $this->getOwner();
                    break;
    
    Jared Hancock's avatar
    Jared Hancock committed
                default:
                    if (isset($this->_answers[$tag]))
    
                        // The answer object is retrieved here which will
                        // automatically invoke the toString() method when the
                        // answer is coerced into text
    
                        return $this->_answers[$tag];
    
    
            return false;
        }
    
        //Replace base variables.
        function replaceVars($input, $vars = array()) {
            global $ost;
    
            $vars = array_merge($vars, array('ticket' => $this));
    
            return $ost->replaceTemplateVariables($input, $vars);
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function markUnAnswered() {
            return (!$this->isAnswered() || $this->setAnsweredState(0));
        }
    
        function markAnswered() {
            return ($this->isAnswered() || $this->setAnsweredState(1));
        }
    
        function markOverdue($whine=true) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    Jared Hancock's avatar
    Jared Hancock committed
            if($this->isOverdue())
                return true;
    
    
            $sql='UPDATE '.TICKET_TABLE.' SET isoverdue=1, updated=NOW() '
    
    Jared Hancock's avatar
    Jared Hancock committed
                .' WHERE ticket_id='.db_input($this->getId());
    
            if(!db_query($sql) || !db_affected_rows())
                return false;
    
    
            $this->logEvent('overdue');
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->onOverdue($whine);
    
    Jared Hancock's avatar
    Jared Hancock committed
            return true;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function clearOverdue() {
    
    
    Peter Rotich's avatar
    Peter Rotich committed
                return true;
    
    
            //NOTE: Previously logged overdue event is NOT annuled.
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            $sql='UPDATE '.TICKET_TABLE.' SET isoverdue=0, updated=NOW() ';
    
    Peter Rotich's avatar
    Peter Rotich committed
            //clear due date if it's in the past
    
            if($this->getDueDate() && Misc::db2gmtime($this->getDueDate()) <= Misc::gmtime())
    
    Peter Rotich's avatar
    Peter Rotich committed
                $sql.=', duedate=NULL';
    
    
            //Clear SLA if est. due date is in the past
    
            if($this->getSLADueDate() && Misc::db2gmtime($this->getSLADueDate()) <= Misc::gmtime())
    
    Peter Rotich's avatar
    Peter Rotich committed
            $sql.=' WHERE ticket_id='.db_input($this->getId());
    
            return (db_query($sql) && db_affected_rows());
        }
    
    
        //Dept Tranfer...with alert.. done by staff
    
    Jared Hancock's avatar
    Jared Hancock committed
        function transfer($deptId, $comments, $alert = true) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg, $thisstaff;
    
            if (!$this->checkStaffPerm($thisstaff, TicketModel::PERM_TRANSFER))
    
    Jared Hancock's avatar
    Jared Hancock committed
                return false;
    
    
            $currentDept = $this->getDeptName(); //Current department
    
            if(!$deptId || !$this->setDeptId($deptId))
                return false;
    
            if($this->isClosed()) $this->reopen();
    
            $this->reload();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $dept = $this->getDept();
    
    Jared Hancock's avatar
    Jared Hancock committed
            if(!$this->getSLAId() || $this->getSLA()->isTransient())
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Make sure the new department allows assignment to the
            // currently assigned agent (if any)
            if ($this->isAssigned()
                    && ($staff=$this->getStaff())
                    && $dept->assignMembersOnly()
                    && !$dept->isMember($staff)) {
                $this->setStaffId(0);
            }
    
    
            /*** log the transfer comments as internal note - with alerts disabled - ***/
    
            $title=sprintf(_S('Ticket transferred from %1$s to %2$s'),
                $currentDept, $this->getDeptName());
    
            $note = $this->logNote($title, $comments, $thisstaff, false);
    
            $this->logEvent('transferred');
    
            //Send out alerts if enabled AND requested
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$alert || !$cfg->alertONTransfer())
    
                return true; //no alerts!!
    
             if (($email = $dept->getAlertEmail())
    
                         && ($tpl = $dept->getTemplate())
                         && ($msg=$tpl->getTransferAlertMsgTemplate())) {
    
                $msg = $this->replaceVars($msg->asArray(),
                    array('comments' => $comments, 'staff' => $thisstaff));
    
    Jared Hancock's avatar
    Jared Hancock committed
                $recipients=array();
                //Assigned staff or team... if any
                if($this->isAssigned() && $cfg->alertAssignedONTransfer()) {
                    if($this->getStaffId())
                        $recipients[]=$this->getStaff();
                    elseif($this->getTeamId() && ($team=$this->getTeam()) && ($members=$team->getMembers()))
    
                        $recipients = array_merge($recipients, $members);
    
    Jared Hancock's avatar
    Jared Hancock committed
                } elseif($cfg->alertDeptMembersONTransfer() && !$this->isAssigned()) {
                    //Only alerts dept members if the ticket is NOT assigned.
    
                    if(($members=$dept->getMembersForAlerts()))
    
                        $recipients = array_merge($recipients, $members);
    
    Jared Hancock's avatar
    Jared Hancock committed
                }
    
                //Always alert dept manager??
                if($cfg->alertDeptManagerONTransfer() && $dept && ($manager=$dept->getManager()))
                    $recipients[]= $manager;
    
    Jared Hancock's avatar
    Jared Hancock committed
                $sentlist=array();
    
                    'inreplyto'=>$note->getEmailMessageId(),
    
                    'references'=>$note->getEmailReferences(),
                    'thread'=>$note);
    
                foreach( $recipients as $k=>$staff) {
                    if(!is_object($staff) || !$staff->isAvailable() || in_array($staff->getEmail(), $sentlist)) continue;
    
                    $alert = $this->replaceVars($msg, array('recipient' => $staff));
    
                    $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $sentlist[] = $staff->getEmail();
    
    Peter Rotich's avatar
    Peter Rotich committed
        function claim() {
            global $thisstaff;