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

/**
 * Controller for handling actions on threads.
 *
 * @package XenForo_Thread
 */
class XenForo_ControllerPublic_Thread extends XenForo_ControllerPublic_Abstract
{
    
/**
     * Adds 'forum' to the list of $containerParams if it exists in $params
     */
    
protected function _postDispatch($controllerResponse, $controllerName, $action)
    {
        if (isset(
$controllerResponse->params['forum']))
        {
            
$controllerResponse->containerParams['forum'] = $controllerResponse->params['forum'];
        }
    }

    
/**
     * Displays a thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionIndex()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$threadFetchOptions, $forumFetchOptions) = $this->_getThreadForumFetchOptions();
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

        
$visitor = XenForo_Visitor::getInstance();
        
$threadModel = $this->_getThreadModel();
        
$postModel = $this->_getPostModel();

        if (
$threadModel->isRedirect($thread))
        {
            
$redirect = $this->getModelFromCache('XenForo_Model_ThreadRedirect')->getThreadRedirectById($thread['thread_id']);
            if (!
$redirect)
            {
                return 
$this->responseNoPermission();
            }
            else
            {
                return 
$this->responseRedirect(
                    
XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL_PERMANENT,
                    
$redirect['target_url']
                );
            }
        }

        
$page = max(1, $this->_input->filterSingle('page', XenForo_Input::UINT));
        
$postsPerPage = XenForo_Application::get('options')->messagesPerPage;

        
$this->canonicalizePageNumber($page, $postsPerPage, $thread['reply_count'] + 1, 'threads', $thread);
        
$this->canonicalizeRequestUrl(
            
XenForo_Link::buildPublicLink('threads', $thread, array('page' => $page))
        );

        
$postFetchOptions = $this->_getPostFetchOptions($thread, $forum);
        
$postFetchOptions += array(
            
'perPage' => $postsPerPage,
            
'page' => $page
        
);

        
$posts = $postModel->getPostsInThread($threadId, $postFetchOptions);

        
// TODO: add a sanity check to ensure we got posts (invalid thread if page 1, invalid page otherwise)

        
$posts = $postModel->getAndMergeAttachmentsIntoPosts($posts);

        
$inlineModOptions = array();
        
$maxPostDate = 0;
        
$firstUnreadPostId = 0;

        
$deletedPosts = 0;
        
$moderatedPosts = 0;

        
$pagePosition = 0;

        
$permissions = $visitor->getNodePermissions($thread['node_id']);
        foreach (
$posts AS &$post)
        {
            
$post['position_on_page'] = ++$pagePosition;

            
$postModOptions = $postModel->addInlineModOptionToPost(
                
$post, $thread, $forum, $permissions
            
);
            
$inlineModOptions += $postModOptions;

            
$post = $postModel->preparePost($post, $thread, $forum, $permissions);

            if (
$post['post_date'] > $maxPostDate)
            {
                
$maxPostDate = $post['post_date'];
            }

            if (
$post['isDeleted'])
            {
                
$deletedPosts++;
            }
            if (
$post['isModerated'])
            {
                
$moderatedPosts++;
            }

            if (!
$firstUnreadPostId && $post['isNew'])
            {
                
$firstUnreadPostId = $post['post_id'];
            }
        }

        if (
$firstUnreadPostId)
        {
            
$requestPaths = XenForo_Application::get('requestPaths');
            
$unreadLink = $requestPaths['requestUri'] . '#post-' . $firstUnreadPostId;
        }
        else if (
$thread['isNew'])
        {
            
$unreadLink = XenForo_Link::buildPublicLink('threads/unread', $thread);
        }
        else
        {
            
$unreadLink = '';
        }

        
$attachmentHash = null;
        if (!empty(
$thread['draft_extra']))
        {
            
$draftExtra = @unserialize($thread['draft_extra']);
            if (!empty(
$draftExtra['attachment_hash']))
            {
                
$attachmentHash = $draftExtra['attachment_hash'];
            }
        }

        
$attachmentParams = $this->_getForumModel()->getAttachmentParams($forum, array(
            
'thread_id' => $thread['thread_id']
        ), 
null, null, $attachmentHash);

        if (
$thread['discussion_type'] == 'poll')
        {
            
$pollModel = $this->_getPollModel();
            
$poll = $pollModel->getPollByContent('thread', $threadId);
            if (
$poll)
            {
                
$poll = $pollModel->preparePoll($poll, $threadModel->canVoteOnPoll($thread, $forum));
                
$poll['canEdit'] = $threadModel->canEditPoll($thread, $forum);
            }
        }
        else
        {
            
$poll = false;
        }

        
$threadModel->markThreadRead($thread, $forum, $maxPostDate);
        
$threadModel->logThreadView($threadId);

        
$viewParams = $this->_getDefaultViewParams($forum, $thread, $posts, $page, array(
            
'deletedPosts' => $deletedPosts,
            
'moderatedPosts' => $moderatedPosts,

            
'inlineModOptions' => $inlineModOptions,

            
'firstPost' => reset($posts),
            
'lastPost' => end($posts),
            
'unreadLink' => $unreadLink,

            
'poll' => $poll,

            
'attachmentParams' => $attachmentParams,
            
'attachmentConstraints' => $this->_getAttachmentModel()->getAttachmentConstraints(),

            
'showPostedNotice' => $this->_input->filterSingle('posted', XenForo_Input::UINT),

            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),
        ));

        return 
$this->responseView('XenForo_ViewPublic_Thread_View', 'thread_view', $viewParams);
    }

    protected function 
_getThreadForumFetchOptions()
    {
        
$visitor = XenForo_Visitor::getInstance();

        
$threadFetchOptions = array(
            
'readUserId' => $visitor['user_id'],
            
'watchUserId' => $visitor['user_id'],
            
'draftUserId' => $visitor['user_id'],
            
'join' => XenForo_Model_Thread::FETCH_AVATAR
        
);
        
$forumFetchOptions = array(
            
'readUserId' => $visitor['user_id']
        );

        return array(
$threadFetchOptions, $forumFetchOptions);
    }

    protected function 
_getPostFetchOptions(array $thread, array $forum)
    {
        
$postFetchOptions = $this->_getPostModel()->getPermissionBasedPostFetchOptions($thread, $forum) + array(
            
'join' => XenForo_Model_Post::FETCH_USER | XenForo_Model_Post::FETCH_USER_PROFILE | XenForo_Model_Post::FETCH_BBCODE_CACHE,
            
'likeUserId' => XenForo_Visitor::getUserId()
        );
        if (!empty(
$postFetchOptions['deleted']))
        {
            
$postFetchOptions['join'] |= XenForo_Model_Post::FETCH_DELETION_LOG;
        }

        return 
$postFetchOptions;
    }

    
/**
     * Gets a preview of the first post in a thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionPreview()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$visitor = XenForo_Visitor::getInstance();

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();
        
$postModel = $this->_getPostModel();

        if (
$threadModel->isRedirect($thread))
        {
            return 
$this->responseView('XenForo_ViewPublic_Thread_Preview', '', array('post' => false));
        }

        
$post = $postModel->getPostById($thread['first_post_id'], array(
            
'join' => XenForo_Model_Post::FETCH_USER
        
));
        if (
$post['thread_id'] != $threadId || !$postModel->canViewPost($post, $thread, $forum))
        {
            return 
$this->responseView('XenForo_ViewPublic_Thread_Preview', '', array('post' => false));
        }

        
$viewParams = array(
            
'post' => $post,
            
'thread' => $thread,
            
'forum' => $forum
        
);

        return 
$this->responseView('XenForo_ViewPublic_Thread_Preview', 'thread_list_item_preview', $viewParams);
    }

    
/**
     * Jumps to the first unread post in the thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionUnread()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        
$visitorUserId = XenForo_Visitor::getUserId();
        
$visitor = XenForo_Visitor::getInstance();

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        
$threadFetchOptions = array('readUserId' => $visitorUserId);
        
$forumFetchOptions = array('readUserId' => $visitorUserId);
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

        if (!
$visitorUserId)
        {
            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }

        
$readDate = $this->_getThreadModel()->getMaxThreadReadDate($thread, $forum);
        
$postModel = $this->_getPostModel();

        
$ignoredUserIds = (!empty($visitor['ignored']) ? unserialize($visitor['ignored']) : array());
        
$ignoredUserIds = array_keys($ignoredUserIds);

        
$fetchOptions = $postModel->getPermissionBasedPostFetchOptions($thread, $forum);
        
$firstUnread = $postModel->getNextPostInThread($threadId, $readDate, $fetchOptions, $ignoredUserIds);
        if (!
$firstUnread)
        {
            
$firstUnread = $postModel->getLastPostInThread($threadId, $fetchOptions);
        }

        if (
$firstUnread)
        {
            
$page = floor($firstUnread['position'] / XenForo_Application::get('options')->messagesPerPage) + 1;

            
$hashTag = ($firstUnread['position'] > 0 ? '#post-' . $firstUnread['post_id'] : '');

            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
                
XenForo_Link::buildPublicLink('threads', $thread, array('page' => $page)) . $hashTag
            
);
        }
        else
        {
            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }
    }

    public function 
actionShowPosts()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$visitor = XenForo_Visitor::getInstance();

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        
$threadFetchOptions = array('readUserId' => $visitor['user_id']);
        
$forumFetchOptions = array('readUserId' => $visitor['user_id']);
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

        
$page = $this->_input->filterSingle('page', XenForo_Input::UINT);
        
$postsPerPage = XenForo_Application::get('options')->messagesPerPage;

        
$threadModel = $this->_getThreadModel();
        
$postModel = $this->_getPostModel();

        
$postFetchOptions = $this->_getPostFetchOptions($thread, $forum);
        
$postFetchOptions += array(
            
'perPage' => $postsPerPage,
            
'page' => $page
        
);

        
$postIds = $this->_input->filterSingle('messageIds', array(XenForo_Input::STRING, 'array' => true));
        
$postIds = array_map('intval', preg_replace('/^post-(d+)$/', '1', $postIds));

        if (
$extraPostId = $this->_input->filterSingle('post_id', XenForo_Input::UINT))
        {
            
$postIds[] = $extraPostId;
        }

        
$posts = $postModel->getPostsByIds($postIds, $postFetchOptions);
        
$posts = $postModel->getAndMergeAttachmentsIntoPosts($posts);

        
$inlineModOptions = array();
        
$maxPostDate = 0;

        
$permissions = $visitor->getNodePermissions($thread['node_id']);
        foreach (
$posts AS $key => &$post)
        {
            
// only allow posts from the specified thread to be loaded (permissions reasons)
            
if ($post['thread_id'] != $thread['thread_id'])
            {
                unset(
$posts[$key]);
                continue;
            }

            
$post = $postModel->preparePost($post, $thread, $forum, $permissions);
        }

        if (empty(
$posts))
        {
            return 
$this->responseError(new XenForo_Phrase('no_posts_matching_criteria_specified_were_found'));
        }

        
$viewParams = $this->_getDefaultViewParams($forum, $thread, $posts, $page, array(
            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),
        ));

        return 
$this->responseView('XenForo_ViewPublic_Thread_ViewPosts', '', $viewParams);
    }

    public function 
actionShowNewPosts()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$visitor = XenForo_Visitor::getInstance();

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        
$threadFetchOptions = array('readUserId' => $visitor['user_id']);
        
$forumFetchOptions = array('readUserId' => $visitor['user_id']);
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

        if (!
$this->_request->isXmlHttpRequest())
        {
            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL_PERMANENT,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }

        
$lastDate = $this->_input->filterSingle('last_date', XenForo_Input::UINT);
        
$viewParams = $this->_getNewPosts($thread, $forum, $lastDate, 5);

        return 
$this->responseView(
            
'XenForo_ViewPublic_Thread_ViewNewPosts',
            
'thread_reply_new_posts',
            
$viewParams
        
);
    }

    
/**
     * Displays a form to add a reply to a thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionReply()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$this->_assertCanReplyToThread($thread, $forum);

        
$quickReplyAttachmentHash = $this->_input->filterSingle('attachment_hash', XenForo_Input::STRING);

        
$attachmentParams = $this->_getForumModel()->getAttachmentParams($forum, array(
            
'thread_id' => $thread['thread_id']
        ), 
null, null, $quickReplyAttachmentHash);

        
$attachments = !empty($attachmentParams['attachments']) ? $attachmentParams['attachments'] : array();
        
$defaultMessage = '';
        
$quotePost = null;

        if (
$quoteId = $this->_input->filterSingle('quote', XenForo_Input::UINT))
        {
            
$postModel = $this->_getPostModel();
            
$quotePost = $postModel->getPostById($quoteId, array(
                
'join' => XenForo_Model_Post::FETCH_USER
            
));
            if (
$quotePost && $quotePost['thread_id'] == $threadId && $postModel->canViewPost($quotePost, $thread, $forum))
            {
                
$defaultMessage = $postModel->getQuoteTextForPost($quotePost);
            }
        }
        else if (
$this->_input->inRequest('more_options'))
        {
            
$defaultMessage = $this->getHelper('Editor')->getMessageText('message', $this->_input);
        }

        
$viewParams = array(
            
'post' => $quotePost,
            
'thread' => $thread,
            
'forum' => $forum,
            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

            
'attachmentParams' => $attachmentParams,
            
'attachments' => $attachments,
            
'attachmentConstraints' => $this->_getAttachmentModel()->getAttachmentConstraints(),

            
'defaultMessage' => $defaultMessage,

            
'watchState' => $this->_getThreadWatchModel()->getThreadWatchStateForVisitor($threadId),

            
'captcha' => XenForo_Captcha_Abstract::createDefault(),

            
'canLockUnlockThread' => $this->_getThreadModel()->canLockUnlockThread($thread, $forum),
            
'canStickUnstickThread' => $this->_getThreadModel()->canStickUnstickThread($thread, $forum)
        );

        return 
$this->responseView('XenForo_ViewPublic_Thread_Reply', 'thread_reply', $viewParams);
    }

    
/**
     * Inserts a new reply into an existing thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionAddReply()
    {
        
$this->_assertPostOnly();

        if (
$this->_input->inRequest('more_options'))
        {
            return 
$this->responseReroute(__CLASS__, 'reply');
        }

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$visitor = XenForo_Visitor::getInstance();

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        
$threadFetchOptions = array('readUserId' => $visitor['user_id']);
        
$forumFetchOptions = array('readUserId' => $visitor['user_id']);
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

        
$this->_assertCanReplyToThread($thread, $forum);

        if (!
XenForo_Captcha_Abstract::validateDefault($this->_input))
        {
            return 
$this->responseCaptchaFailed();
        }

        
$input = $this->_input->filter(array(
            
'attachment_hash' => XenForo_Input::STRING,

            
'watch_thread_state' => XenForo_Input::UINT,
            
'watch_thread' => XenForo_Input::UINT,
            
'watch_thread_email' => XenForo_Input::UINT,

            
'_set' => array(XenForo_Input::UINT, 'array' => true),
            
'discussion_open' => XenForo_Input::UINT,
            
'sticky' => XenForo_Input::UINT,
        ));
        
$input['message'] = $this->getHelper('Editor')->getMessageText('message', $this->_input);
        
$input['message'] = XenForo_Helper_String::autoLinkBbCode($input['message']);

        
$writer = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
        
$writer->set('user_id', $visitor['user_id']);
        
$writer->set('username', $visitor['username']);
        
$writer->set('message', $input['message']);
        
$writer->set('message_state', $this->_getPostModel()->getPostInsertMessageState($thread, $forum));
        
$writer->set('thread_id', $threadId);
        
$writer->setExtraData(XenForo_DataWriter_DiscussionMessage::DATA_ATTACHMENT_HASH, $input['attachment_hash']);
        
$writer->setExtraData(XenForo_DataWriter_DiscussionMessage_Post::DATA_FORUM, $forum);
        
$writer->setOption(XenForo_DataWriter_DiscussionMessage_Post::OPTION_MAX_TAGGED_USERS, $visitor->hasPermission('general', 'maxTaggedUsers'));

        
$spamModel = $this->_getSpamPreventionModel();

        if (!
$writer->hasErrors()
            && 
$writer->get('message_state') == 'visible'
            
&& $spamModel->visitorRequiresSpamCheck()
        )
        {
            
$spamExtraParams = array(
                
'permalink' => XenForo_Link::buildPublicLink('canonical:threads', $thread)
            );
            switch (
$spamModel->checkMessageSpam($input['message'], $spamExtraParams, $this->_request))
            {
                case 
XenForo_Model_SpamPrevention::RESULT_MODERATED:
                    
$writer->set('message_state', 'moderated');
                    break;

                case 
XenForo_Model_SpamPrevention::RESULT_DENIED;
                    
$spamModel->logSpamTrigger('post', null);
                    
$writer->error(new XenForo_Phrase('your_content_cannot_be_submitted_try_later'));
                    break;
            }
        }

        
$writer->preSave();

        if (!
$writer->hasErrors())
        {
            
$this->assertNotFlooding('post');
        }

        
$writer->save();
        
$post = $writer->getMergedData();

        
$spamModel->logContentSpamCheck('post', $post['post_id']);
        
$spamModel->logSpamTrigger('post', $post['post_id']);
        
$this->_getDraftModel()->deleteDraft('thread-' . $thread['thread_id']);

        
$this->_getThreadWatchModel()->setVisitorThreadWatchStateFromInput($threadId, $input);

        
$threadUpdateData = array();

        if (!empty(
$input['_set']['discussion_open']) && $this->_getThreadModel()->canLockUnlockThread($thread, $forum))
        {
            if (
$thread['discussion_open'] != $input['discussion_open'])
            {
                
$threadUpdateData['discussion_open'] = $input['discussion_open'];
            }
        }

        
// discussion sticky state - moderator permission required
        
if (!empty($input['_set']['sticky']) && $this->_getForumModel()->canStickUnstickThreadInForum($forum))
        {
            if (
$thread['sticky'] != $input['sticky'])
            {
                
$threadUpdateData['sticky'] = $input['sticky'];
            }
        }

        if (
$threadUpdateData)
        {
            
$threadWriter = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
            
$threadWriter->setExistingData($thread['thread_id']);
            
$threadWriter->bulkSet($threadUpdateData);
            
$threadWriter->setExtraData(XenForo_DataWriter_Discussion_Thread::DATA_FORUM, $forum);
            
$threadWriter->save();
        }

        
$canViewPost = $this->_getPostModel()->canViewPost($post, $thread, $forum);

        
$page = floor(($thread['reply_count'] + 1) / XenForo_Application::get('options')->messagesPerPage) + 1;

        
// this is a standard redirect
        
if (!$this->_noRedirect() || !$this->_input->inRequest('last_date') || !$canViewPost)
        {
            
$this->_getThreadModel()->markThreadRead($thread, $forum, XenForo_Application::$time);

            if (!
$canViewPost)
            {
                
$return = XenForo_Link::buildPublicLink('threads', $thread, array('page' => $page, 'posted' => 1));
            }
            else
            {
                
$return = XenForo_Link::buildPublicLink('posts', $post);
            }

            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::SUCCESS,
                
$return,
                new 
XenForo_Phrase('your_message_has_been_posted')
            );
        }
        else
        {
            
// load a selection of posts that are newer than the last post viewed
            
$lastDate = $this->_input->filterSingle('last_date', XenForo_Input::UINT);
            
$viewParams = $this->_getNewPosts($thread, $forum, $lastDate, 3);

            return 
$this->responseView(
                
'XenForo_ViewPublic_Thread_ViewNewPosts',
                
'thread_reply_new_posts',
                
$viewParams
            
);
        }
    }

    protected function 
_getNewPosts(array $thread, array $forum, $lastDate, $limit = 3)
    {
        
$postModel = $this->_getPostModel();
        
$visitor = XenForo_Visitor::getInstance();

        
$postFetchOptions = $this->_getPostFetchOptions($thread, $forum);
        
$postFetchOptions += array(
            
'limit' => ($limit + 1),
        );

        
$posts = $postModel->getNewestPostsInThreadAfterDate(
            
$thread['thread_id'], $lastDate, $postFetchOptions
        
);

        
// We fetched one more post than needed, if more than $limit posts were returned,
        // we can show the 'there are more posts' notice
        
if (count($posts) > $limit)
        {
            
$postPermissionOptions = $postModel->getPermissionBasedPostFetchOptions($thread, $forum);
            
$firstUnshownPost = $postModel->getNextPostInThread($thread['thread_id'], $lastDate, $postPermissionOptions);

            
// remove the extra post
            
array_pop($posts);
        }
        else
        {
            
$firstUnshownPost = false;
        }

        
// put the posts into oldest-first order
        
$posts = array_reverse($posts, true);

        
$posts = $postModel->getAndMergeAttachmentsIntoPosts($posts);

        
$permissions = $visitor->getNodePermissions($thread['node_id']);

        foreach (
$posts AS &$post)
        {
            
$post = $postModel->preparePost($post, $thread, $forum, $permissions);
        }

        
// mark thread as read if we're showing the remaining posts in it or they've been read
        
if ($visitor['user_id'])
        {
            if (!
$firstUnshownPost || $firstUnshownPost['post_date'] <= $thread['thread_read_date'])
            {
                
$this->_getThreadModel()->markThreadRead($thread, $forum, XenForo_Application::$time);
            }
        }

        
$page = floor(($thread['reply_count'] + 1) / XenForo_Application::get('options')->messagesPerPage) + 1;

        return 
$this->_getDefaultViewParams($forum, $thread, $posts, $page, array(
            
'firstUnshownPost' => $firstUnshownPost,
            
'lastPost' => end($posts),
        ));
    }

    
/**
     * Shows a preview of the reply.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionReplyPreview()
    {
        
$this->_assertPostOnly();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$this->_assertCanReplyToThread($thread, $forum);

        
$message = $this->getHelper('Editor')->getMessageText('message', $this->_input);
        
$message = XenForo_Helper_String::autoLinkBbCode($message);

        
/** @var $taggingModel XenForo_Model_UserTagging */
        
$taggingModel = $this->getModelFromCache('XenForo_Model_UserTagging');
        
$taggingModel->getTaggedUsersInMessage($message, $message);

        
$viewParams = array(
            
'thread' => $thread,
            
'forum' => $forum,
            
'message' => $message
        
);

        return 
$this->responseView('XenForo_ViewPublic_Thread_ReplyPreview', 'thread_reply_preview', $viewParams);
    }

    public function 
actionSaveDraft()
    {
        
$this->_assertPostOnly();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$this->_assertCanReplyToThread($thread, $forum);

        
$extra = array(
            
'attachment_hash' => $this->_input->filterSingle('attachment_hash', XenForo_Input::STRING)
        );
        
$message = $this->getHelper('Editor')->getMessageText('message', $this->_input);
        
$forceDelete = $this->_input->filterSingle('delete_draft', XenForo_Input::BOOLEAN);

        if (!
strlen($message) || $forceDelete)
        {
            
$draftSaved = false;
            
$draftDeleted = $this->_getDraftModel()->deleteDraft("thread-$thread[thread_id]") || $forceDelete;
        }
        else
        {
            
$this->_getDraftModel()->saveDraft("thread-$thread[thread_id]", $message, $extra);
            
$draftSaved = true;
            
$draftDeleted = false;
        }

        
$lastDate = $this->_input->filterSingle('last_date', XenForo_Input::UINT);
        
$lastKnownDate = $this->_input->filterSingle('last_known_date', XenForo_Input::UINT);
        
$lastKnownDate = max($lastDate, $lastKnownDate);

        if (
$lastDate)
        {
            
$newPostCount = count($this->_getPostModel()->getNewestPostsInThreadAfterDate($threadId, $lastKnownDate));
        }
        else
        {
            
$newPostCount = 0;
        }

        
$viewParams = array(
            
'thread' => $thread,
            
'newPostCount' => $newPostCount,
            
'lastDate' => $lastDate,
            
'draftSaved' => $draftSaved,
            
'draftDeleted' => $draftDeleted
        
);
        return 
$this->responseView('XenForo_ViewPublic_Thread_SaveDraft', 'thread_save_draft', $viewParams);
    }

    
/**
     * Displays a form to edit a thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionEdit()
    {
        
$this->_assertRegistrationRequired();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$this->_assertCanEditThread($thread, $forum);

        
$threadModel = $this->_getThreadModel();

        
$viewParams = array(
            
'thread' => $thread,
            
'forum' => $forum,

            
'prefixes' => $this->_getPrefixModel()->getUsablePrefixesInForums($forum['node_id']),

            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

            
'canLockUnlockThread' => $threadModel->canLockUnlockThread($thread, $forum),
            
'canStickUnstickThread' => $threadModel->canStickUnstickThread($thread, $forum),

            
'canDeleteThread' => $threadModel->canDeleteThread($thread, $forum, 'soft'),
            
'canHardDeleteThread' => $threadModel->canDeleteThread($thread, $forum, 'hard'),

            
'canAlterState' => array(
                
'visible' => $threadModel->canAlterThreadState($thread, $forum, 'visible'),
                
'moderated' => $threadModel->canAlterThreadState($thread, $forum, 'moderated'),
                
'deleted' => $threadModel->canAlterThreadState($thread, $forum, 'deleted'),
            ),

            
'showForumLink' => $this->_input->filterSingle('showForumLink', XenForo_Input::BOOLEAN)
        );

        if (
$this->_input->filterSingle('_listItemEdit', XenForo_Input::UINT))
        {
            return 
$this->responseView('XenForo_ViewPublic_Thread_ListItemEdit', 'thread_list_item_edit', $viewParams);
        }
        else
        {
            return 
$this->responseView('XenForo_ViewPublic_Thread_Edit', 'thread_edit', $viewParams);
        }
    }

    
/**
     * Displays a form to edit a thread title.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionEditTitle()
    {
        
$this->_assertRegistrationRequired();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();

        if (!
$threadModel->canEditThreadTitle($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }

        if (
$this->isConfirmedPost())
        {
            
$dwInput = $this->_input->filter(array(
                
'title' => XenForo_Input::STRING,
                
'prefix_id' => XenForo_Input::UINT
            
));

            if (!
$this->_getPrefixModel()->verifyPrefixIsUsable($dwInput['prefix_id'], $forum['node_id']))
            {
                
$dwInput['prefix_id'] = 0; // not usable, just blank it out
            
}

            
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
            
$dw->setExistingData($threadId);
            
$dw->bulkSet($dwInput);
            
$dw->setExtraData(XenForo_DataWriter_Discussion_Thread::DATA_FORUM, $forum);
            
$dw->preSave();

            if (
$forum['require_prefix'] && !$dw->get('prefix_id'))
            {
                
$dw->error(new XenForo_Phrase('please_select_a_prefix'), 'prefix_id');
            }

            
$dw->save();

            
$this->_updateModeratorLogThreadEdit($thread, $dw);
            
$thread = $dw->getMergedData();

            
// regular redirect
            
return $this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::SUCCESS,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }
        else
        {
            
$viewParams = array(
                
'thread' => $thread,
                
'forum' => $forum,

                
'prefixes' => $this->_getPrefixModel()->getUsablePrefixesInForums($forum['node_id']),

                
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),
            );

            return 
$this->responseView('XenForo_ViewPublic_Thread_EditTitle', 'thread_edit_title', $viewParams);
        }
    }

    
/**
     * Alternative route into actionEdit - adding a _listItemEdit parameter
     *
     * @return XenForo_ControllerResponse_Reroute
     */
    
public function actionListItemEdit()
    {
        
$this->_request->setParam('_listItemEdit', true);

        return 
$this->responseReroute('XenForo_ControllerPublic_Thread', 'edit');
    }

    
/**
     * Updates an existing thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionSave()
    {
        
$this->_assertPostOnly();
        
$this->_assertRegistrationRequired();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$this->_assertCanEditThread($thread, $forum);

        
$dwInput = $this->_input->filter(array(
            
'title' => XenForo_Input::STRING,
            
'prefix_id' => XenForo_Input::UINT,
            
'discussion_state' => XenForo_Input::STRING,
            
'discussion_open' => XenForo_Input::UINT,
            
'sticky' => XenForo_Input::UINT
        
));

        
$threadModel = $this->_getThreadModel();

        if (!
$threadModel->canLockUnlockThread($thread, $forum))
        {
            unset(
$dwInput['discussion_open']);
        }

        if (!
$threadModel->canStickUnstickThread($thread, $forum))
        {
            unset(
$dwInput['sticky']);
        }

        if (!
$threadModel->canAlterThreadState($thread, $forum, $dwInput['discussion_state']))
        {
            unset(
$dwInput['discussion_state']);
        }

        if (!
$this->_getPrefixModel()->verifyPrefixIsUsable($dwInput['prefix_id'], $forum['node_id']))
        {
            
$dwInput['prefix_id'] = 0; // not usable, just blank it out
        
}

        
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
        
$dw->setExistingData($threadId);
        
$dw->bulkSet($dwInput);
        
$dw->setExtraData(XenForo_DataWriter_Discussion_Thread::DATA_FORUM, $forum);
        
$dw->save();

        
$this->_updateModeratorLogThreadEdit($thread, $dw);

        
// special case for the discussion list inline editor
        
if ($this->_input->filterSingle('_returnDiscussionListItem', XenForo_Input::UINT))
        {
            
$visitorUserId = XenForo_Visitor::getUserId();

            
$threadFetchOptions = array(
                
'readUserId' => $visitorUserId,
                
'postCountUserId' => $visitorUserId,
                
'watchUserId' => $visitorUserId,
                
'join' => XenForo_Model_Thread::FETCH_USER | XenForo_Model_Thread::FETCH_DELETION_LOG
            
);
            
$forumFetchOptions = array(
                
'readUserId' => $visitorUserId
            
);

            list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);

            
$thread['forum'] = $forum;

            
$viewParams = array(
                
'thread' => $thread,
                
'forum' => $forum,
                
'showForumLink' => $this->_input->filterSingle('showForumLink', XenForo_Input::BOOLEAN)
            );

            return 
$this->responseView('XenForo_ViewPublic_Thread_Save_ThreadListItem', 'thread_list_item', $viewParams);
        }

        
$thread = $dw->getMergedData();

        
// regular redirect
        
return $this->responseRedirect(
            
XenForo_ControllerResponse_Redirect::SUCCESS,
            
XenForo_Link::buildPublicLink('threads', $thread)
        );
    }

    
/**
     * Logs the moderator actions for thread edits.
     *
     * @param array $thread
     * @param XenForo_DataWriter_Discussion_Thread $dw
     * @param array $skip Array of keys to skip logging for
     */
    
protected function _updateModeratorLogThreadEdit(array $thread, XenForo_DataWriter_Discussion_Thread $dw, array $skip = array())
    {
        
$newData = $dw->getMergedNewData();
        if (
$newData)
        {
            
$oldData = $dw->getMergedExistingData();
            
$basicLog = array();

            foreach (
$newData AS $key => $value)
            {
                
$oldValue = (isset($oldData[$key]) ? $oldData[$key] : '-');
                switch (
$key)
                {
                    case 
'sticky':
                        
XenForo_Model_Log::logModeratorAction('thread', $thread, ($value ? 'stick' : 'unstick'));
                        break;

                    case 
'discussion_open':
                        
XenForo_Model_Log::logModeratorAction('thread', $thread, ($value ? 'unlock' : 'lock'));
                        break;

                    case 
'discussion_state':
                        if (
$value == 'visible' && $oldValue == 'moderated')
                        {
                            
XenForo_Model_Log::logModeratorAction('thread', $thread, 'approve');
                        }
                        else if (
$value == 'visible' && $oldValue == 'deleted')
                        {
                            
XenForo_Model_Log::logModeratorAction('thread', $thread, 'undelete');
                        }
                        else if (
$value == 'deleted')
                        {
                            
XenForo_Model_Log::logModeratorAction(
                                
'thread', $thread, 'delete_soft', array('reason' => '')
                            );
                        }
                        else if (
$value == 'moderated')
                        {
                            
XenForo_Model_Log::logModeratorAction('thread', $thread, 'unapprove');
                        }
                        break;

                    case 
'title':
                        
XenForo_Model_Log::logModeratorAction(
                            
'thread', $thread, 'title', array('old' => $oldValue)
                        );
                        break;

                    case 
'prefix_id':
                        if (
$oldValue)
                        {
                            
$phrase = new XenForo_Phrase('thread_prefix_' . $oldValue);
                            
$oldValue = $phrase->render();
                        }
                        else
                        {
                            
$oldValue = '-';
                        }
                        
XenForo_Model_Log::logModeratorAction(
                            
'thread', $thread, 'prefix', array('old' => $oldValue)
                        );
                        break;

                    default:
                        if (!
in_array($key, $skip))
                        {
                            
$basicLog[$key] = $oldValue;
                        }
                }
            }

            if (
$basicLog)
            {
                
XenForo_Model_Log::logModeratorAction('thread', $thread, 'edit', $basicLog);
            }
        }
    }

    public function 
actionQuickUpdate()
    {
        
$this->_assertPostOnly();
        
$this->_assertRegistrationRequired();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();

        
$input = $this->_input->filter(array(
            
'discussion_open' => XenForo_Input::UINT,
            
'sticky' => XenForo_Input::UINT,
        ));

        
$set = $this->_input->filterSingle('set', XenForo_Input::ARRAY_SIMPLE, array('array' => true));

        
$dwInput = array();

        if (isset(
$set['discussion_open']) && $threadModel->canLockUnlockThread($thread, $forum))
        {
            
$dwInput['discussion_open'] = $input['discussion_open'];
        }

        if (isset(
$set['sticky']) && $threadModel->canStickUnstickThread($thread, $forum))
        {
            
$dwInput['sticky'] = $input['sticky'];
        }

        if (
$dwInput)
        {
            
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
            
$dw->setExistingData($threadId);
            
$dw->bulkSet($dwInput);
            
$dw->setExtraData(XenForo_DataWriter_Discussion_Thread::DATA_FORUM, $forum);
            
$dw->save();

            
$this->_updateModeratorLogThreadEdit($thread, $dw);
        }

        return 
$this->responseRedirect(
            
XenForo_ControllerResponse_Redirect::SUCCESS,
            
XenForo_Link::buildPublicLink('threads', $thread)
        );
    }

    
/**
     * Deletes an existing thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionDelete()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();

        
$hardDelete = $this->_input->filterSingle('hard_delete', XenForo_Input::UINT);
        
$deleteType = ($hardDelete ? 'hard' : 'soft');

        
$this->_assertCanDeleteThread($thread, $forum, $deleteType);

        if (
$this->isConfirmedPost()) // delete the thread
        
{
            
$options = array(
                
'reason' => $this->_input->filterSingle('reason', XenForo_Input::STRING)
            );

            
$threadModel->deleteThread($threadId, $deleteType, $options);

            
XenForo_Model_Log::logModeratorAction(
                
'thread', $thread, 'delete_' . $deleteType, array('reason' => $options['reason'])
            );

            
XenForo_Helper_Cookie::clearIdFromCookie($threadId, 'inlinemod_threads');

            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::SUCCESS,
                
XenForo_Link::buildPublicLink('forums', $forum)
            );
        }
        else 
// show a delete confirmation dialog
        
{
            return 
$this->responseView(
                
'XenForo_ViewPublic_Thread_Delete',
                
'thread_delete',
                array(
                    
'thread' => $thread,
                    
'forum' => $forum,
                    
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

                    
'canHardDelete' => $threadModel->canDeleteThread($thread, $forum, 'hard'),
                )
            );
        }
    }

    public function 
actionModeratorActions()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();

        if (!
$threadModel->canMoveThread($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }

        
/** @var XenForo_Model_Log $logModel */
        
$logModel = $this->getModelFromCache('XenForo_Model_Log');

        
$logs = $logModel->getModeratorLogForDiscussionContent('thread', $threadId);

        
$viewParams = array(
            
'thread' => $thread,
            
'forum' => $forum,
            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

            
'logs' => $logModel->prepareModeratorLogEntries($logs)
        );
        return 
$this->responseView('XenForo_ViewPublic_Thread_ModeratorActions', 'thread_moderator_actions', $viewParams);
    }

    
/**
     * Moves a thread to a different forum.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionMove()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$threadModel = $this->_getThreadModel();

        if (!
$threadModel->canMoveThread($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }

        if (
$this->isConfirmedPost()) // move the thread
        
{
            
$input = $this->_input->filter(array(
                
'node_id' => XenForo_Input::UINT,
                
'title' => XenForo_Input::STRING,
                
'prefix_id' => XenForo_Input::UINT,

                
'create_redirect' => XenForo_Input::STRING,
                
'redirect_ttl_value' => XenForo_Input::UINT,
                
'redirect_ttl_unit' => XenForo_Input::STRING,

                
'send_alert' => XenForo_Input::BOOLEAN
            
));
            
$inputTitle = $this->_input->filterSingle('title', XenForo_Input::STRING);

            
$viewableNodes = $this->getModelFromCache('XenForo_Model_Node')->getViewableNodeList();
            if (isset(
$viewableNodes[$input['node_id']]))
            {
                
$targetNode = $viewableNodes[$input['node_id']];
            }
            else
            {
                return 
$this->responseNoPermission();
            }

            if (
$input['create_redirect'] == 'permanent')
            {
                
$options = array('redirect' => true, 'redirectExpiry' => 0);
            }
            else if (
$input['create_redirect'] == 'expiring')
            {
                
$expiryDate = strtotime('+' . $input['redirect_ttl_value'] . ' ' . $input['redirect_ttl_unit']);
                
$options = array('redirect' => true, 'redirectExpiry' => $expiryDate);
            }
            else
            {
                
$options = array('redirect' => false);
            }

            
$dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
            
$dw->setExistingData($threadId);
            
$dw->set('node_id', $input['node_id']);
            if (
$this->_getThreadModel()->canEditThread($thread, $forum) && $input['title'] !== '')
            {
                if (!
$this->_getPrefixModel()->verifyPrefixIsUsable($input['prefix_id'], $input['node_id']))
                {
                    
$input['prefix_id'] = 0; // not usable, just blank it out
                
}

                
$dw->set('title', $input['title']);
                
$dw->set('prefix_id', $input['prefix_id']);
            }
            
$dw->save();

            
XenForo_Model_Log::logModeratorAction('thread', $thread, 'move', array('from' => $forum['title']));
            
$this->_updateModeratorLogThreadEdit($thread, $dw, array('node_id'));

            if (
$dw->isChanged('node_id'))
            {
                if (
$options['redirect'])
                {
                    
$this->getModelFromCache('XenForo_Model_ThreadRedirect')->createRedirectThread(
                        
XenForo_Link::buildPublicLink('threads', $thread), $thread,
                        
"thread-$thread[thread_id]-$thread[node_id]-", $options['redirectExpiry']
                    );
                }

                if (
$input['send_alert'])
                {
                    
// send watched forum alerts/emails
                    
$firstPost = $this->_getPostModel()->getPostById($thread['first_post_id']);
                    if (
$firstPost && $firstPost['thread_id'] == $thread['thread_id'])
                    {
                        
$this->getModelFromCache('XenForo_Model_ForumWatch')->sendNotificationToWatchUsersOnMove(
                            
$firstPost, $forum['node_id']
                        );
                    }
                }
            }

            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::SUCCESS,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }
        else
        {
            return 
$this->responseView(
                
'XenForo_ViewPublic_Thread_Move',
                
'thread_move',
                array(
                    
'thread' => $thread,
                    
'forum' => $forum,
                    
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

                    
// we're showing the current forum prefixes intentionally; it's the best option right now
                    
'prefixes' => $this->_getPrefixModel()->getUsablePrefixesInForums($forum['node_id']),
                    
'forcePrefixes' => (XenForo_Application::get('threadPrefixes') ? true : false),

                    
'firstThread' => $thread,
                    
'nodeOptions' => $this->getModelFromCache('XenForo_Model_Node')->getViewableNodeList(),

                    
'canEditTitle' => $this->_getThreadModel()->canEditThread($thread, $forum)
                )
            );
        }
    }

    
/**
     * Displays a confirmation of watching (or stopping the watch of) a thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionWatchConfirm()
    {
        
$ftpHelper = $this->getHelper('ForumThreadPost');

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        if (!
$this->_getThreadModel()->canWatchThread($thread, $forum))
        {
            return 
$this->responseNoPermission();
        }

        
$threadWatch = $this->getModelFromCache('XenForo_Model_ThreadWatch')->getUserThreadWatchByThreadId(
            
XenForo_Visitor::getUserId(), $thread['thread_id']
        );

        
$viewParams = array(
            
'thread' => $thread,
            
'forum' => $forum,
            
'threadWatch' => $threadWatch,
            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),
        );

        return 
$this->responseView('XenForo_ViewPublic_Thread_Watch', 'thread_watch', $viewParams);
    }

    
/**
     * Inserts/updates/deletes a thread watch.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionWatch()
    {
        
$this->_assertPostOnly();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        list(
$thread, $forum) = $this->getHelper('ForumThreadPost')->assertThreadValidAndViewable($threadId);

        if (!
$this->_getThreadModel()->canWatchThread($thread, $forum))
        {
            return 
$this->responseNoPermission();
        }

        if (
$this->_input->filterSingle('stop', XenForo_Input::STRING))
        {
            
$newState = '';
        }
        else if (
$this->_input->filterSingle('email_subscribe', XenForo_Input::UINT))
        {
            
$newState = 'watch_email';
        }
        else
        {
            
$newState = 'watch_no_email';
        }

        
$this->_getThreadWatchModel()->setThreadWatchState(XenForo_Visitor::getUserId(), $threadId, $newState);

        return 
$this->responseRedirect(
            
XenForo_ControllerResponse_Redirect::SUCCESS,
            
XenForo_Link::buildPublicLink('threads', $thread),
            
null,
            array(
'linkPhrase' => ($newState ? new XenForo_Phrase('unwatch_thread') : new XenForo_Phrase('watch_thread')))
        );
    }

    
/**
     * Votes on the poll in this thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionPollVote()
    {
        
$this->_assertPostOnly();

        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$pollModel = $this->_getPollModel();
        
$poll = $pollModel->getPollByContent('thread', $threadId);
        if (!
$poll || !$this->_getThreadModel()->canVoteOnPoll($thread, $forum))
        {
            return 
$this->responseNoPermission();
        }

        if (!
$pollModel->canVoteOnPoll($poll, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }

        if (
$poll['multiple'])
        {
            
$responses = $this->_input->filterSingle('response_multiple', XenForo_Input::UINT, array('array' => true));
        }
        else
        {
            
$responses = $this->_input->filterSingle('response', XenForo_Input::UINT);
        }

        if (!
$responses)
        {
            return 
$this->responseError(new XenForo_Phrase('please_vote_for_at_least_one_option'));
        }

        
$pollModel->voteOnPoll($poll['poll_id'], $responses);

        if (
$this->_noRedirect())
        {
            return 
$this->responseReroute(__CLASS__, 'pollResults');
        }

        return 
$this->responseRedirect(
            
XenForo_ControllerResponse_Redirect::SUCCESS,
            
XenForo_Link::buildPublicLink('threads', $thread)
        );
    }

    
/**
     * Views the results of the poll in this thread. Also doubles as viewing voters
     * for a particular response.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionPollResults()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$pollModel = $this->_getPollModel();
        
$poll = $pollModel->getPollByContent('thread', $threadId);
        if (!
$poll)
        {
            return 
$this->responseNoPermission();
        }

        
$poll = $pollModel->preparePoll($poll, false);
        
$poll['canEdit'] = $this->_getThreadModel()->canEditPoll($thread, $forum);

        
$pollResponseId = $this->_input->filterSingle('poll_response_id', XenForo_Input::UINT);
        if (
$pollResponseId)
        {
            if (!isset(
$poll['responses'][$pollResponseId]) || !$poll['public_votes'])
            {
                return 
$this->responseNoPermission();
            }
            else
            {
                
$viewParams = array(
                    
'forum' => $forum,
                    
'thread' => $thread,
                    
'poll' => $poll,
                    
'response' => $poll['responses'][$pollResponseId],
                    
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),
                );

                return 
$this->responseView('XenForo_ViewPublic_Thread_PollVoters', 'thread_poll_voters', $viewParams);
            }
        }

        
$viewParams = array(
            
'thread' => $thread,
            
'forum' => $forum,
            
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

            
'poll' => $poll
        
);

        return 
$this->responseView('XenForo_ViewPublic_Thread_PollResults', 'thread_poll_results', $viewParams);
    }

    
/**
     * Edits the poll in this thread.
     *
     * @return XenForo_ControllerResponse_Abstract
     */
    
public function actionPollEdit()
    {
        
$threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);

        
$ftpHelper = $this->getHelper('ForumThreadPost');
        list(
$thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId);

        
$pollModel = $this->_getPollModel();
        
$poll = $pollModel->getPollByContent('thread', $threadId);
        if (!
$poll)
        {
            return 
$this->responseNoPermission();
        }

        if (!
$this->_getThreadModel()->canEditPoll($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }

        
$canEditMultiple = (!$poll['multiple'] || !$poll['voter_count']);
        
$canEditPublic = ($poll['public_votes'] || !$poll['voter_count']);
        
$canDisableCloseDate = ($poll['close_date'] ? true : false);

        if (
$this->_request->isPost())
        {
            
$input = $this->_input->filter(array(
                
'question' => XenForo_Input::STRING,
                
'existing_responses' => array(XenForo_Input::STRING, 'array' => true),
                
'new_responses' => array(XenForo_Input::STRING, 'array' => true),
                
'multiple' => XenForo_Input::UINT,
                
'public_votes' => XenForo_Input::UINT,
                
'close_date' => XenForo_Input::UINT,

                
'close' => XenForo_Input::UINT,
                
'close_length' => XenForo_Input::UINT,
                
'close_units' => XenForo_Input::STRING
            
));

            
$responses = $pollModel->getPollResponsesInPoll($poll['poll_id']);

            
XenForo_Db::beginTransaction();

            
$pollDw = XenForo_DataWriter::create('XenForo_DataWriter_Poll');
            
$pollDw->setExistingData($poll);
            
$pollDw->set('question', $input['question']);
            if (
$canEditMultiple)
            {
                
$pollDw->set('multiple', $input['multiple']);
            }
            if (
$canEditPublic)
            {
                
$pollDw->set('public_votes', $input['public_votes']);
            }
            if (
$canDisableCloseDate)
            {
                
$pollDw->set('close_date', $input['close_date']);
            }
            else if (
$input['close'])
            {
                
$pollDw->set('close_date', $pollDw->preVerifyCloseDate(strtotime('+' . $input['close_length'] . ' ' . $input['close_units'])));
            }
            else
            {
                
$pollDw->set('close_date', 0);
            }

            
$deleteCount = 0;
            foreach (
$responses AS $response)
            {
                if (!isset(
$input['existing_responses'][$response['poll_response_id']]))
                {
                    continue;
                }

                
$updateText = $input['existing_responses'][$response['poll_response_id']];

                
$responseDw = XenForo_DataWriter::create('XenForo_DataWriter_PollResponse');
                
$responseDw->setExistingData($response, true);
                if (
$updateText === '')
                {
                    
$responseDw->delete();
                    
$deleteCount++;
                }
                else
                {
                    
$responseDw->set('response', $updateText);
                    
$responseDw->save();
                }
            }

            
$pollDw->addResponses($input['new_responses']);
            if (
$deleteCount == count($responses) && !$pollDw->hasNewResponses())
            {
                
$pollDw->delete();
            }
            else
            {
                
$pollDw->save();
            }

            
XenForo_Model_Log::logModeratorAction('thread', $thread, 'poll_edit');

            
XenForo_Db::commit();

            return 
$this->responseRedirect(
                
XenForo_ControllerResponse_Redirect::SUCCESS,
                
XenForo_Link::buildPublicLink('threads', $thread)
            );
        }
        else
        {
            
$viewParams = array(
                
'thread' => $thread,
                
'forum' => $forum,
                
'nodeBreadCrumbs' => $ftpHelper->getNodeBreadCrumbs($forum),

                
'poll' => $pollModel->preparePoll($poll, false),
                
'canEditMultiple' => $canEditMultiple,
                
'canEditPublic' => $canEditPublic,
                
'canDisableCloseDate' => $canDisableCloseDate
            
);

            return 
$this->responseView('XenForo_ViewPublic_Thread_PollEdit', 'thread_poll_edit', $viewParams);
        }
    }

    public function 
actionMultiQuote()
    {
        
$input = $this->_input->filter(array(
            
'formId' => XenForo_Input::STRING,
            
'postIds' => array(XenForo_Input::UINT, 'array' => true),
        ));

        if (
$this->_request->has('postIds'))
        {
            
$mode = 'quote';
            
$postIds = $input['postIds'];
        }
        else
        {
            
$mode = 'overlay';
            
$cookieValue = XenForo_Helper_Cookie::getCookie('MultiQuote');
            
$postIds = explode(',', $cookieValue);
        }

        if (empty(
$postIds))
        {
            return 
$this->responseError(new XenForo_Phrase('no_messages_selected'));
        }

        
$visitor = XenForo_Visitor::getInstance();
        
$threadModel = $this->_getThreadModel();
        
$postModel = $this->_getPostModel();

        
$posts = $postModel->getPostsByIds($postIds, array
            (
                
'join' =>
                    
XenForo_Model_Post::FETCH_USER |
                    
XenForo_Model_Post::FETCH_THREAD |
                    
XenForo_Model_Post::FETCH_FORUM,
                
'permissionCombinationId' => $visitor['permission_combination_id']
            )
        );

        
// permission filter
        
foreach ($posts AS $postId => $post)
        {
            
$post['permissions'] = XenForo_Permission::unserializePermissions($post['node_permission_cache']);

            if (!
$postModel->canViewPostAndContainer($post, $post, $post, $null, $post['permissions']))
            {
                unset(
$posts[$postId]);
            }
        }

        
$viewParams = array(
            
'formId' => $input['formId'],
        );

        if (
$mode == 'overlay')
        {
            
$datedPosts = array();
            foreach (
$posts AS $postId => $post)
            {
                
$datedPosts[$post['post_date']][$postId] = $post;
            }

            
ksort($datedPosts);

            
$posts = array();

            foreach (
$datedPosts AS $postDate => $postIds)
            {
                foreach (
$postIds AS $postId => $post)
                {
                    
$posts[$postId] = $post;
                }
            }

            if (empty(
$posts))
            {
                
// clear the cookie, as its value is invalid or unusable
                
XenForo_Helper_Cookie::deleteCookie('MultiQuote');

                return 
$this->responseError(new XenForo_Phrase('selected_messages_not_found'));
            }

            
$viewParams['posts'] = $posts;

            return 
$this->responseView('XenForo_ViewPublic_Thread_MultiQuoteOverlay', 'thread_multi_quote_overlay', $viewParams);
        }
        else
        {
            
$orderedPosts = array();

            foreach (
$postIds AS $postId)
            {
                if (isset(
$posts[$postId]))
                {
                    
$orderedPosts[$postId] = $posts[$postId];
                }
            }

            
$viewParams['posts'] = $orderedPosts;

            
$quotes = '';
            foreach (
$orderedPosts AS $post)
            {
                
$quotes .= trim($postModel->getQuoteTextForPost($post)) . "nn";
            }

            
$viewParams['quote'] = trim($quotes);

            return 
$this->responseView('XenForo_ViewPublic_Thread_MultiQuote', '', $viewParams);
        }

    }

    
/**
     * Fetches the default set of view parameters required to view posts in a thread.
     *
     * @param array $forum The forums that contains the posts.
     * @param array $thread The thread that contains the posts.
     * @param array $posts Array of individual posts to be displayed.
     * @param integer $page The current page number.
     * @param array $viewParams Optional array of additional view parameters.
     *
     * @return array
     */
    
protected function _getDefaultViewParams(array $forum, array $thread, array $posts, $page = 1, array $viewParams = array())
    {
        
$threadModel = $this->_getThreadModel();

        
$page = max(1, $page);
        
$postsPerPage = XenForo_Application::get('options')->messagesPerPage;

        return array(
            
'thread' => $thread,
            
'forum' => $forum,
            
'posts' => $posts,

            
'ignoredNames' => $this->_getIgnoredContentUserNames($posts),

            
'page' => $page,
            
'postsPerPage' => $postsPerPage,
            
'totalPosts' => $thread['reply_count'] + 1,
            
'postsRemaining' => max(0, $thread['reply_count'] + 1 - ($page * $postsPerPage)),

            
'canReply' => $threadModel->canReplyToThread($thread, $forum),
            
'canQuickReply' => $threadModel->canQuickReply($thread, $forum),
            
'canEditThread' => $threadModel->canEditThread($thread, $forum),
            
'canEditTitle' => $threadModel->canEditThreadTitle($thread, $forum),
            
'canDeleteThread' => $threadModel->canDeleteThread($thread, $forum, 'soft'),
            
'canMoveThread' => $threadModel->canMoveThread($thread, $forum),
            
'canStickUnstickThread' => $threadModel->canStickUnstickThread($thread, $forum),
            
'canLockUnlockThread' => $threadModel->canLockUnlockThread($thread, $forum),
            
'canWatchThread' => $threadModel->canWatchThread($thread, $forum),
            
'canViewIps' => $threadModel->canViewIps($thread, $forum),
            
'canViewAttachments' => $threadModel->canViewAttachmentsInThread($thread, $forum),
            
'canViewModeratorLog' => $threadModel->canViewThreadModeratorLog($thread, $forum),
            
'canViewWarnings' => $this->getModelFromCache('XenForo_Model_User')->canViewWarnings(),
            
'watchState' => $threadModel->getThreadWatchStateFromThread($thread),
        ) + 
$viewParams;
    }

    
/**
     * Asserts that the currently browsing user can reply to the specified thread.
     *
     * @param array $thread
     * @param array $forum
     */
    
protected function _assertCanReplyToThread(array $thread, array $forum)
    {
        if (!
$this->_getThreadModel()->canReplyToThread($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }
    }

    
/**
     * Asserts that the currently browsing user can edit this thread.
     *
     * @param array $thread
     * @param array $forum
     */
    
protected function _assertCanEditThread(array $thread, array $forum)
    {
        if (!
$this->_getThreadModel()->canEditThread($thread, $forum, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }
    }

    
/**
     * Asserts that the currently browsing user can delete this thread.
     *
     * @param array $thread
     * @param array $forum
     * @param string $deleteType Type of deletion (soft or hard)
     */
    
protected function _assertCanDeleteThread(array $thread, array $forum, $deleteType)
    {
        if (!
$this->_getThreadModel()->canDeleteThread($thread, $forum, $deleteType, $errorPhraseKey))
        {
            throw 
$this->getErrorOrNoPermissionResponseException($errorPhraseKey);
        }
    }

    
/**
     * @see XenForo_Controller::canUpdateSessionActivity()
     */
    
public function canUpdateSessionActivity($controllerName, $action, &$newState)
    {
        if (
strtolower($action) == 'addreply')
        {
            return 
true;
        }

        return 
parent::canUpdateSessionActivity($controllerName, $action, $newState);
    }

    
/**
     * Session activity details.
     * @see XenForo_Controller::getSessionActivityDetailsForList()
     */
    
public static function getSessionActivityDetailsForList(array $activities)
    {
        
$threadIds = array();
        foreach (
$activities AS $activity)
        {
            if (!empty(
$activity['params']['thread_id']))
            {
                
$threadIds[$activity['params']['thread_id']] = intval($activity['params']['thread_id']);
            }
        }

        
$threadData = array();

        if (
$threadIds)
        {
            
/* @var $threadModel XenForo_Model_Thread */
            
$threadModel = XenForo_Model::create('XenForo_Model_Thread');

            
$visitor = XenForo_Visitor::getInstance();
            
$permissionCombinationId = $visitor['permission_combination_id'];

            
$threads = $threadModel->getThreadsByIds($threadIds, array(
                
'join' => XenForo_Model_Thread::FETCH_FORUM,
                
'permissionCombinationId' => $permissionCombinationId
            
));
            foreach (
$threads AS $thread)
            {
                
$visitor->setNodePermissions($thread['node_id'], $thread['node_permission_cache']);
                if (
$threadModel->canViewThreadAndContainer($thread, $thread))
                {
                    
$thread['title'] = XenForo_Helper_String::censorString($thread['title']);

                    
$threadData[$thread['thread_id']] = array(
                        
'title' => $thread['title'],
                        
'url' => XenForo_Link::buildPublicLink('threads', $thread),
                        
'previewUrl' => ($threadModel->hasPreview($thread, $thread) ? XenForo_Link::buildPublicLink('threads/preview', $thread) : '')
                    );
                }
            }
        }

        
$output = array();
        foreach (
$activities AS $key => $activity)
        {
            
$thread = false;
            if (!empty(
$activity['params']['thread_id']))
            {
                
$threadId = $activity['params']['thread_id'];
                if (isset(
$threadData[$threadId]))
                {
                    
$thread = $threadData[$threadId];
                }
            }

            if (
$thread)
            {
                
$output[$key] = array(
                    new 
XenForo_Phrase('viewing_thread'),
                    
$thread['title'],
                    
$thread['url'],
                    
$thread['previewUrl']
                );
            }
            else
            {
                
$output[$key] = new XenForo_Phrase('viewing_thread');
            }
        }

        return 
$output;
    }

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

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

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

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

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

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

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

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

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