Вход Регистрация
Файл: library/XenForo/Model/Conversation.php
Строк: 2597
<?php

/**
 * Model for conversations.
 *
 * @package XenForo_Conversation
 */
class XenForo_Model_Conversation extends XenForo_Model
{
    const 
FETCH_LAST_MESSAGE_AVATAR 0x01;
    const 
FETCH_FIRST_MESSAGE 0x02;
    const 
FETCH_RECEIVED_BY 0x04;

    
/**
     * Gets the specified conversation master record.
     *
     * @param integer $conversationId
     *
     * @return array|false
     */
    
public function getConversationMasterById($conversationId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT conversation_master.*
            FROM xf_conversation_master AS conversation_master
            WHERE conversation_master.conversation_id = ?
        '
$conversationId);
    }

    
/**
     * Gets the specified conversation message record.
     *
     * @param integer $messageId
     *
     * @return array|false
     */
    
public function getConversationMessageById($messageId)
    {
        return 
$this->_getDb()->fetchRow('
            SELECT message.*,
                user.*, IF(user.username IS NULL, message.username, user.username) AS username,
                user_profile.*
            FROM xf_conversation_message AS message
            LEFT JOIN xf_user AS user ON
                (user.user_id = message.user_id)
            LEFT JOIN xf_user_profile AS user_profile ON
                (user_profile.user_id = message.user_id)
            WHERE message.message_id = ?
        '
$messageId);
    }

    
/**
     * Gets the specified user conversation.
     *
     * @param integer $conversationId
     * @param integer|array $viewingUser Can be a user array, or a user ID (for B.C. purposes)
     * @param array $fetchOptions Options for extra data to fetch
     *
     * @return array|false
     */
    
public function getConversationForUser($conversationId$viewingUser, array $fetchOptions = array())
    {
        if (
is_array($viewingUser))
        {
            
$this->standardizeViewingUserReference($viewingUser);
            
$userId $viewingUser['user_id'];
        }
        else
        {
            
$userId $viewingUser;
        }

        
$joinOptions $this->prepareConversationFetchOptions($fetchOptions);

        return 
$this->_getDb()->fetchRow('
            SELECT conversation_master.*,
                conversation_user.*,
                conversation_recipient.recipient_state, conversation_recipient.last_read_date
                ' 
$joinOptions['selectFields'] . '
            FROM xf_conversation_user AS conversation_user
            INNER JOIN xf_conversation_master AS conversation_master ON
                (conversation_user.conversation_id = conversation_master.conversation_id)
            INNER JOIN xf_conversation_recipient AS conversation_recipient ON
                    (conversation_user.conversation_id = conversation_recipient.conversation_id
                    AND conversation_user.owner_user_id = conversation_recipient.user_id)
                ' 
$joinOptions['joinTables'] . '
            WHERE conversation_user.conversation_id = ?
                AND conversation_user.owner_user_id = ?
        '
, array($conversationId$userId));
    }

    
/**
     * Gets information about all recipients of a conversation.
     *
     * @param integer $conversationId
     * @param array $fetchOptions Options for extra data to fetch
     *
     * @return array Format: [user id] => info
     */
    
public function getConversationRecipients($conversationId, array $fetchOptions = array())
    {
        
$sqlClauses $this->prepareConversationFetchOptions($fetchOptions);

        return 
$this->fetchAllKeyed('
            SELECT conversation_recipient.*,
                user.*, user_option.*
                ' 
$sqlClauses['selectFields'] . '
            FROM xf_conversation_recipient AS conversation_recipient
            LEFT JOIN xf_user AS user ON
                (user.user_id = conversation_recipient.user_id)
            LEFT JOIN xf_user_option AS user_option ON
                (user_option.user_id = user.user_id)
            ' 
$sqlClauses['joinTables'] . '
            WHERE conversation_recipient.conversation_id = ?
            ORDER BY user.username
        '
'user_id'$conversationId'deleted');
    }

    
/**
     * Gets info about a single recipient of a conversation.
     *
     * @param integer $conversationId
     * @param integer $userId
     * @param array $fetchOptions Options for extra data to fetch
     *
     * @return array|false
     */
    
public function getConversationRecipient($conversationId$userId, array $fetchOptions = array())
    {
        
$sqlClauses $this->prepareConversationFetchOptions($fetchOptions);

        return 
$this->_getDb()->fetchRow('
            SELECT conversation_recipient.*,
                user.*, user_option.*
                ' 
$sqlClauses['selectFields'] . '
            FROM xf_conversation_recipient AS conversation_recipient
            LEFT JOIN xf_user AS user ON
                (user.user_id = conversation_recipient.user_id)
            LEFT JOIN xf_user_option AS user_option ON
                (user_option.user_id = user.user_id)
            ' 
$sqlClauses['joinTables'] . '
            WHERE conversation_recipient.conversation_id = ?
                AND conversation_recipient.user_id = ?
        '
, array($conversationId$userId));
    }

    
/**
     * Get conversations that a user can see, ordered by the latest message first.
     *
     * @param integer $userId
     * @param array $conditions Conditions for the WHERE clause
     * @param array $fetchOptions Options for extra data to fetch
     *
     * @return array Format: [conversation id] => info
     */
    
public function getConversationsForUser($userId, array $conditions = array(), array $fetchOptions = array())
    {
        
$whereClause $this->prepareConversationConditions($conditions$fetchOptions);
        
$sqlClauses $this->prepareConversationFetchOptions($fetchOptions);

        
$limitOptions $this->prepareLimitFetchOptions($fetchOptions);

        
$sql $this->limitQueryResults(
            
'
                SELECT conversation_master.*,
                    conversation_user.*,
                    conversation_starter.*,
                    conversation_master.username AS username,
                    conversation_recipient.recipient_state, conversation_recipient.last_read_date
                    ' 
$sqlClauses['selectFields'] . '
                FROM xf_conversation_user AS conversation_user
                INNER JOIN xf_conversation_master AS conversation_master ON
                    (conversation_user.conversation_id = conversation_master.conversation_id)
                INNER JOIN xf_conversation_recipient AS conversation_recipient ON
                    (conversation_user.conversation_id = conversation_recipient.conversation_id
                    AND conversation_user.owner_user_id = conversation_recipient.user_id)
                LEFT JOIN xf_user AS conversation_starter ON
                    (conversation_starter.user_id = conversation_master.user_id)
                    ' 
$sqlClauses['joinTables'] . '
                WHERE conversation_user.owner_user_id = ?
                    AND ' 
$whereClause '
                ORDER BY conversation_user.last_message_date DESC
            '
$limitOptions['limit'], $limitOptions['offset']
        );

        return 
$this->fetchAllKeyed($sql'conversation_id'$userId);
    }

    
/**
     * Gets the total number of conversations that a user has.
     *
     * @param integer $userId
     * @param array $conditions Conditions for the WHERE clause
     * @param array $fetchConditions Only used in tandem with $conditions at this point
     *
     * @return integer
     */
    
public function countConversationsForUser($userId, array $conditions = array())
    {
        
$fetchOptions = array();
        
$whereClause $this->prepareConversationConditions($conditions$fetchOptions);
        
$sqlClauses $this->prepareConversationFetchOptions($fetchOptions);

        return 
$this->_getDb()->fetchOne('
            SELECT COUNT(*)
            FROM xf_conversation_user AS conversation_user
            INNER JOIN xf_conversation_master AS conversation_master ON
                (conversation_user.conversation_id = conversation_master.conversation_id)
            INNER JOIN xf_conversation_recipient AS conversation_recipient ON
                (conversation_user.conversation_id = conversation_recipient.conversation_id
                AND conversation_user.owner_user_id = conversation_recipient.user_id)
                ' 
$sqlClauses['joinTables'] . '
            WHERE conversation_user.owner_user_id = ?
                AND ' 
$whereClause
        
$userId);
    }

    
/**
     * Get the specified conversations that a user can see, ordered by last message first.
     *
     * @param integer $userId
     * @param array $conversationIds
     *
     * @return array Format: [conversation id] => info
     */
    
public function getConversationsForUserByIds($userId, array $conversationIds)
    {
        if (!
$conversationIds)
        {
            return array();
        }

        return 
$this->fetchAllKeyed('
            SELECT conversation_master.*,
                conversation_user.*,
                conversation_starter.*,
                conversation_master.username AS username,
                conversation_recipient.recipient_state, conversation_recipient.last_read_date
            FROM xf_conversation_user AS conversation_user
            INNER JOIN xf_conversation_master AS conversation_master ON
                (conversation_user.conversation_id = conversation_master.conversation_id)
            INNER JOIN xf_conversation_recipient AS conversation_recipient ON
                (conversation_user.conversation_id = conversation_recipient.conversation_id
                AND conversation_user.owner_user_id = conversation_recipient.user_id)
            LEFT JOIN xf_user AS conversation_starter ON
                (conversation_starter.user_id = conversation_master.user_id)
            WHERE conversation_user.owner_user_id = ?
                AND conversation_user.conversation_id IN (' 
$this->_getDb()->quote($conversationIds) . ')
            ORDER BY conversation_user.last_message_date DESC
        '
'conversation_id'$userId);
    }

    
/**
     * Return a list of users that have received conversations that the specified user has receved,
     * where the recieving user matches the given username search string
     *
     * @param integer $userId
     * @param string $searchString
     *
     * @return array
     */
    
public function findConversationRecipientsForUser($userId$searchString)
    {
        
$userIds $this->_getDb()->fetchCol('
            SELECT DISTINCT recp1.user_id
            FROM xf_conversation_recipient AS recp1
            INNER JOIN xf_conversation_recipient AS recp2 ON
                (recp2.conversation_id = recp1.conversation_id
                AND recp2.user_id = ?
                AND recp2.recipient_state = '
active')
        '
$userId);

        return 
$this->_getUsersMatchingCriteria($userIds$searchString);
    }

    
/**
     * Return a list of users that have started conversations that the specified user has receved,
     * where the starting user matches the given username search string
     *
     * @param integer $userId
     * @param string $searchString
     *
     * @return array
     */
    
public function findConversationStartersForUser($userId$searchString)
    {
        
$userIds $this->_getDb()->fetchCol('
            SELECT DISTINCT conversation_master.user_id
            FROM xf_conversation_master AS conversation_master
            INNER JOIN xf_conversation_user AS conversation_user ON
                (conversation_user.conversation_id = conversation_master.conversation_id
                AND conversation_user.owner_user_id = ?)
        '
$userId);

        return 
$this->_getUsersMatchingCriteria($userIds$searchString);
    }

    
/**
     * Return a list of users that have posted in conversations that the specified user has receved,
     * where the recieving user matches the given username search string
     *
     * @param integer $userId
     * @param string $searchString
     *
     * @return array
     */
    
public function findConversationRespondersForUser($userId$searchString)
    {
        
$userIds $this->_getDb()->fetchCol('
            SELECT DISTINCT conversation_message.user_id
            FROM xf_conversation_message AS conversation_message
            INNER JOIN xf_conversation_user AS conversation_user ON
                (conversation_user.conversation_id = conversation_message.conversation_id)
            WHERE conversation_user.owner_user_id = ?
        '
$userId);

        return 
$this->_getUsersMatchingCriteria($userIds$searchString);
    }

    
/**
     * Fetches a list of users matching the user ID and user name search criteria.
     * Used in conjunction with this class's findConversation[x]ForUser() methods.
     *
     * @param array $userIds
     * @param string $searchString
     *
     * @return array
     */
    
protected function _getUsersMatchingCriteria(array $userIds$searchString)
    {
        if (
$userIds)
        {
            return 
$this->_getUserModel()->getUsers(array(
                
'user_id' => $userIds,
                
'username' => array($searchString 'r')
            ));
        }

        return array();
    }

    
/**
     * Gets conversation IDs in the specified range. The IDs returned will be those immediately
     * after the "start" value (not including the start), up to the specified limit.
     *
     * @param integer $start IDs greater than this will be returned
     * @param integer $limit Number of posts to return
     *
     * @return array List of IDs
     */
    
public function getConversationIdsInRange($start$limit)
    {
        
$db $this->_getDb();

        return 
$db->fetchCol($db->limit('
            SELECT conversation_id
            FROM xf_conversation_master
            WHERE conversation_id > ?
            ORDER BY conversation_id
        '
$limit), $start);
    }

    public function 
prepareConversationFetchOptions(array $fetchOptions)
    {
        
$selectFields '';
        
$joinTables '';
        
$db $this->_getDb();

        if (!empty(
$fetchOptions['join']))
        {
            if (
$fetchOptions['join'] & self::FETCH_LAST_MESSAGE_AVATAR)
            {
                
$selectFields .= ',
                    last_message_user.avatar_date AS last_message_avatar_date,
                    last_message_user.gender AS last_message_gender,
                    last_message_user.gravatar AS last_message_gravatar'
;
                
$joinTables .= '
                    LEFT JOIN xf_user AS last_message_user ON
                        (last_message_user.user_id = conversation_user.last_message_user_id)'
;
            }

            if (
$fetchOptions['join'] & self::FETCH_FIRST_MESSAGE)
            {
                
$selectFields .= ',
                    conversation_message.message'
;
                
$joinTables .= '
                    INNER JOIN xf_conversation_message AS conversation_message ON
                        (conversation_message.message_id = conversation_master.first_message_id)'
;
            }
        }

        if (!empty(
$fetchOptions['receivedUserId']))
        {
            
$joinTables .= '
                INNER JOIN xf_conversation_recipient AS check_recipient ON
                    (check_recipient.conversation_id = conversation_master.conversation_id
                    AND check_recipient.user_id = ' 
$db->quote($fetchOptions['receivedUserId']) . ')';
        }

        if (isset(
$fetchOptions['draftUserId']))
        {
            if (!empty(
$fetchOptions['draftUserId']))
            {
                
$selectFields .= ',
                    draft.message AS draft_message, draft.extra_data AS draft_extra'
;
                
$joinTables .= '
                    LEFT JOIN xf_draft AS draft
                        ON (draft.draft_key = CONCAT('
conversation-', conversation_master.conversation_id)
                        AND draft.user_id = ' 
$db->quote($fetchOptions['draftUserId']) . ')';
            }
            else
            {
                
$selectFields .= ',
                    '' AS draft_message, NULL AS draft_extra'
;
            }
        }

        return array(
            
'selectFields' => $selectFields,
            
'joinTables'   => $joinTables
        
);
    }

    
/**
     * Prepares a set of conditions against which to select conversations.
     *
     * @param array $conditions List of conditions.
     * --popupMode (boolean) constrains results to unread, or sent within timeframe specified by options->conversationPopupExpiryHours
     * @param array $fetchOptions The fetch options that have been provided. May be edited if criteria requires.
     *
     * @return string Criteria as SQL for where clause
     */
    
public function prepareConversationConditions(array $conditions, array &$fetchOptions)
    {
        
$sqlConditions = array();
        
$db $this->_getDb();

        if (!empty(
$conditions['popupMode']))
        {
            
$cutOff XenForo_Application::$time 3600 XenForo_Application::get('options')->conversationPopupExpiryHours;
            
$sqlConditions[] = 'conversation_user.is_unread = 1 OR conversation_user.last_message_date > ' $cutOff;
        }

        if (isset(
$conditions['is_unread']))
        {
            
$sqlConditions[] = 'conversation_user.is_unread = ' . ($conditions['is_unread'] ? 0);
        }

        if (isset(
$conditions['is_starred']))
        {
            
$sqlConditions[] = 'conversation_user.is_starred = ' . ($conditions['is_starred'] ? 0);
        }

        if (!empty(
$conditions['last_message_date']) && is_array($conditions['last_message_date']))
        {
            list(
$operator$cutOff) = $conditions['last_message_date'];

            
$this->assertValidCutOffOperator($operator);
            
$sqlConditions[] = "conversation_user.last_message_date $operator " $db->quote($cutOff);
        }

        if (!empty(
$conditions['search_type']) && !empty($conditions['search_user_id']))
        {
            switch (
$conditions['search_type'])
            {
                case 
'started_by':
                    
$sqlConditions[] = 'conversation_master.user_id = ' $db->quote($conditions['search_user_id']);
                    break;

                case 
'received_by':
                    
$fetchOptions['receivedUserId'] = $conditions['search_user_id'];
                    break;
            }
        }

        return 
$this->getConditionsForClause($sqlConditions);
    }

    
/**
     * Get messages within a given conversation.
     *
     * @param integer $conversationId
     * @param array $fetchOptions Options for extra data to fetch
     *
     * @return array Format [message id] => info
     */
    
public function getConversationMessages($conversationId, array $fetchOptions = array())
    {
        
$limitOptions $this->prepareLimitFetchOptions($fetchOptions);

        return 
$this->fetchAllKeyed($this->limitQueryResults(
            
'
                SELECT message.*,
                    user.*, IF(user.username IS NULL, message.username, user.username) AS username,
                    user_profile.*
                FROM xf_conversation_message AS message
                LEFT JOIN xf_user AS user ON
                    (user.user_id = message.user_id)
                LEFT JOIN xf_user_profile AS user_profile ON
                    (user_profile.user_id = message.user_id)
                WHERE message.conversation_id = ?
                ORDER BY message.message_date
            '
$limitOptions['limit'], $limitOptions['offset']
        ), 
'message_id'$conversationId);
    }

    
/**
     * Finds the newest conversation messages after the specified date.
     *
     * @param integer $conversationId
     * @param integer $date
     * @param array $fetchOptions
     *
     * @return array [message id] => info
     */
    
public function getNewestConversationMessagesAfterDate($conversationId$date, array $fetchOptions = array())
    {
        
$limitOptions $this->prepareLimitFetchOptions($fetchOptions);

        return 
$this->fetchAllKeyed($this->limitQueryResults(
            
'
                SELECT message.*,
                    user.*, IF(user.username IS NULL, message.username, user.username) AS username,
                    user_profile.*
                FROM xf_conversation_message AS message
                LEFT JOIN xf_user AS user ON
                    (user.user_id = message.user_id)
                LEFT JOIN xf_user_profile AS user_profile ON
                    (user_profile.user_id = message.user_id)
                WHERE message.conversation_id = ?
                    AND message.message_date > ?
                ORDER BY message.message_date DESC
            '
$limitOptions['limit'], $limitOptions['offset']
        ), 
'message_id', array($conversationId$date));
    }

    
/**
     * Gets the next message in a conversation, post after the specified date. This is useful
     * for finding the first unread message, for example.
     *
     * @param integer $conversationId
     * @param integer $messageDate Finds first message posted after this
     *
     * @return array|false
     */
    
public function getNextMessageInConversation($conversationId$messageDate)
    {
        
$db $this->_getDb();

        return 
$db->fetchRow($db->limit('
            SELECT *
            FROM xf_conversation_message
            WHERE conversation_id = ?
                AND message_date > ?
            ORDER BY message_date
        '
1), array($conversationId$messageDate));
    }

    
/**
     * Count the number of messages before a given date in a conversation.
     *
     * @param integer $conversationId
     * @param integer $messageDate
     *
     * @return integer
     */
    
public function countMessagesBeforeDateInConversation($conversationId$messageDate)
    {
        return 
$this->_getDb()->fetchOne('
            SELECT COUNT(*)
            FROM xf_conversation_message AS conversation_message
            WHERE conversation_message.conversation_id = ?
                AND conversation_message.message_date < ?
        '
, array($conversationId$messageDate));
    }

    
/**
     * Prepare a conversation for display or further processing.
     *
     * @param array $conversation
     *
     * @return array
     */
    
public function prepareConversation(array $conversation)
    {
        
$conversation['isNew'] = ($conversation['last_message_date'] > $conversation['last_read_date']);
        
$conversation['title'] = XenForo_Helper_String::censorString($conversation['title']);

        
$conversation['lastPageNumbers'] = $this->getLastPageNumbers($conversation['reply_count']);

        
$conversation['last_message'] = array(
            
'message_id' => $conversation['last_message_id'],
            
'message_date' => $conversation['last_message_date'],
            
'user_id' => $conversation['last_message_user_id'],
            
'username' => $conversation['last_message_username']
        );

        if (isset(
$conversation['last_message_avatar_date']))
        {
            
$conversation['last_message']['avatar_date'] = $conversation['last_message_avatar_date'];
        }

        if (isset(
$conversation['last_message_gender']))
        {
            
$conversation['last_message']['gender'] = $conversation['last_message_gender'];
        }

        if (isset(
$conversation['last_message_gravatar']))
        {
            
$conversation['last_message']['gravatar'] = $conversation['last_message_gravatar'];
        }

        if (
array_key_exists('user_group_id'$conversation))
        {
            
$conversation $this->_getUserModel()->prepareUser($conversation);
            
$conversation['isIgnored'] = false// don't ignore conversations - the user can leave the conversation instead
        
}

        
$conversation['recipientNames'] = $conversation['recipients'] ? @unserialize($conversation['recipients']) : array();

        return 
$conversation;
    }

    
/**
     * Prepare a collection of conversations for display or further processing.
     *
     * @param array $conversations
     *
     * @return array
     */
    
public function prepareConversations(array $conversations)
    {
        foreach (
$conversations AS &$conversation)
        {
            
$conversation $this->prepareConversation($conversation);
        }

        return 
$conversations;
    }

    
/**
     * Prepare a message for display or further processing.
     *
     * @param array $message
     * @param array $conversation
     *
     * @return array Prepared message
     */
    
public function prepareMessage(array $message, array $conversation)
    {
        
$message['isNew'] = ($message['message_date'] > $conversation['last_read_date']);

        
$message['canEdit'] = $this->canEditMessage($message$conversation);

        
$message['canReport'] = $this->canReportMessage($message$conversation);

        if (
array_key_exists('user_group_id'$message))
        {
            
$message $this->_getUserModel()->prepareUser($message);
            
$message['isIgnored'] = false// don't ignore messages in conversations - the user can leave the conversation instead
        
}

        return 
$message;
    }

    
/**
     * Prepare a collection of messages (in the same conversation) for display or
     * further processing.
     *
     * @param array $messages
     * @param array $conversation
     *
     * @return array Prepared messages
     */
    
public function prepareMessages(array $messages, array $conversation)
    {
        
$pagePosition 0;

        foreach (
$messages AS &$message)
        {
            
$message $this->prepareMessage($message$conversation);

            
$message['position_on_page'] = ++$pagePosition;
        }

        return 
$messages;
    }

    
/**
     * Gets the maximum message date in a list of messages.
     *
     * @param array $messages
     *
     * @return integer Max message date timestamp; 0 if no messages
     */
    
public function getMaximumMessageDate(array $messages)
    {
        
$max 0;
        foreach (
$messages AS $message)
        {
            if (
$message['message_date'] > $max)
            {
                
$max $message['message_date'];
            }
        }

        return 
$max;
    }

    
/**
     * Add the details of a new conversation reply to conversation recipients.
     *
     * @param array $conversation Conversation info
     * @param array|null $replyUser Information about the user who replied
     * @param array|null $messageInfo Array containing 'message', which is the text the message being sent
     *
     * @return array $recipients
     */
    
public function addConversationReplyToRecipients(array $conversation, array $replyUser null, array $messageInfo null)
    {
        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        
$extraData = array('message_id' => $conversation['last_message_id']);

        
$recipients $this->getConversationRecipients($conversation['conversation_id']);
        foreach (
$recipients AS $recipient)
        {
            if (empty(
$recipient['user_id']))
            {
                continue; 
// deleted user
            
}

            switch (
$recipient['recipient_state'])
            {
                case 
'active':
                    
$db->query('
                        UPDATE xf_conversation_user SET
                            is_unread = 1,
                            reply_count = ' 
$db->quote($conversation['reply_count']) . ',
                            last_message_date = ' 
$db->quote($conversation['last_message_date']) . ',
                            last_message_id = ' 
$db->quote($conversation['last_message_id']) . ',
                            last_message_user_id = ' 
$db->quote($conversation['last_message_user_id']) . ',
                            last_message_username = ' 
$db->quote($conversation['last_message_username']) . '
                        WHERE conversation_id = ?
                            AND owner_user_id = ?
                    '
, array($conversation['conversation_id'], $recipient['user_id']));

                    
$this->rebuildUnreadConversationCountForUser($recipient['user_id']);

                    
$this->insertConversationAlert($conversation$recipient'reply'$replyUser$extraData$messageInfo);
                    break;

                case 
'deleted':
                    
$this->insertConversationRecipient($conversation$recipient['user_id'], $recipient);
                    
$this->insertConversationAlert($conversation$recipient'reply'$replyUser$extraData$messageInfo);
                    break;
            }
        }

        
XenForo_Db::commit($db);

        return 
$recipients;
    }

    
/**
     * Insert a new conversation recipient record.
     *
     * @param array $conversation Conversation info
     * @param integer $user User to insert for
     * @param array $existingRecipient Information about the existing recipient record (if there is one)
     * @param string $insertState State to insert the conversation for with this user
     *
     * @return boolean True if an insert was required (may be false if user is already an active recipient or is ignoring)
     */
    
public function insertConversationRecipient(array $conversation$userId, array $existingRecipient null$insertState 'active')
    {
        if (
$existingRecipient === null)
        {
            
$existingRecipient $this->getConversationRecipient($conversation['conversation_id'], $userId);
        }

        if (
$existingRecipient)
        {
            if (empty(
$existingRecipient['user_id'])
                || 
$existingRecipient['recipient_state'] == 'deleted_ignored'
                
|| $existingRecipient['recipient_state'] == $insertState)
            {
                return 
false;
            }
        }

        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        
$rowsAffected $db->query('
            INSERT INTO xf_conversation_recipient
                (conversation_id, user_id, recipient_state, last_read_date)
            VALUES
                (?, ?, ?, 0)
            ON DUPLICATE KEY UPDATE recipient_state = VALUES(recipient_state)
        '
, array($conversation['conversation_id'], $userId$insertState))->rowCount();

        
$inserted = ($rowsAffected == 1);

        if (
$insertState == 'active')
        {
            
$db->query('
                INSERT IGNORE INTO xf_conversation_user
                    (conversation_id, owner_user_id, is_unread, reply_count,
                    last_message_date, last_message_id, last_message_user_id, last_message_username)
                VALUES
                    (?, ?, 1, ?,
                    ?, ?, ?, ?)
            '
, array(
                
$conversation['conversation_id'], $userId$conversation['reply_count'],
                
$conversation['last_message_date'], $conversation['last_message_id'],
                
$conversation['last_message_user_id'], $conversation['last_message_username']
            ));

            
$this->rebuildUnreadConversationCountForUser($userId);

            if (
$inserted)
            {
                
$db->query('
                    UPDATE xf_conversation_master SET
                        recipient_count = recipient_count + 1
                    WHERE conversation_id = ?
                '
$conversation['conversation_id']);
            }
        }

        
XenForo_Db::commit($db);

        return (
$insertState == 'active');
    }

    
/**
     * Inserts an alert for this conversation.
     *
     * @param array $conversation
     * @param array $alertUser User to notify
     * @param string $action Action taken out (values: insert, reply, join)
     * @param array|null $triggerUser User triggering the alert; defaults to last user to reply
     * @param array|null $extraData
     * @param array|null $messageInfo Array containing the text of the message being sent (if applicable) as 'message'
     */
    
public function insertConversationAlert(array $conversation, array $alertUser$action,
        array 
$triggerUser null, array $extraData null, array &$messageInfo null
    
)
    {
        if (!
$triggerUser)
        {
            
$triggerUser = array(
                
'user_id' => $conversation['last_message_user_id'],
                
'username' => $conversation['last_message_username']
            );
        }

        if (
$triggerUser['user_id'] == $alertUser['user_id'])
        {
            return;
        }

        if (
$alertUser['email_on_conversation'] && $alertUser['user_state'] == 'valid' && !$alertUser['is_banned'])
        {
            if (!isset(
$conversation['titleCensored']))
            {
                
$conversation['titleCensored'] = XenForo_Helper_String::censorString($conversation['title']);
            }

            if (isset(
$messageInfo['message']) && XenForo_Application::get('options')->emailConversationIncludeMessage)
            {
                if (!isset(
$messageInfo['messageText']))
                {
                    
$bbCodeParserText XenForo_BbCode_Parser::create(XenForo_BbCode_Formatter_Base::create('Text'));
                    
$messageInfo['messageText'] = new XenForo_BbCode_TextWrapper($messageInfo['message'], $bbCodeParserText);

                    
$bbCodeParserHtml XenForo_BbCode_Parser::create(XenForo_BbCode_Formatter_Base::create('HtmlEmail'));
                    
$messageInfo['messageHtml'] = new XenForo_BbCode_TextWrapper($messageInfo['message'], $bbCodeParserHtml);
                }

                
$emailTemplate "conversation_{$action}_messagetext";
            }
            else
            {
                
$emailTemplate "conversation_{$action}";
            }

            
$mail XenForo_Mail::create($emailTemplate, array(
                
'receiver' => $alertUser,
                
'sender' => $triggerUser,
                
'options' => XenForo_Application::get('options'),
                
'conversation' => $conversation,
                
'message' => $messageInfo,
            ), 
$alertUser['language_id']);

            
$mail->enableAllLanguagePreCache();
            
$mail->queue($alertUser['email'], $alertUser['username']);
        }
    }

    
/**
     * Delets a conversation record for a specific user. If all users have deleted the conversation,
     * it will be completely removed.
     *
     * @param integer $conversationId
     * @param integer $userId
     * @param string $deleteType Type of deletion (either delete, or delete_ignore)
     */
    
public function deleteConversationForUser($conversationId$userId$deleteType)
    {
        
$recipientState = ($deleteType == 'delete_ignore' 'deleted_ignored' 'deleted');

        
$db $this->_getDb();
        
XenForo_Db::beginTransaction($db);

        
$db->update('xf_conversation_recipient',
            array(
'recipient_state' => $recipientState),
            
'conversation_id = ' $db->quote($conversationId) . ' AND user_id = ' $db->quote($userId)
        );
        
$db->delete('xf_conversation_user',
            
'conversation_id = ' $db->quote($conversationId) . ' AND owner_user_id = ' $db->quote($userId)
        );
        
$db->delete('xf_user_alert',
            
'content_type = 'conversation' AND content_id = ' $db->quote($conversationId)
                . 
' AND alerted_user_id = ' $db->quote($userId)
        );

        
$this->rebuildUnreadConversationCountForUser($userId);

        
$haveActive false;
        foreach (
$this->getConversationRecipients($conversationId) AS $recipient)
        {
            if (empty(
$recipient['user_id']))
            {
                continue; 
// deleted user
            
}

            if (
$recipient['recipient_state'] == 'active')
            {
                
$haveActive true;
                break;
            }
        }

        if (!
$haveActive)
        {
            
// no one has the conversation any more, so delete it
            
$dw XenForo_DataWriter::create('XenForo_DataWriter_ConversationMaster');
            
$dw->setExistingData($conversationId);
            
$dw->delete();
        }

        
XenForo_Db::commit($db);
    }

    
/**
     * Marks the conversation as read to a certain point for a user.
     *
     * @param integer $conversationId
     * @param integer $userId
     * @param integer $newReadDate Timestamp to mark as read until
     * @param integer $lastMessageDate Date of last message; only marks whole conversation read if more than this date
     * @param boolean $updateVisitor If true, reduces the conversations_unread counter for the visitor; should be false for replies
     */
    
public function markConversationAsRead($conversationId$userId$newReadDate$lastMessageDate 0$updateVisitor true)
    {
        
$db $this->_getDb();

        
XenForo_Db::beginTransaction($db);

        
$this->_updateConversationReadDate($conversationId$userId$newReadDate$db);

        if (
$newReadDate >= $lastMessageDate)
        {
            
$rowsChanged $db->update('xf_conversation_user',
                array(
'is_unread' => 0),
                
'conversation_id = ' $db->quote($conversationId) . ' AND owner_user_id = ' $db->quote($userId)
            );
            if (
$rowsChanged)
            {
                
$db->query('
                    UPDATE xf_user SET
                        conversations_unread = IF(conversations_unread > 1, conversations_unread - 1, 0)
                    WHERE user_id = ?
                '
$userId);

                
$visitor XenForo_Visitor::getInstance();
                if (
$updateVisitor && $userId == $visitor['user_id'] && $visitor['conversations_unread'] >= 1)
                {
                    
$visitor['conversations_unread'] -= 1;
                }
            }
        }

        
XenForo_Db::commit($db);
    }

    
/**
     * Marks the conversation as (completely) unread for a user.
     *
     * @param integer $conversationId
     * @param integer $userId
     */
    
public function markConversationAsUnread($conversationId$userId)
    {
        
$db $this->_getDb();

        
XenForo_Db::beginTransaction($db);

        
$this->_updateConversationReadDate($conversationId$userId0$db);

        
$rowsChanged $db->update('xf_conversation_user',
            array(
'is_unread' => 1),
            
'conversation_id = ' $db->quote($conversationId) . ' AND owner_user_id = ' $db->quote($userId)
        );
        if (
$rowsChanged)
        {
            
$db->query('
                UPDATE xf_user SET
                    conversations_unread = conversations_unread + 1
                WHERE user_id = ?
            '
$userId);

            
$visitor XenForo_Visitor::getInstance();
            if (
$userId == $visitor['user_id'])
            {
                
$visitor['conversations_unread'] += 1;
            }
        }

        
XenForo_Db::commit($db);
    }

    public function 
changeConversationStarState($conversationId$userId$starred)
    {
        
$db $this->_getDb();
        return (bool)
$db->update('xf_conversation_user',
            array(
'is_starred' => $starred 0),
            
'conversation_id = ' $db->quote($conversationId) . ' AND owner_user_id = ' $db->quote($userId)
        );
    }

    
/**
     * Update the read date record for a conversation
     *
     * @param integer $conversationId
     * @param integer $userId
     * @param integer $newReadDate
     * @param Zend_Db_Adapter_Abstract $db
     *
     * @return integer $newReadDate
     */
    
protected function _updateConversationReadDate($conversationId$userId$newReadDateZend_Db_Adapter_Abstract $db null)
    {
        if (empty(
$db))
        {
            
$db $this->_getDb();
        }

        
$db->update('xf_conversation_recipient', array('last_read_date' => $newReadDate),
            
'conversation_id = ' $db->quote($conversationId) . ' AND user_id = ' $db->quote($userId)
        );

        return 
$newReadDate;
    }

    
/**
     * Gets the count of unread conversations for the specified user.
     *
     * @param integer $userId
     *
     * @return integer
     */
    
public function countUnreadConversationsForUser($userId)
    {
        return 
$this->_getDb()->fetchOne('
            SELECT COUNT(*)
            FROM xf_conversation_user AS conversation_user
            INNER JOIN xf_conversation_master AS conversation_master ON
                (conversation_user.conversation_id = conversation_master.conversation_id)
            INNER JOIN xf_conversation_recipient AS conversation_recipient ON
                    (conversation_user.conversation_id = conversation_recipient.conversation_id
                    AND conversation_user.owner_user_id = conversation_recipient.user_id)
            WHERE conversation_user.owner_user_id = ?
                AND conversation_user.is_unread = 1
        '
$userId);
    }

    
/**
     * Recalculates the unread conversation count for the specified user.
     *
     * @param integer $userId
     */
    
public function rebuildUnreadConversationCountForUser($userId)
    {
        
$db $this->_getDb();
        
$db->update('xf_user', array(
            
'conversations_unread' => $this->countUnreadConversationsForUser($userId)
        ), 
'user_id = ' $db->quote($userId));
    }

    
/**
     * Determines if the viewing user can start conversations in general.
     *
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canStartConversations(&$errorPhraseKey '', array $viewingUser null)
    {
        
// moved for easier access on all pages
        
return $this->_getUserModel()->canStartConversations($errorPhraseKey$viewingUser);
    }

    
/**
     * Determines if the viewing user can start a conversation with the given user.
     * Does not check standard conversation permissions.
     *
     * @param array $user
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canStartConversationWithUser(array $user, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (
$user['user_id'] == $viewingUser['user_id'])
        {
            
$errorPhraseKey 'you_may_not_start_conversation_with_yourself';
            return 
false;
        }

        if (empty(
$user['permissions']))
        {
            if (empty(
$user['global_permission_cache']))
            {
                
$user['global_permission_cache'] = $this->_getDb()->fetchOne('
                    SELECT cache_value
                    FROM xf_permission_combination
                    WHERE permission_combination_id = ?
                '
$user['permission_combination_id']);
            }

            
$user['permissions'] = XenForo_Permission::unserializePermissions($user['global_permission_cache']);
        }

        
$userModel $this->_getUserModel();

        if (!
$userModel->canBypassUserPrivacy($null$viewingUser)
            && !
XenForo_Permission::hasPermission($user['permissions'], 'conversation''receive')
        )
        {
            return 
false;
        }

        return (
            
$this->canStartConversations($errorPhraseKey$viewingUser)
            && !
$user['is_banned']
            && 
$userModel->passesPrivacyCheck(
                
$user['allow_send_personal_conversation'], $user$viewingUser
            
)
        );
    }

    
/**
     * Determines if the specified user can reply to the conversation.
     * Does not check conversation viewing permissions.
     *
     * @param array $conversation
     * @param string $errorPhraseKey Returned phrase key for a specific error
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canReplyToConversation(array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        return (
$conversation['user_id'] == $viewingUser['user_id'] || $conversation['conversation_open']);
    }

    
/**
     * Determines if the specified user can edit the conversation.
     * Does not check conversation viewing permissions.
     *
     * @param array $conversation
     * @param string $errorPhraseKey Returned phrase key for a specific error
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canEditConversation(array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        return (
$conversation['user_id'] == $viewingUser['user_id']);
    }

    
/**
     * Determines if the specified user can invite users the conversation.
     * Does not check conversation viewing permissions.
     *
     * @param array $conversation
     * @param string $errorPhraseKey Returned phrase key for a specific error
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canInviteUsersToConversation(array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (
XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''alwaysInvite'))
        {
            return 
true;
        }

        if (!
$conversation['conversation_open'])
        {
            return 
false;
        }

        if (
$conversation['user_id'] == $viewingUser['user_id'] || $conversation['open_invite'])
        {
            if (
XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''start'))
            {
                
$remaining $this->allowedAdditionalConversationRecipients($conversation$viewingUser);
                return (
$remaining == -|| $remaining >= 1);
            }
        }

        return 
false;
    }

    
/**
     * Determines if the specified user can edit the specified message within a conversation
     *
     * @param array $message
     * @param array $conversation
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canEditMessage(array $message, array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        
// moderator permission, so ignore conversation open/closed and time limit
        
if (XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''editAnyPost'))
        {
            return 
true;
        }

        
// no editing of messages in a closed conversation by normal users
        
if (!$conversation['conversation_open'])
        {
            
$errorPhraseKey 'conversation_is_closed';
            return 
false;
        }

        
// own message
        
if ($message['user_id'] == $viewingUser['user_id'])
        {
            if (
XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''editOwnPost'))
            {
                
$editLimit XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''editOwnPostTimeLimit');

                if (
$editLimit != -&& (!$editLimit || $message['message_date'] < XenForo_Application::$time 60 $editLimit))
                {
                    
$errorPhraseKey = array('message_edit_time_limit_expired''minutes' => $editLimit);
                    return 
false;
                }

                return 
true;
            }
        }

        
$errorPhraseKey 'you_may_not_edit_this_message';
        return 
false;
    }

    
/**
     * Checks that the viewing user may report the specified conversation message
     *
     * @param array $message
     * @param array $conversation
     * @param string
     * @param boolean $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canReportMessage(array $message, array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        return 
$this->_getUserModel()->canReportContent($errorPhraseKey$viewingUser);
    }

    
/**
     * Check permission to view a reported conversation
     *
     * @param array $message
     * @param array $conversation
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canManageReportedMessage(array $message, array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (
$viewingUser['is_moderator'] && XenForo_Permission::hasPermission($viewingUser['permissions'], 'general''warn'))
        {
            return 
true;
        }

        
$errorPhraseKey 'you_may_not_manage_this_reported_content';
        return 
false;
    }

    
/**
     * Determines if a new attachment can be posted in the specified conversation,
     * with the given permissions. If no permissions are specified, permissions
     * are retrieved from the currently visiting user. This does not check viewing permissions.
     *
     * @param array $conversation Info about the conversation posting in
     * @param string $errorPhraseKey Returned phrase key for a specific error
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canUploadAndManageAttachment(array $conversation = array(), &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (!
$viewingUser['user_id'])
        {
            return 
false;
        }

        if (
$conversation)
        {
            
// must be able to reply to the conversation in order to upload an attachment
            
if (!$this->canReplyToConversation($conversation$errorPhraseKey$viewingUser))
            {
                return 
false;
            }
        }
        else if (!
$this->canStartConversations($errorPhraseKey$viewingUser))
        {
            return 
false;
        }

        return 
XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''uploadAttachment');
    }

    
/**
     * Determines if the specified user can view attachments in the specified conversation
     *
     * @param array $conversation
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canViewAttachmentOnConversation(array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        if (!
$viewingUser['user_id'])
        {
            return 
false;
        }

        return (
$conversation['owner_user_id'] == $viewingUser['user_id']);
    }

    
/**
     * Determines if the specified user can view an attachment to the specified message
     *
     * @param array $message
     * @param array $conversation
     * @param string $errorPhraseKey
     * @param array|null $viewingUser
     *
     * @return boolean
     */
    
public function canViewAttachmentOnConversationMessage(array $message, array $conversation, &$errorPhraseKey '', array $viewingUser null)
    {
        return 
$this->canViewAttachmentOnConversation($conversation$errorPhraseKey$viewingUser);
    }

    
/**
     * Calculates the allowed number of additional conversation receiptions the
     * viewing user can add to the given conversation.
     * @param array $conversation Conversation; if empty array, assumes new conversation
     * @param array|null $viewingUser
     *
     * @return integer -1 means unlimited; 0 is no more invites; other is remaining count
     */
    
public function allowedAdditionalConversationRecipients(array $conversation, array $viewingUser null)
    {
        
$this->standardizeViewingUserReference($viewingUser);

        
$maxRecipients XenForo_Permission::hasPermission($viewingUser['permissions'], 'conversation''maxRecipients');
        if (
$maxRecipients == -1)
        {
            return -
1;
        }

        if (
$conversation)
        {
            
$remaining = ($maxRecipients $conversation['recipient_count'] + 1); // +1 represents self; self doesn't count
            
return max(0$remaining);
        }
        else
        {
            return 
$maxRecipients;
        }
    }

    
/**
     * Gets the quote text for the specified conversation message.
     *
     * @param array $message
     * @param integer $maxQuoteDepth Max depth of quotes (-1 for unlimited)
     *
     * @return string
     */
    
public function getQuoteForConversationMessage(array $message$maxQuoteDepth 0)
    {
        return 
'[QUOTE="' $message['username']
            . 
', convMessage: ' $message['message_id']
            . (!empty(
$message['user_id']) ? ', member: ' $message['user_id'] : '')
            . 
'"]'
            
trim(XenForo_Helper_String::stripQuotes($message['message'], $maxQuoteDepth))
            . 
"[/QUOTE]n";
    }

    
/**
     * Returns the last few page numbers of a conversation
     *
     * @param integer $replyCount
     *
     * @return array|boolean
     */
    
public function getLastPageNumbers($replyCount)
    {
        
$perPage XenForo_Application::get('options')->messagesPerPage;

        if ((
$replyCount +1) > $perPage)
        {
            return 
XenForo_Helper_Discussion::getLastPageNumbers($replyCount$perPage);
        }
        else
        {
            return 
false;
        }
    }

    
/**
     * Gets the set of attachment params required to allow uploading.
     *
     * @param array $conversation
     * @param array $contentData Information about the content, for URL building
     * @param array|null $viewingUser
     * @param string|null $tempHash
     *
     * @return array|bool
     */
    
public function getAttachmentParams(array $conversation = array(), array $contentData = array(), array $viewingUser null$tempHash null)
    {
        if (
$this->canUploadAndManageAttachment($conversation$null$viewingUser))
        {
            
$existing is_string($tempHash) && strlen($tempHash) == 32;
            
$output = array(
                
'hash' => $existing $tempHash md5(uniqid(''true)),
                
'content_type' => 'conversation_message',
                
'content_data' => $contentData
            
);
            if (
$existing)
            {
                
$attachmentModel $this->getModelFromCache('XenForo_Model_Attachment');
                
$output['attachments'] = $attachmentModel->prepareAttachments(
                    
$attachmentModel->getAttachmentsByTempHash($tempHash)
                );
            }

            return 
$output;
        }
        else
        {
            return 
false;
        }
    }

    
/**
     * Gets the attachments that belong to the given messages, and merges them in with
     * their parent message (in the attachments key). The attachments key will not be
     * set if no attachments are found for the message.
     *
     * @param array $messages
     *
     * @return array Messages, with attachments added where necessary
     */
    
public function getAndMergeAttachmentsIntoConversationMessages(array $messages)
    {
        
$messageIds = array();

        foreach (
$messages AS $messageId => $message)
        {
            if (
$message['attach_count'])
            {
                
$messageIds[] = $messageId;
            }
        }

        if (
$messageIds)
        {
            
$attachmentModel $this->_getAttachmentModel();

            
$attachments $attachmentModel->getAttachmentsByContentIds('conversation_message'$messageIds);

            foreach (
$attachments AS $attachment)
            {
                
$messages[$attachment['content_id']]['attachments'][$attachment['attachment_id']] = $attachmentModel->prepareAttachment($attachment);
            }
        }

        return 
$messages;
    }

    
/**
     * @param integer $userId
     *
     * @return array
     */
    
public function getConversationsStartedByUser($userId)
    {
        return 
$this->fetchAllKeyed("
            SELECT *
            FROM xf_conversation_master
            WHERE user_id = ?
            ORDER BY start_date DESC
        "
'conversation_id'$userId);
    }

    
/**
     * @param integer $userId
     *
     * @return int Total conversations deleted
     */
    
public function deleteConversationsStartedByUser($userId)
    {
        
$conversations $this->getConversationsStartedByUser($userId);
        
$i 0;
        foreach (
$conversations AS $conversation)
        {
            
$dw XenForo_DataWriter::create('XenForo_DataWriter_ConversationMaster');
            
$dw->setExistingData($conversationtrue);
            
$dw->delete();
            
$i++;
        }

        return 
$i;
    }

    
/**
     * @return XenForo_Model_Alert
     */
    
protected function _getAlertModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_Alert');
    }

    
/**
     * @return XenForo_Model_Attachment.
     */
    
protected function _getAttachmentModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_Attachment');
    }

    
/**
     * @return XenForo_Model_User
     */
    
protected function _getUserModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_User');
    }
}
Онлайн: 1
Реклама