How do I create an add-on that allows a user to click a link and call php code?

Replace $threadId = $thread_id?

I tried that, but I still get the same error.

PHP:
$db->fetchAllKeyed('
SELECT post_id
        FROM xf_post
        WHERE thread_id= ?
        ORDER BY post_date ASC
', 'post_id', $thread_id);

Question, what does the ? mark do? I see that in the Xenforo code but never understood it.
 
This works.

PHP:
		$query = $db->fetchAll("
		SELECT post_id
		FROM xf_post
		WHERE thread_id='$thread_id'
		ORDER BY post_date ASC");
		
		foreach($query AS $rowName => $results)
		{
			$post_id[] = $results['post_id'];
		}
 
I tried that, but I still get the same error.

PHP:
$db->fetchAllKeyed('
SELECT post_id
        FROM xf_post
        WHERE thread_id= ?
        ORDER BY post_date ASC
', 'post_id', $thread_id);

Question, what does the ? mark do? I see that in the Xenforo code but never understood it.
Where code you put it? At ControllerPublic or Model? If this is at ControllerPublic so maybe this have problems, I not testing with $db->fetchAllKeyed on ControllerPublic. And you can give more information you get?? Check on sever errors log
 
I have completed the Post.php portion of the code. The add-on at this point will successfully change the post date and position of any post you choose in a thread. Additionally the xf_thread table will be updated so when a visitor is viewing the Thread View page the thread starter and last post information are correct.

Here's the code.

PHP:
<?php

class Andy_ChangeDate_ControllerPublic_Post extends XFCP_Andy_ChangeDate_ControllerPublic_Post
{
    public function actionChangedate()
    {
        $post['post_id'] = $this->_input->filterSingle('post_id', XenForo_Input::UINT);

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

        if (XenForo_Visitor::getInstance()->isSuperAdmin())
        {
            return $this->responseView('Andy_DateChange_ViewPublic_Post','andy_changedate',$viewParams);
        }
    }

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

        // if you are not a super admin, you will get an error
        if (!XenForo_Visitor::getInstance()->isSuperAdmin())
        {
            return;
        }

        $postId= $this->_input->filterSingle('post_id', XenForo_Input::UINT);

        // get input text from template
        $newPostDate = $this->_input->filterSingle('new_post_date', XenForo_Input::STRING);

        if ($newPostDate == '')
        {
            return $this->responseError('Date Missing');
        }

        // get input text from template
        $newPostTime = $this->_input->filterSingle('new_post_time', XenForo_Input::STRING);

        if ($newPostTime == '')
        {
            return $this->responseError('Time Missing');
        }

        // convert to unix timestamp
        date_default_timezone_set('America/Los_Angeles');
        $date = $newPostDate . ' ' . $newPostTime;
        $dateline = strtotime($date);

        if ($dateline == '')
        {
            return $this->responseError('Date or Time format incorrect');
        }

        //########################################
        // start database operations
        //########################################

        $db = XenForo_Application::getDb();

        //########################################
        // get thread_id

        $query = "
        SELECT thread_id
        FROM xf_post
        WHERE post_id='$postId'";

        $thread_id = XenForo_Application::get('db')->fetchOne($query);

        if ($thread_id == '')
        {
            return $this->responseError('Invalid post number in URL');
        }

        //########################################
        // update xf_post

        $db->query("
        UPDATE xf_post
        SET post_date='$dateline'
        WHERE post_id='$postId'
        LIMIT 1");

        //#####################################
        // get every post_id in thread

        $query = $db->fetchAll("
        SELECT post_id
        FROM xf_post
        WHERE thread_id='$thread_id'
        ORDER BY post_date ASC");

        foreach($query AS $rowName => $results)
        {
            $post_id[] = $results['post_id'];
        }

        $count = count($post_id);

        //#####################################
        // update xf_post.position for each post

        for ($i=0; $i<$count; $i++)
        {
            $db->query("
            UPDATE xf_post
            SET position='$i'
            WHERE post_id='$post_id[$i]'
            LIMIT 1");
        }

        //#####################################
        // update xf_thread first post info

        $query = $db->fetchAll("
        SELECT user_id, username
        FROM xf_post
        WHERE thread_id='$thread_id'
        AND position='0'
        LIMIT 1");

        foreach($query AS $rowName => $results)
        {
            $user_id = $results['user_id'];
            $username = $results['username'];
        }

        $db->query("
        UPDATE xf_thread
        SET user_id='$user_id', username='$username'
        WHERE thread_id='$thread_id'
        LIMIT 1");

        //#####################################
        // update xf_thread last post info

        $last_position = $i - 1;

        $query = $db->fetchAll("
        SELECT post_date, post_id, user_id, username
        FROM xf_post
        WHERE thread_id='$thread_id'
        AND position='$last_position'
        LIMIT 1");

        foreach($query AS $rowName => $results)
        {
            $last_post_date = $results['post_date'];
            $last_post_id = $results['post_id'];
            $last_post_user_id = $results['user_id'];
            $last_post_username = $results['username'];
        }

        $db->query("
        UPDATE xf_thread
        SET last_post_date='$last_post_date', last_post_id='$last_post_id', last_post_user_id='$last_post_user_id', last_post_username='$last_post_username'
        WHERE thread_id='$thread_id'
        LIMIT 1");

        // this variable is needed for the line below (XenForo_Link::buildPublicLink)
        $post['post_id'] = $postId;
   
        return $this->responseRedirect(
        XenForo_ControllerResponse_Redirect::SUCCESS,
        XenForo_Link::buildPublicLink('posts', $post),'Post Date Changed');
    }
}

?>
 
Last edited:
This add-on is now completed and I've sent it to Marcus for testing. Once it's verified to work properly I'll add it to the Resources.

I would like to thank the following people who helped me today on this add-on, you guys are all awesome!

Chris Deeming
Daniel Hood
Jake Bunce
Jeremy
Marcus
Nobita.Kun
 
I have completed the Post.php portion of the code. The add-on at this point will successfully change the post date and position of any post you choose in a thread. Additionally the xf_thread table will be updated so when a visitor is viewing the Thread View page the thread starter and last post information are correct.

Here's the code.

PHP:
<?php

class Andy_ChangeDate_ControllerPublic_Post extends XFCP_Andy_ChangeDate_ControllerPublic_Post
{
    public function actionChangedate()
    {
        $post['post_id'] = $this->_input->filterSingle('post_id', XenForo_Input::UINT);

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

        if (XenForo_Visitor::getInstance()->isSuperAdmin())
        {
            return $this->responseView('Andy_DateChange_ViewPublic_Post','andy_changedate',$viewParams);
        }
    }

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

        // if you are not a super admin, you will get an error
        if (!XenForo_Visitor::getInstance()->isSuperAdmin())
        {
            return;
        }

        $postId= $this->_input->filterSingle('post_id', XenForo_Input::UINT);

        // get input text from template
        $newPostDate = $this->_input->filterSingle('new_post_date', XenForo_Input::STRING);

        if ($newPostDate == '')
        {
            return $this->responseError('Date Missing');
        }

        // get input text from template
        $newPostTime = $this->_input->filterSingle('new_post_time', XenForo_Input::STRING);

        if ($newPostTime == '')
        {
            return $this->responseError('Time Missing');
        }

        // convert to unix timestamp
        date_default_timezone_set('America/Los_Angeles');
        $date = $newPostDate . ' ' . $newPostTime;
        $dateline = strtotime($date);

        if ($dateline == '')
        {
            return $this->responseError('Date or Time format incorrect');
        }

        //########################################
        // start database operations
        //########################################

        $db = XenForo_Application::getDb();

        //########################################
        // get thread_id

        $query = "
        SELECT thread_id
        FROM xf_post
        WHERE post_id='$postId'";

        $thread_id = XenForo_Application::get('db')->fetchOne($query);

        if ($thread_id == '')
        {
            return $this->responseError('Invalid post number in URL');
        }

        //########################################
        // update xf_post

        $db->query("
        UPDATE xf_post
        SET post_date='$dateline'
        WHERE post_id='$postId'
        LIMIT 1");

        //#####################################
        // get every post_id in thread

        $query = $db->fetchAll("
        SELECT post_id
        FROM xf_post
        WHERE thread_id='$thread_id'
        ORDER BY post_date ASC");

        foreach($query AS $rowName => $results)
        {
            $post_id[] = $results['post_id'];
        }

        $count = count($post_id);

        //#####################################
        // update xf_post.position for each post

        for ($i=0; $i<$count; $i++)
        {
            $db->query("
            UPDATE xf_post
            SET position='$i'
            WHERE post_id='$post_id[$i]'
            LIMIT 1");
        }

        //#####################################
        // update xf_thread first post info

        $query = $db->fetchAll("
        SELECT user_id, username
        FROM xf_post
        WHERE thread_id='$thread_id'
        AND position='0'
        LIMIT 1");

        foreach($query AS $rowName => $results)
        {
            $user_id = $results['user_id'];
            $username = $results['username'];
        }

        $db->query("
        UPDATE xf_thread
        SET user_id='$user_id', username='$username'
        WHERE thread_id='$thread_id'
        LIMIT 1");

        //#####################################
        // update xf_thread last post info

        $last_position = $i - 1;

        $query = $db->fetchAll("
        SELECT post_date, post_id, user_id, username
        FROM xf_post
        WHERE thread_id='$thread_id'
        AND position='$last_position'
        LIMIT 1");

        foreach($query AS $rowName => $results)
        {
            $last_post_date = $results['post_date'];
            $last_post_id = $results['post_id'];
            $last_post_user_id = $results['user_id'];
            $last_post_username = $results['username'];
        }

        $db->query("
        UPDATE xf_thread
        SET last_post_date='$last_post_date', last_post_id='$last_post_id', last_post_user_id='$last_post_user_id', last_post_username='$last_post_username'
        WHERE thread_id='$thread_id'
        LIMIT 1");

        // this variable is needed for the line below (XenForo_Link::buildPublicLink)
        $post['post_id'] = $postId;
  
        return $this->responseRedirect(
        XenForo_ControllerResponse_Redirect::SUCCESS,
        XenForo_Link::buildPublicLink('posts', $post),'Post Date Changed');
    }
}

?>
For each query should be move on Model ;)
As I see that require more querries :oops:
SOme performance for you:
PHP:
return $this->responseError('Date Missing');
Replace by:
PHP:
return $this->responseError(new XenForo_Phrase('date_missing'));
PHP:
if ($dateline == '')
        {
            return $this->responseError('Date or Time format incorrect');
        }
Should be:
PHP:
if ($dateline == '')
        {
            return $this->responseError(new XenForo_Phrase('date_or_time_format_incorrect'));
        }
PHP:
 if ($thread_id == '')
        {
            return $this->responseError('Invalid post number in URL');
        }
Should be:
PHP:
 if ($thread_id == '')
        {
            return $this->responseError(new XenForo_Phrase('invalid_post_number_in_url'));
        }
 
Replace by:
PHP:
return $this->responseError(new XenForo_Phrase('date_missing'));

I assume the reason for doing it this way is so that it's easy to create a phrase and change it to another language or description.

If I wanted to include phrases with the add-on, how is that done?
 
My next question is in regards to the location of database queries. Currently all my db queries are located in the Post.php file. But I understand the proper place for db related code needs to be in the Model.php file. So the first thing I did was create the proper folder and file.

pic001.webp

Now what. I have no idea how to proceed at this point to move the queries from Post.php to Model.php.
 
Last edited:
If all your queries are related to Posts, they should in theory be contained within the post model. Models are extended the same way as the controllers via the listener system and class proxies.
 
Thank you, Jeremy.

Yes all the queries are related to posts. It's good to know I can keep the queries in the Post.php file.
 
Last edited:
You misunderstand me. Queries should be contained within a model in a proper MVC application (or a DataWriter to update the fields in the database if we want to get real technical here). Extending the model (to put them in XenForo_Model_Post in essence) works identical to extending controllers.
 
You can create another listener for load_class_model and extend XenForo_Model_Post (you write that as the hint):

method: load_class_model
hint: XenForo_Model_Post
your Listener: Andy_ChangeDate_Listener:: loadClassModel

If you extend this model, you can use all its functions in your own file Andy/ChangeDate/Model/Post.php (class) you create. One great function is this from XenForo_Model_Post:

PHP:
/**
    * Recalculates the position of all posts in the thread, and returns info relating
    * to the thread: firstPostId, firstPostDate, firstPostState, lastPostId, visibleCount.
    *
    * @param integer $threadId
    *
    * @return array
    */
   public function recalculatePostPositionsInThread($threadId)
   {

So actually all you have to do to recalculate your posts is
PHP:
$this->recalculatePostPositionsInThread($threadId);

Easy, isn't it? :D
 
Last edited:
Top Bottom