how to avoid rewritting the parent method on extended class?

DarkSign

Active member
Hello XF developers! I am a new coder, so I hope I won't bother you guys so much. I already have read almost all threads here on the dev section.

So, I am writting an add-on that will auto-lock a thread after X posts. I probably am doing it wrong, because I just want to add my custom code on the extended class, and use the parent method to actually save the data.

Here is my try at coding it.

Code:
<?php
class DS_AutoLockThreads_ControllerPublic_AutoLockThreads extends XFCP_DS_AutoLockThreads_ControllerPublic_AutoLockThreads
{
    public function actionAddReply()
    {
        $threadOpened = $this->_input->filterSingle('discussion_open', XenForo_Input::UINT); //checking if the thread is locked
        if ($threadOpened) //1 for Locked, so that returns true here
        {
            //no reason to continue, throw Exception
            return $this->responseNoPermission();
        }
        
        //extracting the Options
        $Options = XenForo_Application::get('options');
        
        
    $this->_assertPostOnly();

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

        $threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        
        //Getting the Model to check the last post
        $limitPost = $this->_getPostModel()->getLastPostInThread($threadId);
        
        $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->preSave();

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

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

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

        $threadUpdateData = array();
        
        if (($limitPost['position']+2 >= $Options->maxPosts) && ($threadId == $Options->threadId))
        {
            //For some reason, I need to use that +2 hack, so it actually works
            
            //Here is the trick! We lock the thread if the requirements are ok
            $input['_set']['discussion_open'] = 1;
        }
            if (!empty($input['_set']['discussion_open']))
        {
            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->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, $visitor['user_id']);

            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
            $threadModel = $this->_getThreadModel();
            $postModel = $this->_getPostModel();

            // the max number of posts we want to fetch
            $limit = 3;

            $postPermissionOptions = $postModel->getPermissionBasedPostFetchOptions($thread, $forum);
            $postFetchOptions = $postPermissionOptions + array(
                'limit' => ($limit + 1),
                'join' => XenForo_Model_Post::FETCH_USER | XenForo_Model_Post::FETCH_USER_PROFILE,
            );
            if (!empty($postPermissionOptions['deleted']))
            {
                $postFetchOptions['join'] |= XenForo_Model_Post::FETCH_DELETION_LOG;
            }

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

            $posts = $postModel->getNewestPostsInThreadAfterDate(
                $threadId, $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)
            {
                $firstUnshownPost = $postModel->getNextPostInThread($threadId, $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, $visitor['user_id']);
                }
            }

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

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

as you can see, I had to rewrite all the parent's method on that extended class. It does the job, but... is it really necessary?

Oh, any other comments on my code is welcomed. I know I am not doing it properly, but again, I am learning PHP OO, and getting help using the XF code itself.

And as it is now, it only locks 1 thread. I want to extend it to use a custom template that will add the checkbox 'Auto lock after' and a text box to put the maxium posts allowed, into that Thread Tools for admins and mods only. So any advice on how to do it would be nice too.

Thank you for your time.
 
The following woud be a good start for extending that function.
First let the member post, then check if count has reached your limit.
You might also want to consider redirecting the user after the inline post so they will know the thread has been locked.

PHP:
    public function actionAddReply()
    {
        $redirect = parent::actionAddReply();

        $threadId = $this->_input->filterSingle('thread_id', XenForo_Input::UINT);
        $options = XenForo_Application::get('options');
        if ($threadId == $options->AutoLockThreads_thread_id)
        {
            $postModel = $this->_getPostModel();
            $numPosts = $postModel->countVisiblePostsInThread($threadId);

            if ($numPosts >= $options->AutoLockThreads_count_limit)
            {
                $visitorId = XenForo_Visitor::getUserId();

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

                $threadWriter = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
                $threadWriter->setExistingData($thread['thread_id']);
                $threadWriter->set('discussion_open', 0);
                $threadWriter->save();

                $this->_getThreadModel()->markThreadRead($thread, $forum, XenForo_Application::$time, $visitorId);

            }
        }

        return $redirect;
    }
 
$numPosts = $postModel->countVisiblePostsInThread($threadId);

That sure helps! I didn't think about that I could have non-visible posts being counted. (y)

$ftpHelper = $this->getHelper('ForumThreadPost');
$threadFetchOptions = array('readUserId' => $visitorId);
$forumFetchOptions = array('readUserId' => $visitorId);
list($thread, $forum) = $ftpHelper->assertThreadValidAndViewable($threadId, $threadFetchOptions, $forumFetchOptions);
I tried to understand that Helper, but I really am lost on it. :( Some more explanation would be fine

$threadWriter->set('discussion_open', 0);

Using the Writer is really cool. I think I need to learn more about it. Just pointing that the discussion_open should be 1 to lock it. ;) Doing a test I really should had tested before posting. So, why I need to put 0 there, and on my code I need to put 1?

So far, so good! It looks way better! Thank you for help! I will test it! :D

UPDATE: It works! So bad that all my first add-on trying was rewritted, but living and learning! I will give you credits for the function.

Now it works for only one thread, like I said, so it's time to move on to learn templating and how to put a checkbox and a edit box in the Thread Tools hehehe.

Thank you very much!
 
That helper fetches the thread and forum information if the visitor has proper viewing permissions.
You don't really need it except to mark the thread as read.

The above code is just a quick example.

...Just pointing that the discussion_open should be 1 to lock it
No, zero (0) is locked.
 
if that was just a quick example, then I really need to learn lots more :)

Haha, so I was doing something really wrong, because setting that array with 1 made the thread being locked someway.

Your "quick example" worked like a charm! It actually does what I needed.

You might also want to consider redirecting the user after the inline post so they will know the thread has been locked.
I didn't get this part...
 
I didn't get this part...
Usually when you make a reply, Xenforo doesn't refresh the page. Instead the new post got added to the bottom using AJAX. So the user will never know if the thread got closed. In which case Syndol suggests you redirect the whole page.
 
Oh I understood now. I made some tests and it is working nice, throwing the exception even with the AJAX thing, so I don't think it's really necessary.
 
And as it is now, it only locks 1 thread. I want to extend it to use a custom template that will add the checkbox 'Auto lock after' and a text box to put the maxium posts allowed, into that Thread Tools for admins and mods only. So any advice on how to do it would be nice too.

To check for multiple thread id's, have the add-on options save a string with the ids as "1,4,3,8"
Then in the code use:
PHP:
if (in_array($threadId, explode(',', $options->AutoLockThreads_thread_ids)))
{

}

In regards to including options for admins at time of thread creation see template 'thread_create'
You will need to use hook 'thread_create_fields_extra' to add an option such as the ones in template 'thread_fields_status'

As well as set the mod usergroup permission in the admin control panel under users - Permission Definitions
 
Top Bottom