Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Repository/Conversation.php
<?php

namespace XF\Repository;

use
XF\Mvc\Entity\Repository;
use
XF\Util\Arr;

use function
count, intval, is_array, is_string;

class
Conversation extends Repository
{
    public function
findUserConversations(\XF\Entity\User $user, $forList = true)
    {
       
/** @var \XF\Finder\ConversationUser $finder */
       
$finder = $this->finder('XF:ConversationUser');
       
$finder->forUser($user, $forList)
            ->
setDefaultOrder('last_message_date', 'desc');

        return
$finder;
    }

    public function
findUserConversationsForPopupList(\XF\Entity\User $user, $unread, $cutOff = null)
    {
       
$finder = $this->findUserConversations($user);
       
$finder->where('is_unread', $unread ? 1 : 0);

        if (
$cutOff)
        {
           
$finder->where('last_message_date', '>', $cutOff);
        }

        return
$finder;
    }

    public function
getUserConversationsForPopup(\XF\Entity\User $user,$maxLimit, $cutOff, array $extraWith = [])
    {
       
$unreadFinder = $this->findUserConversationsForPopupList($user, true)->with($extraWith);
       
$unread = $unreadFinder->fetch($maxLimit);
       
$totalUnread = $unread->count();

        if (
$totalUnread < $maxLimit)
        {
           
$readFinder = $this->findUserConversationsForPopupList($user, false, $cutOff)->with($extraWith);
           
$read = $readFinder->fetch($maxLimit - $totalUnread)->toArray();
        }
        else
        {
           
$read = [];
        }

        return [
           
'unread' => $unread->toArray(),
           
'read' => $read
       
];
    }

    public function
markUserConversationRead(\XF\Entity\ConversationUser $userConv, $newRead = null)
    {
        if (
$newRead === null)
        {
           
$newRead = \XF::$time;
        }

        if (!
$userConv->Master)
        {
            return;
        }

       
$markRecipient = ($userConv->Recipient && $newRead > $userConv->Recipient->last_read_date);
       
$markUser = ($userConv->is_unread && $newRead >= $userConv->Master->last_message_date);

        if (
$markRecipient || $markUser)
        {
           
$this->db()->beginTransaction();

            if (
$markRecipient)
            {
               
$userConv->Recipient->last_read_date = $newRead;
               
$userConv->Recipient->save(false, false);
            }

            if (
$markUser)
            {
               
$userConv->is_unread = false;
               
$userConv->save(false, false);
            }

           
$this->db()->commit();
        }
    }

    public function
markUserConversationUnread(\XF\Entity\ConversationUser $userConv)
    {
        if (!
$userConv->Master)
        {
            return;
        }

       
$markRecipient = ($userConv->Recipient && $userConv->Recipient->last_read_date > 0);
       
$markUser = ($userConv->is_unread ? false : true);

        if (
$markRecipient || $markUser)
        {
           
$this->db()->beginTransaction();

            if (
$markRecipient)
            {
               
$userConv->Recipient->last_read_date = 0;
               
$userConv->Recipient->save(false, false);
            }

            if (
$markUser)
            {
               
$userConv->is_unread = true;
               
$userConv->save(false, false);
            }

           
$this->db()->commit();
        }
    }

   
/**
     * @param \XF\Entity\User $user
     *
     * @return \XF\Mvc\Entity\Finder
     */
   
public function findConversationsStartedByUser(\XF\Entity\User $user)
    {
        return
$this->finder('XF:ConversationMaster')
            ->
where('user_id', $user->user_id)
            ->
setDefaultOrder('start_date', 'DESC');
    }

    public function
getValidatedRecipients($recipients, \XF\Entity\User $from, &$error = null, $checkPrivacy = true)
    {
       
$error = null;

        if (
is_string($recipients))
        {
           
$recipients = Arr::stringToArray($recipients, '#\s*,\s*#');
        }
        else if (
$recipients instanceof \XF\Entity\User)
        {
           
$recipients = [$recipients];
        }

        if (!
count($recipients))
        {
            return [];
        }

        if (
$recipients instanceof \XF\Mvc\Entity\AbstractCollection)
        {
           
$first = $recipients->first();
        }
        else
        {
           
$first = reset($recipients);
        }

        if (
$first instanceof \XF\Entity\User)
        {
           
$type = 'user';
        }
        else
        {
           
$type = 'name';
        }

        foreach (
$recipients AS $k => $recipient)
        {
            if (
$type == 'user' && !($recipient instanceof \XF\Entity\User))
            {
                throw new \
InvalidArgumentException("Recipient at key $k must be a user entity");
            }
        }

        if (
$type == 'name')
        {
           
/** @var \XF\Repository\User $userRepo */
           
$userRepo = $this->repository('XF:User');
           
$users = $userRepo->getUsersByNames($recipients, $notFound, ['Privacy']);

            if (
$notFound)
            {
               
$error = \XF::phraseDeferred('the_following_recipients_could_not_be_found_x',
                    [
'names' => implode(', ', $notFound)]
                );
            }
        }
        else
        {
           
$users = $recipients;
        }

        if (
is_array($users))
        {
            if (
count($users) === 1 && reset($users) === $from)
            {
               
$error = \XF::phraseDeferred('you_cannot_start_conversation_with_yourself');
                return [];
            }
        }
        else if (
$users instanceof \XF\Mvc\Entity\ArrayCollection)
        {
            if (
$users->count() === 1 && $users->first() === $from)
            {
               
$error = \XF::phraseDeferred('you_cannot_start_conversation_with_yourself');
                return [];
            }
        }

       
$newRecipients = [];
       
$cantStart = [];

        foreach (
$users AS $user)
        {
            if (
$user->user_id == $from->user_id)
            {
                continue;
            }

            if (
$checkPrivacy && !$from->canStartConversationWith($user))
            {
               
$cantStart[$user->user_id] = $user->username;
                continue;
            }

           
$newRecipients[$user->user_id] = $user;
        }

        if (
$cantStart)
        {
           
$error = \XF::phraseDeferred('you_may_not_start_a_conversation_with_the_following_recipients_x',
                [
'names' => implode(', ', $cantStart)]
            );
        }

        return
$newRecipients;
    }

    public function
insertRecipients(
        \
XF\Entity\ConversationMaster $conversation, array $recipientUsers, \XF\Entity\User $from = null
   
)
    {
       
$existingRecipients = $conversation->Recipients->populate();
       
$insertedActiveUsers = [];
       
$inserted = 0;
       
$fromUserId = $from ? $from->user_id : null;

       
$this->db()->beginTransaction();

       
/** @var \XF\Entity\User $user */
       
foreach ($recipientUsers AS $user)
        {
            if (
$fromUserId && $user->isIgnoring($fromUserId))
            {
               
$state = 'deleted_ignored';
            }
            else
            {
               
$state = 'active';
            }

            if (isset(
$existingRecipients[$user->user_id]))
            {
               
$recipient = $existingRecipients[$user->user_id];

                if (
$recipient->recipient_state != 'deleted')
                {
                   
// keep the current state regardless of the new state unless in an unignored deleted state
                   
continue;
                }
                else if (
$state != 'active')
                {
                   
// if we're in a unignored deleted state, don't allow us to go to anything other than active
                   
continue;
                }
            }
            else
            {
               
$recipient = $conversation->getNewRecipient($user);
            }

            if (
$fromUserId && $user->user_id == $fromUserId)
            {
               
// if inserting by self, that would imply they're creating a conversation, so mark it read
               
$recipient->last_read_date = $conversation->last_message_date;
            }

           
$recipient->recipient_state = $state;

            if (
$recipient->isInsert())
            {
               
$inserted++; // need to update recipient count and cache

               
if ($recipient->recipient_state == 'active')
                {
                   
$insertedActiveUsers[$user->user_id] = $user;
                }
            }

           
$recipient->save(true, false);
        }

        if (
$inserted)
        {
           
$this->rebuildConversationRecipientCache($conversation);
        }

       
$this->db()->commit();

        return
$insertedActiveUsers;
    }

    public function
getConversationRecipientCache(\XF\Entity\ConversationMaster $conversation, &$total = 0)
    {
       
$cache = $this->db()->fetchAllKeyed("
            SELECT recipient.user_id, COALESCE(user.username, '') AS username
            FROM xf_conversation_recipient AS recipient
            LEFT JOIN xf_user AS user ON (recipient.user_id = user.user_id)
            WHERE recipient.conversation_id = ?
            ORDER BY user.username
        "
, 'user_id', $conversation->conversation_id);

       
$total = count($cache);

        unset(
$cache[$conversation->user_id]);

        return
$cache;
    }

    public function
rebuildConversationRecipientCache(\XF\Entity\ConversationMaster $conversation)
    {
       
$cache = $this->getConversationRecipientCache($conversation, $recipientTotal);
       
$conversation->fastUpdate([
           
'recipient_count' => $recipientTotal,
           
'recipients' => $cache
       
]);
       
$conversation->clearCache('Recipients');
       
$conversation->clearCache('Users');

        return
$cache;
    }

    public function
updateRecipientCacheForUserChange($oldUserId, $newUserId, $oldUsername, $newUsername)
    {
       
// note that xf_conversation_recipient must already be updated
       
$oldFind = '"' . intval($oldUserId) . '":' . '{"user_id":' . intval($oldUserId) . ',"username":"' . $oldUsername . '"}';
       
$newReplace = '"' . intval($newUserId) . '":' . '{"user_id":' . intval($newUserId) . ',"username":"' . $newUsername . '"}';

       
$this->db()->query("
            UPDATE (
                SELECT conversation_id
                FROM xf_conversation_recipient
                WHERE user_id = ?
            ) AS temp
            INNER JOIN xf_conversation_master AS master ON (master.conversation_id = temp.conversation_id)
            SET master.recipients = REPLACE(master.recipients, ?, ?)
        "
, [$newUserId, $oldFind, $newReplace]);
    }

    public function
findRecipientsForList(\XF\Entity\ConversationMaster $conversation)
    {
       
$finder = $conversation->getRelationFinder('Recipients');
       
$finder->with('User')->order('User.username');

        return
$finder;
    }
}