Skip to content
Snippets Groups Projects
class.thread.php 85.3 KiB
Newer Older
<?php
/*********************************************************************
    class.thread.php

Peter Rotich's avatar
Peter Rotich committed
    Thread of things!
Peter Rotich's avatar
Peter Rotich committed
    XXX: Please DO NOT add any ticket related logic! use ticket class.

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

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

    vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
include_once(INCLUDE_DIR.'class.ticket.php');
include_once(INCLUDE_DIR.'class.draft.php');
include_once(INCLUDE_DIR.'class.role.php');
//Ticket thread.
class Thread extends VerySimpleModel {
    static $meta = array(
        'table' => THREAD_TABLE,
        'pk' => array('id'),
        'joins' => array(
            'ticket' => array(
                'constraint' => array(
                    'object_type' => "'T'",
                    'object_id' => 'TicketModel.ticket_id',
            'task' => array(
                'constraint' => array(
                    'object_type' => "'A'",
                    'object_id' => 'Task.id',
                ),
            ),
            'collaborators' => array(
                'reverse' => 'Collaborator.thread',
            ),
                'reverse' => 'ThreadEntry.thread',
            'events' => array(
                'reverse' => 'ThreadEvent.thread',
    const MODE_STAFF = 1;
    const MODE_CLIENT = 2;

    var $_entries;
    var $_collaborators; // Cache for collabs
    var $_participants;
Peter Rotich's avatar
Peter Rotich committed

    function getId() {
Peter Rotich's avatar
Peter Rotich committed
    function getObjectId() {
Peter Rotich's avatar
Peter Rotich committed
    function getObjectType() {
    function getObject() {

        if (!$this->_object)
            $this->_object = ObjectModel::lookup(
                    $this->getObjectId(), $this->getObjectType());

        return $this->_object;
    }

    function getNumAttachments() {
        return Attachment::objects()->filter(array(
            'thread_entry__thread' => $this
        ))->count();
Peter Rotich's avatar
Peter Rotich committed
    function getNumEntries() {
        return $this->entries->count();
    }
    function getEntries($criteria=false) {
        if (!isset($this->_entries)) {
            $this->_entries = $this->entries->annotate(array(
                'has_attachments' => SqlAggregate::COUNT(SqlCase::N()
                    ->when(array('attachments__inline'=>0), 1)
                    ->otherwise(null)
                ),
            ));
            $this->_entries->exclude(array('flags__hasbit'=>ThreadEntry::FLAG_HIDDEN));
            if ($criteria)
                $this->_entries->filter($criteria);
        }
        return $this->_entries;
    // Collaborators
    function getNumCollaborators() {
        return $this->collaborators->count();
    }

    function getNumActiveCollaborators() {

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

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

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

    function getCollaborators($criteria=array()) {

        if ($this->_collaborators && !$criteria)
            return $this->_collaborators;

        $collaborators = $this->collaborators
            ->filter(array('thread_id' => $this->getId()));

        if (isset($criteria['isactive']))
            $collaborators->filter(array('isactive' => $criteria['isactive']));

        // TODO: sort by name of the user
        $collaborators->order_by('user__name');

        if (!$criteria)
            $this->_collaborators = $collaborators;

        return $collaborators;
    }

    function addCollaborator($user, $vars, &$errors, $event=true) {

        if (!$user)
            return null;

        $vars = array_merge(array(
                'threadId' => $this->getId(),
                'userId' => $user->getId()), $vars);
        if (!($c=Collaborator::add($vars, $errors)))
            return null;

        $this->_collaborators = null;

        if ($event)
            $this->getEvents()->log($this->getObject(),
                'collab',
                array('add' => array($user->getId() => array(
                        'name' => $user->getName()->getOriginal(),
                        'src' => @$vars['source'],
                    ))
                )
            );

        return $c;
    }

    function updateCollaborators($vars, &$errors) {
        global $thisstaff;

        if (!$thisstaff) return;

        //Deletes
        if($vars['del'] && ($ids=array_filter($vars['del']))) {
            $collabs = array();
            foreach ($ids as $k => $cid) {
                if (($c=Collaborator::lookup($cid))
                        && $c->getThreadId() == $this->getId()
                        && $c->delete())
                     $collabs[] = $c;
            }
            $this->getEvents()->log($this->getObject(), 'collab', array(
                'del' => array($c->user_id => array('name' => $c->getName()->getOriginal()))
            ));
        }

        //statuses
        $cids = null;
        if($vars['cid'] && ($cids=array_filter($vars['cid']))) {
            $this->collaborators->filter(array(
                'thread_id' => $this->getId(),
                'id__in' => $cids
            ))->update(array(
                'updated' => SqlFunction::NOW(),
                'isactive' => 1,
            ));
        }

        if ($cids) {
            $this->collaborators->filter(array(
                'thread_id' => $this->getId(),
                Q::not(array('id__in' => $cids))
            ))->update(array(
                'updated' => SqlFunction::NOW(),
                'isactive' => 0,
            ));
        }

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

        return true;
    }


    //UserList of participants (collaborators)
    function getParticipants() {

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

            $this->_participants = $list;
        }

        return $this->_participants;
    }


    // Render thread
    function render($type=false, $options=array()) {

        $mode = $options['mode'] ?: self::MODE_STAFF;

        // Register thread actions prior to rendering the thread.
        if (!class_exists('tea_showemailheaders'))
            include_once INCLUDE_DIR . 'class.thread_actions.php';

        $entries = $this->getEntries();
        if ($type && is_array($type))
            $entries->filter(array('type__in' => $type));

Peter Rotich's avatar
Peter Rotich committed
        if ($options['sort'] && !strcasecmp($options['sort'], 'DESC'))
            $entries->order_by('-id');

        // Precache all the attachments on this thread
        AttachmentFile::objects()->filter(array(
            'attachments__thread_entry__thread__id' => $this->id
        ))->all();

        $events = $this->getEvents();
        $inc = ($mode == self::MODE_STAFF) ? STAFFINC_DIR : CLIENTINC_DIR;
        include $inc . 'templates/thread-entries.tmpl.php';
Peter Rotich's avatar
Peter Rotich committed
    function getEntry($id) {
Peter Rotich's avatar
Peter Rotich committed
        return ThreadEntry::lookup($id, $this->getId());
    function getEvents() {
        return $this->events;
    }

    /**
     * postEmail
     *
     * After some security and sanity checks, attaches the body and subject
     * of the message in reply to this thread item
     *
     * Parameters:
     * mailinfo - (array) of information about the email, with at least the
     *          following keys
     *      - mid - (string) email message-id
     *      - name - (string) personal name of email originator
     *      - email - (string<email>) originating email address
     *      - subject - (string) email subject line (decoded)
     *      - body - (string) email message body (decoded)
     */
    function postEmail($mailinfo, $entry=null) {
        // +==================+===================+=============+
        // | Orig Thread-Type | Reply Thread-Type | Requires    |
        // +==================+===================+=============+
        // | *                | Message (M)       | From: Owner |
        // | *                | Note (N)          | From: Staff |
        // | Response (R)     | Message (M)       |             |
        // | Message (M)      | Response (R)      | From: Staff |
        // +------------------+-------------------+-------------+

        if (!$object = $this->getObject()) {
            // How should someone find this thread?
            return false;
        }
        elseif ($object instanceof Ticket && (
               !$mailinfo['staffId']
            && $object->isClosed()
            && !$object->isReopenable()
        )) {
            // Ticket is closed, not reopenable, and email was not submitted
            // by an agent. Email cannot be submitted
            return false;
        }

        $vars = array(
            'mid' =>    $mailinfo['mid'],
            'header' => $mailinfo['header'],
            'poster' => $mailinfo['name'],
            'origin' => 'Email',
            'source' => 'Email',
            'ip' =>     '',
            'reply_to' => $entry,
Loading
Loading full blame...