XF 1.5 How can I extend actionForum properly?

Thank you once again for your help, Jake.

I'm making progress. Here's what I have now.

PHP:
<?php

class Andy_StickyAllPages_ControllerPublic_Forum extends XFCP_Andy_StickyAllPages_ControllerPublic_Forum
{
    public function actionForum()    
    {
        // get parent
        $parent = parent::actionForum();

        // if page is greater than 1
        if ($parent->params['page'] > 1) 
        {    
            // get forum
            $forum = $parent->params['forum'];

            // get forumId
            $forumId = $forum['node_id'];

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

            $page = max(1, $this->_input->filterSingle('page', XenForo_Input::UINT));
            
            // get threadsPerPage
            $threadsPerPage = $parent->params['threadsPerPage'];
            
            // get order
            $order = $forum['default_sort_order'];
            
            // get orderDirection
            $orderDirection = $forum['default_sort_direction'];
            
            // get displayConditions
            $displayConditions = $parent->params['displayConditions'];

            $fetchElements = $this->_getThreadFetchElements($forum, $displayConditions);
            $threadFetchConditions = $fetchElements['conditions'];
            $threadFetchOptions = $fetchElements['options'] + array(
                'perPage' => $threadsPerPage,
                'page' => $page,
                'order' => $order,
                'orderDirection' => $orderDirection
            );
            unset($fetchElements);

            // get threads
            $threads = $parent->params['threads'];
            
            // get stickyThreads
            $stickyThreadFetchOptions = $threadFetchOptions;
            unset($stickyThreadFetchOptions['perPage'], $stickyThreadFetchOptions['page']);

            $stickyThreadConditions = $threadFetchConditions;
            unset($stickyThreadConditions['last_post_date']);

            $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $stickyThreadConditions, $stickyThreadFetchOptions);

            // prepare all threads for the thread list
            $inlineModOptions = array();
            $permissions = $visitor->getNodePermissions($forumId);

            foreach ($stickyThreads AS &$thread)
            {
                $threadModOptions = $threadModel->addInlineModOptionToThread($thread, $forum, $permissions);
                $inlineModOptions += $threadModOptions;

                $thread = $threadModel->prepareThread($thread, $forum, $permissions);
            }
            unset($thread);

            // update parent
            $parent->params['stickyThreads'] = $stickyThreads;
        }

        // return parent
        return $parent;
    }
}
 
Last edited:
Wish I would have seen this earlier and paid attention to it. I already do almost this exact thing in a custom add-on:

Code:
public function actionForum()
{
    $parent = parent::actionForum();

    if(empty($parent->params['stickyThreads']))
    {
        $forum = $parent->params['forum'];
        $forumId = $forum['node_id'];
           
        $threadModel = $this->_getThreadModel();

        $displayConditions = $this->_getDisplayConditions($forum);
        list($defaultOrder, $defaultOrderDirection) = $this->_getDefaultThreadSort($forum);

        $order = $this->_input->filterSingle('order', XenForo_Input::STRING, array('default' => $defaultOrder));
        $orderDirection = $this->_input->filterSingle('direction', XenForo_Input::STRING, array('default' => $defaultOrderDirection));

        $fetchElements = $this->_getThreadFetchElements($forum, $displayConditions);
        $threadFetchConditions = $fetchElements['conditions'];

        $threadFetchOptions = $fetchElements['options'] + array(
                'order' => $order,
                'orderDirection' => $orderDirection
            );

        unset($threadFetchConditions['last_post_date']);

        $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $threadFetchConditions, $threadFetchOptions);
        $visitor = XenForo_Visitor::getInstance();

        $permissions = $visitor->getNodePermissions($forumId);
        $inlineModOptions = array();

        foreach ($stickyThreads AS &$thread)
        {
            $threadModOptions = $threadModel->addInlineModOptionToThread($thread, $forum, $permissions);
            $inlineModOptions += $threadModOptions;
            $thread = $threadModel->prepareThread($thread, $forum, $permissions);
        }

        $parent->params['stickyThreads'] = $stickyThreads;
    }

    return $parent;
}
 
Last edited:
Wish I would have seen this earlier and paid attention to it. I already do almost this exact thing in a custom add-on:

Code:
public function actionForum()
{
    $parent = parent::actionForum();

    if(empty($parent->params['stickyThreads']))
    {
        $forum = $parent->params['forum'];
        $forumId = $forum['node_id'];
          
        $threadModel = $this->_getThreadModel();

        $displayConditions = $this->_getDisplayConditions($forum);
        list($defaultOrder, $defaultOrderDirection) = $this->_getDefaultThreadSort($forum);

        $order = $this->_input->filterSingle('order', XenForo_Input::STRING, array('default' => $defaultOrder));
        $orderDirection = $this->_input->filterSingle('direction', XenForo_Input::STRING, array('default' => $defaultOrderDirection));

        $fetchElements = $this->_getThreadFetchElements($forum, $displayConditions);
        $threadFetchConditions = $fetchElements['conditions'];

        $threadFetchOptions = $fetchElements['options'] + array(
                'order' => $order,
                'orderDirection' => $orderDirection
            );

        unset($threadFetchConditions['last_post_date']);

        $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $threadFetchConditions, $threadFetchOptions);
        $visitor = XenForo_Visitor::getInstance();

        $permissions = $visitor->getNodePermissions($forumId);
        $inlineModOptions = array();

        foreach ($stickyThreads AS &$thread)
        {
            $threadModOptions = $threadModel->addInlineModOptionToThread($thread, $forum, $permissions);
            $inlineModOptions += $threadModOptions;
            $thread = $threadModel->prepareThread($thread, $forum, $permissions);
        }

        $parent->params['stickyThreads'] = $stickyThreads;
    }

    return $parent;
}

FWIW you'll probably want to add to $parent->params['inlineModOptions'] instead of creating a new array (that isn't even getting passed back into the View)
 
Wish I would have seen this earlier and paid attention to it. I already do almost this exact thing in a custom add-on:

Code:
public function actionForum()
{
    $parent = parent::actionForum();

    if(empty($parent->params['stickyThreads']))
    {
        $forum = $parent->params['forum'];
        $forumId = $forum['node_id'];
         
        $threadModel = $this->_getThreadModel();

        $displayConditions = $this->_getDisplayConditions($forum);
        list($defaultOrder, $defaultOrderDirection) = $this->_getDefaultThreadSort($forum);

        $order = $this->_input->filterSingle('order', XenForo_Input::STRING, array('default' => $defaultOrder));
        $orderDirection = $this->_input->filterSingle('direction', XenForo_Input::STRING, array('default' => $defaultOrderDirection));

        $fetchElements = $this->_getThreadFetchElements($forum, $displayConditions);
        $threadFetchConditions = $fetchElements['conditions'];

        $threadFetchOptions = $fetchElements['options'] + array(
                'order' => $order,
                'orderDirection' => $orderDirection
            );

        unset($threadFetchConditions['last_post_date']);

        $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $threadFetchConditions, $threadFetchOptions);
        $visitor = XenForo_Visitor::getInstance();

        $permissions = $visitor->getNodePermissions($forumId);
        $inlineModOptions = array();

        foreach ($stickyThreads AS &$thread)
        {
            $threadModOptions = $threadModel->addInlineModOptionToThread($thread, $forum, $permissions);
            $inlineModOptions += $threadModOptions;
            $thread = $threadModel->prepareThread($thread, $forum, $permissions);
        }

        $parent->params['stickyThreads'] = $stickyThreads;
    }

    return $parent;
}
$displayConditions, $order, and $orderDirection are already in the parent's view params.

It's worth noting that none of the examples so far have protected against $parent not actually being the expected view response! This is very important.

PHP:
$parent = parent::actionForum();

if (!($parent instanceof XenForo_ControllerResponse_View))
{
    return $parent;
}

// rest of the code ...

return $parent;
 
Thank you, snog, Jake and Chris. Your help is amazing.

This is what I currently have.

PHP:
<?php

class Andy_StickyAllPages_ControllerPublic_Forum extends XFCP_Andy_StickyAllPages_ControllerPublic_Forum
{
    public function actionForum()    
    {
        // get parent
        $parent = parent::actionForum();
        
        // return parent action if this is a redirect or other non View response 
        if (!$parent instanceof XenForo_ControllerResponse_View)
        {
            return $parent;
        }

        if (empty($parent->params['stickyThreads']))
        {    
            $forum = $parent->params['forum'];
            $forumId = $forum['node_id'];

            $threadModel = $this->_getThreadModel();

            $displayConditions = $parent->params['displayConditions'];
            list($defaultOrder, $defaultOrderDirection) = $this->_getDefaultThreadSort($forum);

            $fetchElements = $this->_getThreadFetchElements($forum, $displayConditions);
            $threadFetchConditions = $fetchElements['conditions'];

            $threadFetchOptions = $fetchElements['options'] + array(
                'order' => $forum['default_sort_order'],
                'orderDirection' => $forum['default_sort_direction']
            );

            unset($threadFetchConditions['last_post_date']);

            $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $threadFetchConditions, $threadFetchOptions);
            $visitor = XenForo_Visitor::getInstance();

            $permissions = $visitor->getNodePermissions($forumId);

            foreach ($stickyThreads AS &$thread)
            {
                $thread = $threadModel->prepareThread($thread, $forum, $permissions);
            }
            
            // update parent
            $parent->params['stickyThreads'] = $stickyThreads;
        }

        // return parent
        return $parent;
    }
}
 
Last edited:
FWIW you'll probably want to add to $parent->params['inlineModOptions'] instead of creating a new array (that isn't even getting passed back into the View)

Actually now that I've revisited it, the inlineModOptions aren't needed at all.

$displayConditions, $order, and $orderDirection are already in the parent's view params.

It's worth noting that none of the examples so far have protected against $parent not actually being the expected view response! This is very important.

PHP:
$parent = parent::actionForum();

if (!($parent instanceof XenForo_ControllerResponse_View))
{
    return $parent;
}

// rest of the code ...

return $parent;
Thanks Chis :)

While I've not had a problem without it, I'll take your advice. ;)

Here's my modified code:
Code:
public function actionForum()
{
    $parent = parent::actionForum();

    if (!($parent instanceof XenForo_ControllerResponse_View))
    {
        return $parent;
    }

    if(empty($parent->params['stickyThreads']))
    {
        $forum = $parent->params['forum'];
        $forumId = $forum['node_id'];

        $threadModel = $this->_getThreadModel();

        $fetchElements = $this->_getThreadFetchElements($forum, $parent->params['displayConditions']);
        $threadFetchConditions = $fetchElements['conditions'];

        $threadFetchOptions = $fetchElements['options'] + array(
                'order' => $parent->params['order'],
                'orderDirection' => $parent->params['orderDirection']
            );

        unset($threadFetchConditions['last_post_date']);

        $stickyThreads = $threadModel->getStickyThreadsInForum($forumId, $threadFetchConditions, $threadFetchOptions);
        $visitor = XenForo_Visitor::getInstance();

        $permissions = $visitor->getNodePermissions($forumId);

        foreach ($stickyThreads AS &$thread)
        {
            $thread = $threadModel->prepareThread($thread, $forum, $permissions);
        }

        $parent->params['stickyThreads'] = $stickyThreads;
    }

    return $parent;
}
 
Last edited:
It's worth noting that none of the examples so far have protected against $parent not actually being the expected view response! This is very important.
Nor have any of them protected against the forum/thread object not being set because of another add-on returned a custom view object (one of the anti-DDOS add-on's does this). When dealing with code which can be extended by others, it is always good to check your assumptions.

For example:
Code:
    if (!($parent instanceof XenForo_ControllerResponse_View) || empty($parent->params['forum']))
    {
        return $parent;
    }
 
Nor have any of them protected against the forum/thread object not being set because of another add-on returned a custom view object (one of the anti-DDOS add-on's does this). When dealing with code which can be extended by others, it is always good to check your assumptions.

For example:
Code:
    if (!($parent instanceof XenForo_ControllerResponse_View) || empty($parent->params['forum']))
    {
        return $parent;
    }
I would have to disagree with this.

If another add-on does not return the expected params for the actionForum function, then it is potentially breaking every add-on that might extend that function.

In this particular case, the params are simply being modified but all are still returned in the parent.

I guess it's splitting hairs as to who would be at fault. This add-on for not checking the value of forum or the other add-on for not supplying it? My vote would be for the one not supplying it.
 
Last edited:
Nor have any of them protected against the forum/thread object not being set because of another add-on returned a custom view object (one of the anti-DDOS add-on's does this)

Someone really shouldn't be removing expected parameters from a controller response in the first place. There are much better ways to get the same effect.
 
Top Bottom