Skip to content
Snippets Groups Projects
class.ticket.php 119 KiB
Newer Older
  • Learn to ignore specific revisions
  •         $this->setLastMsgId($message->getId());
        }
    
    Jared Hancock's avatar
    Jared Hancock committed
    
        //DeptId can NOT be 0. No orphans please!
    
            // Make sure it's a valid department
            if ($deptId == $this->getDeptId() || !($dept=Dept::lookup($deptId))) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                return false;
    
            }
            $this->dept = $dept;
            return $this->save();
    
        // Set staff ID...assign/unassign/release (id can be 0)
    
    Peter Rotich's avatar
    Peter Rotich committed
        function setStaffId($staffId) {
    
            if (!is_numeric($staffId))
    
    Peter Rotich's avatar
    Peter Rotich committed
                return false;
    
            $this->staff = Staff::lookup($staffId);
            return $this->save();
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function setSLAId($slaId) {
    
            if ($slaId == $this->getSLAId())
                return true;
    
            $this->sla = Sla::lookup($slaId);
            return $this->save();
    
    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) {
    
            if (!is_numeric($teamId))
                return false;
    
            $this->team = Team::lookup($teamId);
            return $this->save();
    
    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
    
            $hadStatus = $this->getStatusId();
    
            if ($this->getStatusId() == $status->getId())
                return true;
    
    
            // Perform checks on the *new* status, _before_ the status changes
    
            $ecb = null;
    
            switch ($status->getState()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                case 'closed':
    
                    // Check if ticket is closeable
                    $closeable = $this->isCloseable();
                    if ($closeable !== true)
                        $errors['err'] = $closeable ?: sprintf(__('%s cannot be closed'), __('This ticket'));
    
                    if ($errors)
    
                    $this->closed = $this->lastupdate = SqlFunction::NOW();
                    $this->duedate = null;
    
                    if ($thisstaff && $set_closing_agent)
    
                        $this->staff = $thisstaff;
    
                    $this->clearOverdue(false);
    
                    $ecb = function($t) use ($status) {
                        $t->logEvent('closed', array('status' => array($status->getId(), $status->getName())));
    
                        $t->deleteDrafts();
                    };
                    break;
                case 'open':
    
                    // TODO: check current status if it allows for reopening
    
                    if ($this->isClosed()) {
    
                        $this->closed = $this->lastupdate = $this->reopened = SqlFunction::NOW();
    
                        $ecb = function ($t) {
    
                            $t->logEvent('reopened', false, null, 'closed');
    
    
                    // If the ticket is not open then clear answered flag
                    if (!$this->isOpen())
    
                        $this->isanswered = 0;
    
    Jared Hancock's avatar
    Jared Hancock committed
                    break;
    
            $this->status = $status;
    
            if (!$this->save())
    
                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 ($hadStatus) {
    
                $alert = false;
                if ($comments) {
                    // Send out alerts if comments are included
                    $alert = true;
    
                    $this->logNote(__('Status Changed'), $comments, $thisstaff, $alert);
    
            // Log events via callback
    
            if ($ecb)
                $ecb($this);
            elseif ($hadStatus)
                // Don't log the initial status change
                $this->logEvent('edited', array('status' => $status->getId()));
    
    
            return true;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function setState($state, $alerts=false) {
    
            switch (strtolower($state)) {
            case 'open':
                return $this->setStatus('open');
            case 'closed':
                return $this->setStatus('closed');
            case 'answered':
                return $this->setAnsweredState(1);
            case 'unanswered':
                return $this->setAnsweredState(0);
            case 'overdue':
                return $this->markOverdue();
            case 'notdue':
                return $this->clearOverdue();
            case 'unassined':
                return $this->unassign();
            }
            // FIXME: Throw and excception and add test cases
    
    Jared Hancock's avatar
    Jared Hancock committed
            return false;
        }
    
    
    
    
        function setAnsweredState($isanswered) {
    
            $this->isanswered = $isanswered;
            return $this->save();
    
        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) : false;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function onNewTicket($message, $autorespond=true, $alertstaff=true) {
            global $cfg;
    
            //Log stuff here...
    
            if (!$autorespond && !$alertstaff)
                return true; //No alerts to send.
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            /* ------ SEND OUT NEW TICKET AUTORESP && ALERTS ----------*/
    
                || !($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()
                && $dept->autoRespONNewTicket()
                && ($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'],
    
            // Send alert to out sleepy & idle staff.
    
                && $cfg->alertONNewTicket()
                && ($email=$dept->getAlertEmail())
                && ($msg=$tpl->getNewTicketAlertMsgTemplate())
            ) {
    
                $msg = $this->replaceVars($msg->asArray(), array('message' => $message));
    
                $recipients = $sentlist = array();
                // Exclude the auto responding email just incase it's from staff member.
    
                if ($message instanceof ThreadEntry && $message->isAutoReply())
    
                    $sentlist[] = $this->getEmail();
    
    
                // 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();
                }
    
                // Only alerts dept members if the ticket is NOT assigned.
                if ($cfg->alertDeptMembersONNewTicket() && !$this->isAssigned()) {
    
                    if ($members = $dept->getMembersForAlerts()->all())
    
                        $recipients = array_merge($recipients, $members);
    
                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();
    
        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())
    
    
            //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()) {
    
            $this->isanswered = 1;
            $this->save();
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            $vars = array_merge($options,
                array(
                    'activity' => _S('New Response'),
                    'threadentry' => $response
                )
            );
    
    Peter Rotich's avatar
    Peter Rotich committed
            $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())
            ) {
    
            }
            // 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 (0 === strcasecmp($R2->getEmail(), $R->mailbox.'@'.$R->host)) {
    
                $poster = $entry->getStaff();
    
                // Skip the ticket owner
    
                $skip[$this->getUserId()] = 1;
    
                '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;
    
    
            $this->isanswered = 0;
            $this->lastupdate = SqlFunction::NOW();
            $this->save();
    
            // 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()) {
    
                // 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 (!$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 (($lastrep = $this->getLastRespondent())
                        && $lastrep->isAvailable()
                    ) {
                        $this->setStaffId($lastrep->getId()); //direct assignment;
                    }
                    else {
                        // unassign - last respondent is not available.
                        $this->setStaffId(0);
                    }
                }
            }
    
    
            // 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.
            }
    
            $dept = $this->getDept();
            $email = $dept->getAutoRespEmail();
    
            // 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())
            ) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                return;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            // Alert recipients
    
            $recipients = array();
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            //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;
    
    Peter Rotich's avatar
    Peter Rotich committed
                $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;
    
            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;
    
            $user_comments = (bool) $comments;
    
            $comments = $comments ?: _S('Ticket assignment');
            $assigner = $thisstaff ?: _S('SYSTEM (Auto Assignment)');
    
            //Log an internal note - no alerts on the internal note.
    
            if ($user_comments) {
                $note = $this->logNote(
                    sprintf(_S('Ticket Assigned to %s'), $assignee->getName()),
                    $comments, $assigner, false);
            }
    
            // See if we need to send alerts
            if (!$alert || !$cfg->alertONAssignment())
                return true; //No alerts!
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            $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;
            }
    
            // Get the message template
    
            if ($recipients
    
                && ($msg=$tpl->getAssignedAlertMsgTemplate())
            ) {
    
                $msg = $this->replaceVars($msg->asArray(),
    
                    array('comments' => $comments,
                          'assignee' => $assignee,
                          'assigner' => $assigner
                    )
                );
                // Send the alerts.
                $sentlist = array();
    
                $options = $note instanceof ThreadEntry
                    ? array(
                        'inreplyto'=>$note->getEmailMessageId(),
                        'references'=>$note->getEmailReferences(),
                        'thread'=>$note)
                    : 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, $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;
    
            // 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)
                );
                // Recipients
                $recipients = array();
                // Assigned staff or team... if any
                if ($this->isAssigned() && $cfg->alertAssignedONOverdueTicket()) {
                    if ($this->getStaffId()) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                        $recipients[]=$this->getStaff();
    
                    }
                    elseif ($this->getTeamId()
                        && ($team = $this->getTeam())
                        && ($members = $team->getMembers())
                    ) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                        $recipients=array_merge($recipients, $members);
    
                    }
                }
                elseif ($cfg->alertDeptMembersONOverdueTicket() && !$this->isAssigned()) {
                    // Only alerts dept members if the ticket is NOT assigned.
    
                    if ($members = $dept->getMembersForAlerts()->all())
    
                        $recipients = array_merge($recipients, $members);
    
                // Always alert dept manager??
                if ($cfg->alertDeptManagerONOverdueTicket()
                    && $dept && ($manager=$dept->getManager())
                ) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                    $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();
    
    Jared Hancock's avatar
    Jared Hancock committed
                }
            }
            return true;
    
        // TemplateVariable interface
    
        function asVar() {
           return $this->getNumber();
    
        function getVar($tag) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    
            switch(mb_strtolower($tag)) {
    
            case 'phone':
            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 new FormattedDate($this->getCreateDate());
                break;
             case 'due_date':
                if ($due = $this->getEstDueDate())
                    return new FormattedDate($due);
                break;
            case 'close_date':
                if ($this->isClosed())
                    return new FormattedDate($this->getCloseDate());
                break;
            case 'last_update':
                return new FormattedDate($this->last_update);
            case 'user':
                return $this->getOwner();
            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];
    
        static function getVarScope() {
            $base = array(
    
                'assigned' => __('Assigned agent and/or team'),
    
                'close_date' => array(
    
                    'class' => 'FormattedDate', 'desc' => __('Date Closed'),
    
                ),
                'create_date' => array(
    
                    'class' => 'FormattedDate', 'desc' => __('Date created'),
    
                    'class' => 'Dept', 'desc' => __('Department'),
    
                'due_date' => array(
    
                    'class' => 'FormattedDate', 'desc' => __('Due Date'),
    
                'email' => __('Default email address of ticket owner'),
    
                'name' => array(
                    'class' => 'PersonsName', 'desc' => __('Name of ticket owner'),
                ),
    
                'number' => __('Ticket number'),
                'phone' => __('Phone number of ticket owner'),
    
                'priority' => array(
    
                    'class' => 'Priority', 'desc' => __('Priority'),
    
                ),
                'recipients' => array(
    
                    'class' => 'UserList', 'desc' => __('List of all recipient names'),
    
                'source' => __('Source'),
    
                'status' => array(
    
                    'class' => 'TicketStatus', 'desc' => __('Status'),
    
                ),
                'staff' => array(
                    'class' => 'Staff', 'desc' => __('Assigned/closing agent'),
                ),
                'subject' => 'Subject',
                'team' => array(
                    'class' => 'Team', 'desc' => __('Assigned/closing team'),
                ),
                'thread' => array(
    
                    'class' => 'TicketThread', 'desc' => __('Ticket Thread'),
    
                    'class' => 'Topic', 'desc' => __('Help topic'),
                ),
                // XXX: Isn't lastreponse and lastmessage more useful
                'last_update' => array(
                    'class' => 'FormattedDate', 'desc' => __('Time of last update'),
    
                ),
                'user' => array(
                    'class' => 'User', 'desc' => __('Ticket owner'),
                ),
            );
    
            $extra = VariableReplacer::compileFormScope(TicketForm::getInstance());
            return $base + $extra;
        }
    
    
        //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) {
            global $cfg;
    
            if ($this->isOverdue())
    
    Jared Hancock's avatar
    Jared Hancock committed
                return true;
    
    
            $this->isoverdue = 1;
            if (!$this->save())
    
    Jared Hancock's avatar
    Jared Hancock committed
                return false;
    
    
            $this->logEvent('overdue');
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->onOverdue($whine);
    
    Jared Hancock's avatar
    Jared Hancock committed
            return true;
        }
    
    
        function clearOverdue($save=true) {
    
            if (!$this->isOverdue())
    
    Peter Rotich's avatar
    Peter Rotich committed
                return true;
    
    
            //NOTE: Previously logged overdue event is NOT annuled.
    
    
            $this->isoverdue = 0;
    
            // clear due date if it's in the past
            if ($this->getDueDate() && Misc::db2gmtime($this->getDueDate()) <= Misc::gmtime())
                $this->duedate = null;
    
            // Clear SLA if est. due date is in the past
            if ($this->getSLADueDate() && Misc::db2gmtime($this->getSLADueDate()) <= Misc::gmtime())
                $this->sla = null;
    
            return $save ? $this->save() : true;
    
        //Dept Tranfer...with alert.. done by staff
    
    Peter Rotich's avatar
    Peter Rotich committed
        function transfer(TransferForm $form, &$errors, $alert=true) {
            global $thisstaff, $cfg;
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Check if staff can do the transfer
            if (!$this->checkStaffPerm($thisstaff, Ticket::PERM_TRANSFER))
    
    Jared Hancock's avatar
    Jared Hancock committed
                return false;
    
    Peter Rotich's avatar
    Peter Rotich committed
            $cdept = $this->getDept(); // Current department
            $dept = $form->getDept(); // Target department
            if (!$dept || !($dept instanceof Dept))
                $errors['dept'] = __('Department selection required');
            elseif ($dept->getid() == $this->getDeptId())
                $errors['dept'] = sprintf(
                        __('%s already in the department'), __('Ticket'));
            else {
                $this->dept_id = $dept->getId();
    
                // 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->staff_id = 0;
                }
            }
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($errors || !$this->save(true))
    
                return false;
    
            if ($this->isClosed())
                $this->reopen();
    
            if (!$this->getSLAId() || $this->getSLA()->isTransient())
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Log transfer event
            $this->logEvent('transferred');
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Post internal note if any
    
    Peter Rotich's avatar
    Peter Rotich committed
            $note = null;
            $comments = $form->getField('comments')->getClean();
            if ($comments) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                $title = sprintf(__('%1$s transferred from %2$s to %3$s'),
                        __('Ticket'),
                       $cdept->getName(),
                        $dept->getName());
    
    Peter Rotich's avatar
    Peter Rotich committed
                $_errors = array();
                $note = $this->postNote(
    
    Peter Rotich's avatar
    Peter Rotich committed
                        array('note' => $comments, 'title' => $title),
    
    Peter Rotich's avatar
    Peter Rotich committed
                        $_errors, $thisstaff, false);
    
            //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(),
    
    Peter Rotich's avatar
    Peter Rotich committed
                    array('comments' => $note, 'staff' => $thisstaff));
    
                // Recipients
                $recipients = array();
                // Assigned staff or team... if any
    
    Jared Hancock's avatar
    Jared Hancock committed
                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);
    
                    }
                }
                elseif ($cfg->alertDeptMembersONTransfer() && !$this->isAssigned()) {
                    // Only alerts dept members if the ticket is NOT assigned.
    
                    if ($members = $dept->getMembersForAlerts()->all())
    
                        $recipients = array_merge($recipients, $members);
    
                // Always alert dept manager??
                if ($cfg->alertDeptManagerONTransfer()
                    && $dept
                    && ($manager=$dept->getManager())
                ) {
                    $recipients[] = $manager;
                }
    
    Peter Rotich's avatar
    Peter Rotich committed
                $sentlist = $options = array();
    
                if ($note) {
                    $options += 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();