Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Pub/Controller/Forum.php
<?php

namespace XF\Pub\Controller;

use
XF\Mvc\ParameterBag;

use function
count, in_array, intval, is_int, strval;

class
Forum extends AbstractController
{
   
/**
     * @param ParameterBag $params
     *
     * @return \XF\Mvc\Reply\Reroute|\XF\Mvc\Reply\View
     */
   
public function actionIndex(ParameterBag $params)
    {
        if (
$params->node_id || $params->node_name)
        {
            return
$this->rerouteController('XF:Forum', 'Forum', $params);
        }

        if (
$this->responseType == 'rss')
        {
            return
$this->getForumRss();
        }

        switch (
$this->options()->forumsDefaultPage)
        {
            case
'new_posts':
                return
$this->rerouteController(__CLASS__, 'newposts');

            case
'forums':
            default:
                return
$this->rerouteController(__CLASS__, 'list');
        }
    }

    public function
actionList(ParameterBag $params)
    {
       
$this->assertNotEmbeddedImageRequest();

        if (
$params->node_id || $params->node_name)
        {
           
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);
            return
$this->redirectPermanently($this->buildLink('forums', $forum));
        }

       
$selfRoute = ($this->options()->forumsDefaultPage == 'forums' ? 'forums' : 'forums/list');

       
$this->assertCanonicalUrl($this->buildLink($selfRoute));

       
$nodeRepo = $this->getNodeRepo();
       
$nodes = $nodeRepo->getNodeList();

       
$nodeTree = $nodeRepo->createNodeTree($nodes);
       
$nodeExtras = $nodeRepo->getNodeListExtras($nodeTree);

       
$viewParams = [
           
'nodeTree' => $nodeTree,
           
'nodeExtras' => $nodeExtras,
           
'selfRoute' => $selfRoute
       
];
        return
$this->view('XF:Forum\Listing', 'forum_list', $viewParams);
    }

    public function
actionNewPosts(ParameterBag $params)
    {
       
$this->assertNotEmbeddedImageRequest();

        if (
$params->node_id || $params->node_name)
        {
           
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);
            return
$this->redirectPermanently($this->buildLink('forums', $forum));
        }

        if (
$this->options()->forumsDefaultPage != 'new_posts')
        {
            return
$this->redirectPermanently($this->buildLink('whats-new/posts'));
        }

       
/** @var \XF\ControllerPlugin\FindNew $findNewPlugin */
       
$findNewPlugin = $this->plugin('XF:FindNew');

       
$handler = $findNewPlugin->getFindNewHandler('thread');
        if (!
$handler)
        {
            return
$this->noPermission();
        }

       
$page = $this->filterPage();
       
$perPage = $handler->getResultsPerPage();

       
$findNewId = $this->filter('f', 'uint');

        if (
$this->options()->forumsDefaultPage == 'new_posts')
        {
            if (!
$findNewId && $page == 1)
            {
               
$selfRoute = 'forums';
            }
            else
            {
               
$selfRoute = 'forums/new-posts';
            }
        }
        else
        {
           
$selfRoute = 'forums/new-posts';
        }

       
$this->assertCanonicalUrl($this->buildLink($selfRoute));

       
$findNew = $findNewPlugin->getFindNewRecord($findNewId, 'thread');
        if (!
$findNew)
        {
           
$filters = $findNewPlugin->getRequestedFilters($handler);
           
$findNew = $findNewPlugin->runFindNewSearch($handler, $filters);

            if (
$this->filter('save', 'bool') && $this->isPost())
            {
               
$findNewPlugin->saveDefaultFilters($handler, $filters);
            }

            if (
$findNewPlugin->findNewRequiresSaving($findNew))
            {
               
$findNew->save();

                if (
$this->isPost())
                {
                    return
$this->redirect($this->buildLink('forums/new-posts', null, [
                       
'f' => $findNew->find_new_id
                   
]));
                }
            }

           
$page = 1;
        }
        else
        {
           
$remove = $this->filter('remove', 'str');
            if (
$remove)
            {
               
$filters = $findNew->filters;
                unset(
$filters[$remove]);

               
$findNew = $findNewPlugin->runFindNewSearch($handler, $filters);
                if (
$findNewPlugin->findNewRequiresSaving($findNew))
                {
                   
$findNew->save();

                    return
$this->redirect($this->buildLink('forums/new-posts', null, [
                       
'f' => $findNew->find_new_id
                   
]));
                }
            }
        }

        if (
$findNew->result_count)
        {
           
$this->assertValidPage($page, $perPage, $findNew->result_count, 'forums/new-posts');

           
$pageIds = $findNew->getPageResultIds($page, $perPage);
           
$threads = $handler->getPageResults($pageIds);
        }
        else
        {
           
$threads = [];
        }

       
$canInlineMod = false;
        foreach (
$threads AS $thread)
        {
            if (
$thread->canUseInlineModeration())
            {
               
$canInlineMod = true;
                break;
            }
        }

       
$viewParams = [
           
'findNew' => $findNew,
           
'originalFindNewId' => $findNewId,
           
'page' => $page,
           
'perPage' => $perPage,
           
'selfRoute' => $selfRoute,

           
'threads' => $threads,
           
'canInlineMod' => $canInlineMod
       
];
        return
$this->view('XF:Forum\NewPosts', 'forum_new_posts', $viewParams);
    }

    public function
actionForum(ParameterBag $params)
    {
       
$this->assertNotEmbeddedImageRequest();

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name, $this->getForumViewExtraWith());
       
$forumTypeHandler = $forum->TypeHandler;

       
$overrideReply = $forumTypeHandler->overrideDisplay($forum, $this);
        if (
$overrideReply)
        {
            return
$overrideReply;
        }

        if (
$this->responseType == 'rss')
        {
           
$this->assertNotEmbeddedImageRequest();

            return
$this->getForumRss($forum);
        }

       
$filters = $this->getForumFilterInput($forum);

       
$page = $this->filterPage($params->page);
       
$perPage = $forumTypeHandler->getThreadsPerPage($forum);

       
$this->assertCanonicalUrl($this->buildLink('forums', $forum, ['page' => $page]));

       
$threadRepo = $this->getThreadRepo();

       
$threadList = $threadRepo->findThreadsForForumView(
           
$forum, [
           
'allowOwnPending' => $this->hasContentPendingApproval()
        ]
        );
       
$this->applyForumFilters($forum, $threadList, $filters);
       
$forum->TypeHandler->adjustForumThreadListFinder($forum, $threadList, $page, $this->request);

        if (
$page == 1)
        {
           
$stickyThreadList = clone $threadList;

           
/** @var \XF\Entity\Thread[]|\XF\Mvc\Entity\AbstractCollection $stickyThreads */
           
$stickyThreads = $stickyThreadList->where('sticky', 1)->fetch();
        }
        else
        {
           
$stickyThreads = null;
        }

       
$this->applyDateLimitFilters($forum, $threadList, $filters);

       
$threadList->where('sticky', 0)
            ->
limitByPage($page, $perPage);

       
/** @var \XF\Entity\Thread[]|\XF\Mvc\Entity\AbstractCollection $threads */
       
$threads = $threadList->fetch();
       
$totalThreads = $threadList->total();

       
$this->assertValidPage($page, $perPage, $totalThreads, 'forums', $forum->Node);

       
$nodeRepo = $this->getNodeRepo();
       
$nodes = $nodeRepo->getNodeList($forum->Node);
       
$nodeTree = count($nodes) ? $nodeRepo->createNodeTree($nodes, $forum->node_id) : null;
       
$nodeExtras = $nodeTree ? $nodeRepo->getNodeListExtras($nodeTree) : null;

       
$allThreads = $threads;
        if (
$stickyThreads)
        {
           
$allThreads->merge($stickyThreads);
        }
       
$forumTypeHandler->fetchExtraContentForThreadsFullView($forum, $allThreads);

       
$canInlineMod = false;
        if (
$stickyThreads)
        {
            foreach (
$stickyThreads AS $thread)
            {
                if (
$thread->canUseInlineModeration())
                {
                   
$canInlineMod = true;
                    break;
                }
            }
        }
        foreach (
$threads AS $thread)
        {
            if (
$thread->canUseInlineModeration())
            {
               
$canInlineMod = true;
                break;
            }
        }

       
// if the forum is unread and perhaps it shouldn't be, see if we can mark it AS read
       
if (\XF::visitor()->user_id
           
&& $page == 1
           
&& !$filters
           
&& $forum->isUnread()
        )
        {
           
$hasNew = false;
            foreach (
$threads AS $thread)
            {
                if (
$thread->isUnread() && !$thread->isIgnored())
                {
                   
$hasNew = true;
                    break;
                }
            }

            if (!
$hasNew)
            {
               
$this->getForumRepo()->markForumReadIfPossible($forum);
            }
        }

        if (!empty(
$filters['starter_id']))
        {
           
$starterFilter = $this->em()->find('XF:User', $filters['starter_id']);
        }
        else
        {
           
$starterFilter = null;
        }

        if (!empty(
$filters['thread_type']) && $forum->TypeHandler->isThreadTypeAllowed($filters['thread_type'], $forum))
        {
           
$threadTypeFilter = $this->app->threadType($filters['thread_type'], false);
        }
        else
        {
           
$threadTypeFilter = null;
        }

       
$isDateLimited = (empty($filters['no_date_limit']) && (!empty($filters['last_days']) || $forum->list_date_limit_days));
       
$threadEndOffset = ($page - 1) * $perPage + count($threads);
       
$showDateLimitDisabler = ($isDateLimited && $threadEndOffset >= $totalThreads);

       
$canonicalFilters = [];
        if (
$page > 1 && $forum->list_date_limit_days && empty($filters['order']))
        {
           
$cutOff = \XF::$time - ($forum->list_date_limit_days * 86400);
           
$lastThread = $threads->last();
            if (
               
$showDateLimitDisabler
               
|| ($lastThread && $lastThread->last_post_date <= $cutOff)
            )
            {
               
// we have removed the date limit and the last thread here is only shown because of that
               
$canonicalFilters['no_date_limit'] = 1;
            }
        }

       
$isPreRegQuickThread = $forum->canCreateThreadPreReg();
        if (
$forum->canCreateThread() || $isPreRegQuickThread)
        {
           
$quickThreadPrefixes = \XF::asPreRegActionUserIfNeeded(
               
$isPreRegQuickThread,
                function() use (
$forum) { return $forum->getUsablePrefixes(); }
            );
        }
        else
        {
           
$quickThreadPrefixes = [];
        }

       
$overrideContext = [
           
'page' => $page,
           
'filters' => $filters
       
];

       
$viewParams = [
           
'forum' => $forum,

           
'canInlineMod' => $canInlineMod,

           
'nodeTree' => $nodeTree,
           
'nodeExtras' => $nodeExtras,

           
'stickyThreads' => $stickyThreads,
           
'threads' => $threads,

           
'page' => $page,
           
'perPage' => $perPage,
           
'total' => $totalThreads,

           
'filters' => $filters,
           
'canonicalFilters' => $canonicalFilters,
           
'starterFilter' => $starterFilter,
           
'threadTypeFilter' => $threadTypeFilter,
           
'showDateLimitDisabler' => $showDateLimitDisabler,

           
'sortInfo' => $this->getEffectiveSortInfo($forum, $filters),

           
'pendingApproval' => $this->filter('pending_approval', 'bool'),

           
'templateOverrides' => $forumTypeHandler->getForumViewTemplateOverrides($forum, $overrideContext),

           
'quickThreadPrefixes' => $quickThreadPrefixes
       
];

        list(
$viewClass, $viewTemplate) = $forumTypeHandler->getForumViewAndTemplate($forum);
       
$viewParams = $forumTypeHandler->adjustForumViewParams($forum, $viewParams, $this->request);

        return
$this->view($viewClass, $viewTemplate, $viewParams);
    }

    protected function
getForumViewExtraWith()
    {
       
$extraWith = [];
       
$userId = \XF::visitor()->user_id;
        if (
$userId)
        {
           
$extraWith[] = 'Watch|' . $userId;
        }

        return
$extraWith;
    }

    protected function
getAvailableForumSorts(\XF\Entity\Forum $forum)
    {
        return
$forum->TypeHandler->getThreadListSortOptions($forum);
    }

    protected function
getAvailableDateLimits(\XF\Entity\Forum $forum)
    {
        return [-
1, 7, 14, 30, 60, 90, 182, 365];
    }

    protected function
applyForumFilters(\XF\Entity\Forum $forum, \XF\Finder\Thread $threadFinder, array $filters)
    {
        if (!empty(
$filters['prefix_id']))
        {
           
$threadFinder->where('prefix_id', intval($filters['prefix_id']));
        }

        if (!empty(
$filters['starter_id']))
        {
           
$threadFinder->where('user_id', intval($filters['starter_id']));
        }

        if (!empty(
$filters['thread_type']))
        {
           
$threadFinder->where('discussion_type', $filters['thread_type']);
        }

       
$sorts = $this->getAvailableForumSorts($forum);

        if (!empty(
$filters['order']) && isset($sorts[$filters['order']]))
        {
           
$threadFinder->order($sorts[$filters['order']], $filters['direction']);
        }
       
// else the default order has already been applied

       
$forum->TypeHandler->applyForumFilters($forum, $threadFinder, $filters);
    }

    protected function
applyDateLimitFilters(\XF\Entity\Forum $forum, \XF\Finder\Thread $threadFinder, array $filters)
    {
        if (!empty(
$filters['last_days']) && empty($filters['no_date_limit']))
        {
            if (
$filters['last_days'] > 0)
            {
               
$threadFinder->where('last_post_date', '>=', \XF::$time - ($filters['last_days'] * 86400));
            }
        }
        else if (
$forum->list_date_limit_days && empty($filters['no_date_limit']))
        {
           
$threadFinder->where('last_post_date', '>=', \XF::$time - ($forum->list_date_limit_days * 86400));
        }
    }

    protected function
getForumFilterInput(\XF\Entity\Forum $forum)
    {
       
$filters = [];

       
$input = $this->filter([
           
'prefix_id' => 'uint',
           
'starter' => 'str',
           
'starter_id' => 'uint',
           
'last_days' => 'int',
           
'order' => 'str',
           
'direction' => 'str',
           
'no_date_limit' => 'bool',
           
'thread_type' => 'str'
       
]);

        if (
$input['no_date_limit'])
        {
           
$filters['no_date_limit'] = $input['no_date_limit'];
        }

        if (
$input['prefix_id'] && $forum->isPrefixValid($input['prefix_id']))
        {
           
$filters['prefix_id'] = $input['prefix_id'];
        }

        if (
$input['starter_id'])
        {
           
$filters['starter_id'] = $input['starter_id'];
        }
        else if (
$input['starter'])
        {
           
$user = $this->em()->findOne('XF:User', ['username' => $input['starter']]);
            if (
$user)
            {
               
$filters['starter_id'] = $user->user_id;
            }
        }

        if (
            (
$input['last_days'] > 0 && $input['last_days'] != $forum->list_date_limit_days)
            || (
$input['last_days'] < 0 && $forum->list_date_limit_days)
        )
        {
            if (
in_array($input['last_days'], $this->getAvailableDateLimits($forum)))
            {
               
$filters['last_days'] = $input['last_days'];
            }
        }

       
$sorts = $this->getAvailableForumSorts($forum);

        if (
$input['order'] && isset($sorts[$input['order']]))
        {
            if (!
in_array($input['direction'], ['asc', 'desc']))
            {
               
$input['direction'] = 'desc';
            }

            if (
$input['order'] != $forum->default_sort_order || $input['direction'] != $forum->default_sort_direction)
            {
               
$filters['order'] = $input['order'];
               
$filters['direction'] = $input['direction'];
            }
        }

        if (
$input['thread_type'] && $forum->TypeHandler->isThreadTypeAllowed($input['thread_type'], $forum))
        {
           
$filters['thread_type'] = $input['thread_type'];
        }

       
$filters = $forum->TypeHandler->getForumFilterInput($forum, $this->request, $filters);

        return
$filters;
    }

    public function
actionFilters(ParameterBag $params)
    {
       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);

       
$filters = $this->getForumFilterInput($forum);

        if (
$this->filter('apply', 'bool'))
        {
            if (!empty(
$filters['last_days']))
            {
                unset(
$filters['no_date_limit']);
            }
            return
$this->redirect($this->buildLink('forums', $forum, $filters));
        }

        if (!empty(
$filters['starter_id']))
        {
           
$starterFilter = $this->em()->find('XF:User', $filters['starter_id']);
        }
        else
        {
           
$starterFilter = null;
        }

       
$allowedThreadTypes = [];
        foreach (
$forum->TypeHandler->getFilterableThreadTypes($forum) AS $threadType)
        {
           
$handler = $this->app->threadType($threadType, false);
            if (
$handler)
            {
               
$allowedThreadTypes[$threadType] = $handler;
            }
        }

       
$viewParams = [
           
'forum' => $forum,
           
'prefixes' => $forum->prefixes->groupBy('prefix_group_id'),
           
'filters' => $filters,
           
'starterFilter' => $starterFilter,
           
'allowedThreadTypes' => $allowedThreadTypes,
           
'sortOptions' => $forum->TypeHandler->getThreadListSortOptions($forum)
        ];
       
$view = $this->view('XF:Forum\Filters', 'forum_filters', $viewParams);

        return
$forum->TypeHandler->adjustForumFiltersPopup($forum, $view);
    }

    public function
actionDraft(ParameterBag $params)
    {
       
$this->assertDraftsEnabled();

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);

       
/** @var \XF\Service\Thread\Creator $creator */
       
$creator = $this->setupThreadCreate($forum);
       
$thread = $creator->getThread();

       
$extraData = [
           
'prefix_id' => $thread->prefix_id,
           
'title' => $thread->title,
           
'discussion_type' => $this->filter('discussion_type', 'str'),
           
'tags' => $this->filter('tags', 'str'),
           
'attachment_hash' => $this->filter('attachment_hash', 'str'),
           
'custom_fields' => $thread->custom_fields->getFieldValues()
        ];

       
$extraData = array_merge(
           
$extraData,
           
$thread->TypeHandler->getExtraDataForDraft($thread, $this->request)
        );

       
/** @var \XF\ControllerPlugin\Draft $draftPlugin */
       
$draftPlugin = $this->plugin('XF:Draft');
       
$draftReply = $draftPlugin->actionDraftMessage($forum->draft_thread, $extraData, 'message', $draftAction);

        if (
$draftAction == 'save')
        {
           
$draftPlugin->refreshTempAttachments($extraData['attachment_hash']);
        }

        return
$draftReply;
    }

    protected function
getPrefixIdIfUsable(\XF\Entity\Forum $forum): int
   
{
       
$prefixId = $this->filter('prefix_id', 'uint');
        if (
$prefixId)
        {
           
$isUsable = \XF::asPreRegActionUserIfNeeded(
               
$forum->canCreateThreadPreReg(),
                function() use (
$forum, $prefixId) { return $forum->isPrefixUsable($prefixId); }
            );
            if (
$isUsable)
            {
                return
$prefixId;
            }
        }

        return
0;
    }

   
/**
     * @param \XF\Entity\Forum $forum
     *
     * @return \XF\Service\Thread\Creator
     */
   
protected function setupThreadCreate(\XF\Entity\Forum $forum)
    {
       
$title = $this->filter('title', 'str');
       
$message = $this->plugin('XF:Editor')->fromInput('message');

       
/** @var \XF\Service\Thread\Creator $creator */
       
$creator = $this->service('XF:Thread\Creator', $forum);

       
$isPreRegAction = $forum->canCreateThreadPreReg();
        if (
$isPreRegAction)
        {
           
// only returns true when pre-reg creating is the only option
           
$creator->setIsPreRegAction(true);
        }

       
$creator->setDiscussionTypeAndData(
           
$this->filter('discussion_type', 'str'),
           
$this->request
       
);

       
$creator->setContent($title, $message);

       
$prefixId = $this->getPrefixIdIfUsable($forum);
        if (
$prefixId)
        {
           
$creator->setPrefix($prefixId);
        }

       
$canEditTags = \XF::asPreRegActionUserIfNeeded(
           
$isPreRegAction,
            function() use (
$forum) { return $forum->canEditTags(); }
        );
        if (
$canEditTags)
        {
           
$creator->setTags($this->filter('tags', 'str'));
        }

       
// attachments aren't supported in pre-reg actions
       
if ($forum->canUploadAndManageAttachments())
        {
           
$creator->setAttachmentHash($this->filter('attachment_hash', 'str'));
        }

       
$setOptions = $this->filter('_xfSet', 'array-bool');
        if (
$setOptions)
        {
           
$thread = $creator->getThread();

            if (isset(
$setOptions['discussion_open']) && $thread->canLockUnlock())
            {
               
$creator->setDiscussionOpen($this->filter('discussion_open', 'bool'));
            }
            if (isset(
$setOptions['sticky']) && $thread->canStickUnstick())
            {
               
$creator->setSticky($this->filter('sticky', 'bool'));
            }
        }

       
$customFields = $this->filter('custom_fields', 'array');
       
$creator->setCustomFields($customFields);

        return
$creator;
    }

    protected function
finalizeThreadCreate(\XF\Service\Thread\Creator $creator)
    {
       
$creator->sendNotifications();

       
$forum = $creator->getForum();
       
$thread = $creator->getThread();
       
$visitor = \XF::visitor();

       
$setOptions = $this->filter('_xfSet', 'array-bool');
        if (
$thread->canWatch())
        {
            if (isset(
$setOptions['watch_thread']))
            {
               
$watch = $this->filter('watch_thread', 'bool');
                if (
$watch)
                {
                   
/** @var \XF\Repository\ThreadWatch $threadWatchRepo */
                   
$threadWatchRepo = $this->repository('XF:ThreadWatch');

                   
$state = $this->filter('watch_thread_email', 'bool') ? 'watch_email' : 'watch_no_email';
                   
$threadWatchRepo->setWatchState($thread, $visitor, $state);
                }
            }
            else
            {
               
// use user preferences
               
$this->repository('XF:ThreadWatch')->autoWatchThread($thread, $visitor, true);
            }
        }

        if (
$visitor->user_id)
        {
           
$this->getThreadRepo()->markThreadReadByVisitor($thread, $thread->post_date);

           
$forum->draft_thread->delete();

            if (
$thread->discussion_state == 'moderated')
            {
               
$this->session()->setHasContentPendingApproval();
            }
        }
    }

    public function
actionCreateThread(ParameterBag $params)
    {
        return
$this->rerouteController('XF:Forum', 'postThread', $params);
    }

    public function
actionPostThreadChooser()
    {
       
$visitor = \XF::visitor();
        if (!
$visitor->canCreateThread($error) && !$visitor->canCreateThreadPreReg())
        {
            return
$this->noPermission($error);
        }

       
$this->assertCanonicalUrl($this->buildLink('forums/post-thread'));

       
$nodeRepo = $this->getNodeRepo();
       
$nodes = $nodeRepo->getNodeList();

       
$canCreateThread = false;
        foreach (
$nodes AS $nodeId => $node)
        {
            if (
$node->node_type_id != 'Forum')
            {
                continue;
            }

           
/** @var \XF\Entity\Forum $forum */
           
$forum = $node->Data;
            if (
$forum->canCreateThread() || $forum->canCreateThreadPreReg())
            {
               
$canCreateThread = true;
                break;
            }
        }

        if (!
$canCreateThread)
        {
            return
$this->noPermission();
        }

       
$nodeTree = $nodeRepo->createNodeTree($nodes);
       
$nodeTree = $nodeTree->filter(null, function($id, \XF\Entity\Node $node, $depth, $children, \XF\Tree $tree)
        {
            if (
$children)
            {
                return
true;
            }
            if (
               
$node->node_type_id == 'Forum'
               
&& (
                   
$node->Data->canCreateThread()
                    ||
$node->Data->canCreateThreadPreReg()
                )
            )
            {
                return
true;
            }
            return
false;
        });

       
$nodeExtras = $nodeRepo->getNodeListExtras($nodeTree);

       
$viewParams = [
           
'nodeTree' => $nodeTree,
           
'nodeExtras' => $nodeExtras
       
];
        return
$this->view('XF:Forum\PostThreadChooser', 'forum_post_thread_chooser', $viewParams);
    }

    public function
actionPostThread(ParameterBag $params)
    {
        if (!
$params->node_id && !$params->node_name)
        {
            return
$this->rerouteController('XF:Forum', 'postThreadChooser');
        }

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name, ['DraftThreads|' . \XF::visitor()->user_id]);

       
$isPreRegThread = $forum->canCreateThreadPreReg();

        if (!
$forum->canCreateThread($error) && !$isPreRegThread)
        {
            return
$this->noPermission($error);
        }

       
$this->assertCanonicalUrl($this->buildLink('forums/post-thread', $forum));

       
$switches = $this->filter([
           
'inline-mode' => 'bool',
           
'more-options' => 'bool'
       
]);
        if (
$switches['more-options'])
        {
           
$switches['inline-mode'] = false;
        }

       
$creatableThreadTypes = [];
        foreach (
$forum->getCreatableThreadTypes() AS $threadType)
        {
           
$handler = $this->app->threadType($threadType, false);
            if (
$handler)
            {
               
$creatableThreadTypes[$threadType] = $handler;
            }
        }

       
// inline mode doesn't pull from drafts
       
$draftThreadType = $switches['inline-mode'] ? null : $forum->draft_thread['discussion_type'];
        if (
$draftThreadType && isset($creatableThreadTypes[$draftThreadType]))
        {
           
$defaultThreadType = $draftThreadType;
        }
        else
        {
           
$defaultThreadType = key($creatableThreadTypes);
        }

       
$thread = null;
       
$post = null;
       
$tags = null;
       
$typeDataDraftOverride = null;

        if (
$this->isPost())
        {
            if (!
$this->captchaIsValid() && !$isPreRegThread)
            {
                return
$this->error(\XF::phrase('did_not_complete_the_captcha_verification_properly'));
            }

           
$creator = $this->setupThreadCreate($forum);

            if (
$switches['more-options'])
            {
               
$thread = $creator->getThread();
               
$post = $creator->getPost();
               
$tags = $this->filter('tags', 'str');
               
$defaultThreadType = $thread->discussion_type;
               
$typeDataDraftOverride = $thread->TypeHandler->getExtraDataForDraft($thread, $this->request);
            }
            else
            {
                if (!
$isPreRegThread)
                {
                   
$creator->checkForSpam();
                }

                if (!
$creator->validate($errors))
                {
                    return
$this->error($errors);
                }

                if (
$isPreRegThread)
                {
                   
/** @var \XF\ControllerPlugin\PreRegAction $preRegActionPlugin */
                   
$preRegActionPlugin = $this->plugin('XF:PreRegAction');
                    return
$preRegActionPlugin->actionPreRegAction(
                       
'XF:Thread\Create',
                       
$forum,
                       
$this->getPreRegThreadActionData($creator)
                    );
                }

               
$this->assertNotFlooding('thread', $this->app->options()->floodCheckLengthDiscussion ?: null);

               
/** @var \XF\Entity\Thread $thread */
               
$thread = $creator->save();
               
$this->finalizeThreadCreate($creator);

                if (
$switches['inline-mode'])
                {
                   
$viewParams = [
                       
'thread' => $thread,
                       
'forum' => $forum,
                       
'templateOverrides' => $forum->TypeHandler->getForumViewTemplateOverrides($forum),
                       
'inlineMode' => true
                   
];
                    return
$this->view('XF:Forum\ThreadItem', 'thread_list_item', $viewParams);
                }
                else if (!
$thread->canView())
                {
                    return
$this->redirect($this->buildLink('forums', $forum, ['pending_approval' => 1]));
                }
                else
                {
                    return
$this->redirect($this->buildLink('threads', $thread));
                }
            }
        }

        if (
$forum->canUploadAndManageAttachments())
        {
           
/** @var \XF\Repository\Attachment $attachmentRepo */
           
$attachmentRepo = $this->repository('XF:Attachment');
           
$attachmentData = $attachmentRepo->getEditorData(
               
'post',
               
$forum,
               
$switches['inline-mode'] ? null : $forum->draft_thread['attachment_hash']
            );
        }
        else
        {
           
$attachmentData = null;
        }

       
$templateName = $switches['inline-mode'] ? 'forum_post_quick_thread' : 'forum_post_thread';

        if (!
$thread)
        {
           
$thread = $forum->getNewThread();
           
$prefixId = $this->getPrefixIdIfUsable($forum);
            if (
$prefixId)
            {
               
$thread->prefix_id = $prefixId;
            }
            if (!
$switches['inline-mode'] && $forum->draft_thread && $forum->draft_thread['custom_fields'])
            {
               
/** @var \XF\CustomField\Set $customFields */
               
$customFields = $thread->custom_fields;
               
$customFields->bulkSet($forum->draft_thread['custom_fields'], null, 'user', true);
            }
        }

       
$prefixes = \XF::asPreRegActionUserIfNeeded(
           
$isPreRegThread,
            function() use (
$forum) { return $forum->getUsablePrefixes(); }
        );
       
$canEditTags = \XF::asPreRegActionUserIfNeeded(
           
$isPreRegThread,
            function() use (
$forum) { return $forum->canEditTags(); }
        );

       
$viewParams = [
           
'forum' => $forum,
           
'thread' => $thread ?: $forum->getNewThread(),
           
'post' => $post ?: null,
           
'title' => $this->filter('title', 'string'),
           
'prefixes' => $prefixes,
           
'canEditTags' => $canEditTags,
           
'tags' => $tags,
           
'typeDataDraftOverride' => $typeDataDraftOverride,

           
'creatableThreadTypes' => $creatableThreadTypes,
           
'defaultThreadType' => $defaultThreadType,

           
'attachmentData' => $attachmentData,
           
'inlineMode' => $switches['inline-mode']
        ];
        return
$this->view('XF:Forum\PostThread', $templateName, $viewParams);
    }

    protected function
getPreRegThreadActionData(\XF\Service\Thread\Creator $creator)
    {
       
$thread = $creator->getThread();
       
$post = $creator->getPost();

        return [
           
'title' => $thread->title,
           
'message' => $post->message,
           
'prefix_id' => $thread->prefix_id,
           
'tags' => $this->filter('tags', 'str'),
           
'custom_fields' => $thread->custom_fields->getFieldValues(),

           
'discussion_type' => $thread->discussion_type,
           
'type_input' => $thread->TypeHandler->getExtraDataForPreRegAction($thread, $this->request)
        ];
    }

    public function
actionThreadPreview(ParameterBag $params)
    {
       
$this->assertPostOnly();

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);
        if (!
$forum->canCreateThread($error) && !$forum->canCreateThreadPreReg())
        {
            return
$this->noPermission($error);
        }

       
$creator = $this->setupThreadCreate($forum);
        if (!
$creator->validate($errors) && isset($errors['message']))
        {
            return
$this->error($errors);
        }

       
$thread = $creator->getThread();
       
$post = $creator->getPost();
       
$attachments = null;

       
$tempHash = $this->filter('attachment_hash', 'str');
        if (
$tempHash && $thread->Forum->canUploadAndManageAttachments())
        {
           
$attachRepo = $this->repository('XF:Attachment');
           
$attachments = $attachRepo->findAttachmentsByTempHash($tempHash)->fetch();
        }

        return
$this->plugin('XF:BbCodePreview')->actionPreview(
           
$post->message, 'post', $post->User, $attachments, $thread->canViewAttachments()
        );
    }

    public function
actionMarkRead(ParameterBag $params)
    {
       
$visitor = \XF::visitor();
        if (!
$visitor->user_id)
        {
            return
$this->noPermission();
        }

       
$markDate = $this->filter('date', 'uint');
        if (!
$markDate)
        {
           
$markDate = \XF::$time;
        }

       
$forumRepo = $this->getForumRepo();

       
$lookup = $params->node_id ?: $params->node_name;
        if (
$lookup)
        {
           
$forum = $this->assertViewableForum($lookup);
        }
        else
        {
           
$forum = null;
        }

        if (
$this->isPost())
        {
            if (
$forum)
            {
               
$forumRepo->markForumTreeReadByVisitor($forum, $markDate);

                return
$this->redirect(
                   
$this->buildLink('forums', $forum),
                    \
XF::phrase('forum_x_marked_as_read', ['forum' => $forum->title])
                );
            }
            else
            {
               
$forumRepo->markForumTreeReadByVisitor(null, $markDate);

                return
$this->redirect(
                   
$this->buildLink('forums'),
                    \
XF::phrase('all_forums_marked_as_read')
                );
            }
        }
        else
        {
           
$viewParams = [
               
'forum' => $forum,
               
'date' => $markDate
           
];
            return
$this->view('XF:Forum\MarkRead', 'forum_mark_read', $viewParams);
        }
    }

    public function
actionWatch(ParameterBag $params)
    {
       
$visitor = \XF::visitor();
        if (!
$visitor->user_id)
        {
            return
$this->noPermission();
        }

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);
        if (!
$forum->canWatch($error))
        {
            return
$this->noPermission($error);
        }

        if (
$this->isPost())
        {
            if (
$this->filter('stop', 'bool'))
            {
               
$notifyType = 'delete';
            }
            else
            {
               
$notifyType = $this->filter('notify', 'str');
                if (
$notifyType != 'thread' && $notifyType != 'message')
                {
                   
$notifyType = '';
                }

                if (
$forum->allowed_watch_notifications == 'none')
                {
                   
$notifyType = '';
                }
                else if (
$forum->allowed_watch_notifications == 'thread' && $notifyType == 'message')
                {
                   
$notifyType = 'thread';
                }
            }

           
$sendAlert = $this->filter('send_alert', 'bool');
           
$sendEmail = $this->filter('send_email', 'bool');

           
/** @var \XF\Repository\ForumWatch $watchRepo */
           
$watchRepo = $this->repository('XF:ForumWatch');
           
$watchRepo->setWatchState($forum, $visitor, $notifyType, $sendAlert, $sendEmail);

           
$redirect = $this->redirect($this->buildLink('forums', $forum));
           
$redirect->setJsonParam('switchKey', $notifyType == 'delete' ? 'watch' : 'unwatch');
            return
$redirect;
        }
        else
        {
           
$viewParams = [
               
'forum' => $forum,
               
'isWatched' => !empty($forum->Watch[$visitor->user_id])
            ];
            return
$this->view('XF:Forum\Watch', 'forum_watch', $viewParams);
        }
    }

    public function
actionPrefixes(ParameterBag $params)
    {
       
$this->assertPostOnly();

       
$viewParams = [];

       
$nodeId = $this->filter('val', 'uint');
        if (
$nodeId)
        {
           
$forum = $this->assertViewableForum($nodeId);

           
$initialPrefix = null;
           
$initialPrefixId = $this->filter('initial_prefix_id', 'uint');
            if (
$initialPrefixId && isset($forum->prefixes[$initialPrefixId]))
            {
               
$initialPrefix = $forum->prefixes[$initialPrefixId];
            }

           
$viewParams['forum'] = $forum;
           
$viewParams['prefixes'] = $forum->getUsablePrefixes($initialPrefix);
        }

        return
$this->view('XF:Forum\Prefixes', 'forum_prefixes', $viewParams);
    }

    public function
actionPrefixHelp(ParameterBag $params)
    {
       
$this->assertPostOnly();

       
$forum = $this->assertViewableForum($params->node_id ?: $params->node_name);

       
$prefixId = $this->getPrefixIdIfUsable($forum);
        if (
$prefixId)
        {
           
$prefix = $this->em()->find('XF:ThreadPrefix', $prefixId);

            return
$this->view('XF:Forum\PrefixHelp', 'prefix_usage_help', [
               
'prefix' => $prefix,
               
'contentType' => 'thread'
           
]);
        }

        return
$this->notFound();
    }

    protected function
getForumRss(\XF\Entity\Forum $forum = null)
    {
       
$limit = $this->options()->discussionsPerPage;

       
$threadRepo = $this->getThreadRepo();
       
$threadList = $threadRepo->findThreadsForRssFeed($forum)->limit($limit * 3);

       
$order = $this->filter('order', 'str');
        switch (
$order)
        {
            case
'post_date':
                break;

            default:
               
$order = 'last_post_date';
                break;
        }
       
$threadList->order($order, 'DESC');

       
$threads = $threadList->fetch()->filterViewable()->slice(0, $limit);

        return
$this->view('XF:Forum\Rss', '', [
           
'forum' => $forum,
           
'order' => $order,
           
'threads' => $threads
       
]);
    }

   
/**
     * @param string|int $nodeIdOrName
     * @param array $extraWith
     *
     * @return \XF\Entity\Forum
     *
     * @throws \XF\Mvc\Reply\Exception
     */
   
protected function assertViewableForum($nodeIdOrName, array $extraWith = [])
    {
        if (
$nodeIdOrName === null)
        {
            throw
$this->exception($this->notFound(\XF::phrase('requested_forum_not_found')));
        }

       
$visitor = \XF::visitor();
       
$extraWith[] = 'Node.Permissions|' . $visitor->permission_combination_id;
        if (
$visitor->user_id)
        {
           
$extraWith[] = 'Read|' . $visitor->user_id;
        }

       
$finder = $this->em()->getFinder('XF:Forum');
       
$finder->with('Node', true)->with($extraWith);
        if (
is_int($nodeIdOrName) || $nodeIdOrName === strval(intval($nodeIdOrName)))
        {
           
$finder->where('node_id', $nodeIdOrName);
        }
        else
        {
           
$finder->where(['Node.node_name' => $nodeIdOrName, 'Node.node_type_id' => 'Forum']);
        }

       
/** @var \XF\Entity\Forum $forum */
       
$forum = $finder->fetchOne();
        if (!
$forum)
        {
            throw
$this->exception($this->notFound(\XF::phrase('requested_forum_not_found')));
        }
        if (!
$forum->canView($error))
        {
            throw
$this->exception($this->noPermission($error));
        }

       
$this->plugin('XF:Node')->applyNodeContext($forum->Node);

        return
$forum;
    }

   
/**
     * @return \XF\Repository\Forum
     */
   
protected function getForumRepo()
    {
        return
$this->repository('XF:Forum');
    }

   
/**
     * @return \XF\Repository\Node
     */
   
protected function getNodeRepo()
    {
        return
$this->repository('XF:Node');
    }

   
/**
     * @return \XF\Repository\Thread
     */
   
protected function getThreadRepo()
    {
        return
$this->repository('XF:Thread');
    }

   
/**
     * @param \XF\Entity\Forum $forum
     * @param array            $filters
     *
     * @return array [order, direction]
     */
   
protected function getEffectiveSortInfo(\XF\Entity\Forum $forum, array $filters)
    {
       
$sortInfo = [
           
'order' => $forum->default_sort_order,
           
'direction' => $forum->default_sort_direction
       
];

        if (isset(
$filters['order']))
        {
           
$sortInfo['order'] = $filters['order'];
        }

        if (isset(
$filters['direction']))
        {
           
$sortInfo['direction'] = $filters['direction'];
        }

        return
$sortInfo;
    }

   
/**
     * @param \XF\Entity\SessionActivity[] $activities
     */
   
public static function getActivityDetails(array $activities)
    {
        return \
XF\ControllerPlugin\Node::getNodeActivityDetails(
           
$activities,
           
'Forum',
            \
XF::phrase('viewing_forum'),
            function(
$activity, $nodeId, $nodeName)
            {
                if (
$activity->controller_action == 'NewPosts')
                {
                    return \
XF::phrase('viewing_latest_content');
                }
                else
                {
                    return (!
$nodeId && !$nodeName) ? \XF::phrase('viewing_forum_list') : \XF::phrase('viewing_forum');
                }
            }
        );
    }
}