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

It is also possible, that post_id is not delivered by the route /posts/23123/, but maybe you have to put post_id in the html form submit like
PHP:
<input type="hidden" name="post_id" value ="{$postId}" />

Edit: I don't think that's the case, as the edit form field has a link like "<form action='/posts/234234/changedate'", right?
PHP:
<form action="{xen:link 'posts/changedatesave', $post}" method="post" class="xenForm AutoValidator" data-redirect="on">
should do that
 
Last edited:
It is important which part of the addon code is giving you this error. It's also important to know the variables and if there is everything right. If ZEND_DEBUG::dump does not work for you, echo can be of help.

I guess the first datawriter works and this error is for the second datawriter. But you would have to check it with putting lots of "echo 'I am here';" in your code :)
 
Look at your code this is problem:
PHP:
 $dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
        $dw->setExistingData('post_id', $postId);
        $dw->set('date', $newDate);
        $dw->save();

Replace by:
PHP:
$post = $this->getModelFromCache('XenForo_Model_Post')->getPostById($postId);

 $dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
if($post)
{
        $dw->setExistingData($post);
}
        $dw->set('date', $newDate);
        $dw->save();
 
Hi Nobita.Kun,

Thank you kindly for your code, it works perfectly. Here's the code I used based on your tips.

PHP:
$post = $this->getModelFromCache('XenForo_Model_Post')->getPostById($postId);

$dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');

if ($post)
{
  $dw->setExistingData($post);
}

$dw->set('post_date', $dateline);
$dw->save();

So at this point the new post date is saved. The other code is still needed to update the position field and other database values need to be updated.

The code you suggested, I will assume is the best way to update the post_date, but I'll see if any other programmers chime in with another solution.
 
You are rewriting the whole post again with the DataWriter. It is more useful to only manipulate post_date with this code
PHP:
$dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
$dw->setExistingData( $postId); // without 'post_id'
$dw->set('date', $newDate);
$dw->save();

Did you check the counters are not updated? XenForo_DataWriter_Discussion::updateCountersAfterMessageSave might be executed in post_save state.
 
Thank you, Marcus. Your code works perfect. Here is the code I used to update the post_date.

PHP:
$dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
$dw->setExistingData($postId);
$dw->set('post_date', $dateline);
$dw->save();

I will now check if the position and other data is updated.
 
Hi Marcus,

It looks like the only change made is the xf_post.post_date. So we will need to add more DataWriter code to update the xf_post.position of each post in the thread.

Marcus, your code works perfect. The thread starter and last post data is updated with the latest information.
 
Last edited:
With the help from Marcus, the add-on is now using the DataWriter to update the database.

library/Andy/ChangeDate/ControllerPublic/Post.php

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(new XenForo_Phrase('date_missing'));
     }
     
     // get input text from template
     $newPostTime = $this->_input->filterSingle('new_post_time', XenForo_Input::STRING);
     
     if ($newPostTime == '')
     {
       return $this->responseError(new XenForo_Phrase('time_missing'));
     }     
     
     // convert to unix timestamp
     date_default_timezone_set('America/Los_Angeles');
     $date = $newPostDate . ' ' . $newPostTime;
     $dateline = strtotime($date);
     
     if ($dateline == '')
     {
       return $this->responseError(new XenForo_Phrase('date_or_time_format_incorrect'));
     }
     
     //########################################
     // start database operations
     //########################################
     
     // update the xf_post.post_date
     $dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
     $dw->setExistingData($postId);
     $dw->set('post_date', $dateline);
     $dw->save();
     
     // update the xf_post.position for each post in the thread
     // also updates other tables with first post and last post information
     $threadId = $dw->get('thread_id');
     $dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
     $dw->setExistingData($threadId);
     $dw->rebuildDiscussion();
     $dw->save();
     
     //########################################
     // return with response redirect
     //########################################
     
     // this variable is needed 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');       
   }
}

?>
 
Nice!

You could directly assign this:
$post['post_id'] = $this->_input->filterSingle('post_id', XenForo_Input::UINT);
You would have to put in $post['post_id'] in the DataWriter instead of $postId

I think you can also remove $dw->save; from the second DataWriter, you don't save the Discussion, and the rebuildCounters are already set.
 
You also shouldn't be instructing the server as to which time zone it should use.

Hi Jeremy,

Thank you for reviewing my code.

When I omit that line of code the post date is off by seven hours. Is there a better solution?
 
You could directly assign this:
$post['post_id'] = $this->_input->filterSingle('post_id', XenForo_Input::UINT);
You would have to put in $post['post_id'] in the DataWriter instead of $postId

Unfortunately I don't understand your suggestion here.
 
Something like this
PHP:
  // if you are not a super admin, you will get an error
  if (!XenForo_Visitor::getInstance()->isSuperAdmin())
  {
  return;
  }

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

  // get input text from template
  if (!$newPostDate = $this->_input->filterSingle('new_post_date', XenForo_Input::STRING))
  {
  return $this->responseError(new XenForo_Phrase('date_missing'));
  }

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

  if ($newPostTime = $this->_input->filterSingle('new_post_time', XenForo_Input::STRING))
  {
  return $this->responseError(new XenForo_Phrase('time_missing'));
  }

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

  if ($dateline == '')
  {
  return $this->responseError(new XenForo_Phrase('date_or_time_format_incorrect'));
  }

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

  // update the xf_post.post_date
  $dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
  $dw->setExistingData($post['post_id']);
  $dw->set('post_date', $dateline);
  $dw->save();

First $postId = .... then $post['post_id'] = $postId is a bit complicated :) In my suggestion here the line $post['post_id'] = $postId can be removed as you fetch the post_id directly like this $post['post_id'] = $this->_input->filterSingle('post_id', XenForo_Input::UINT);
 
If you want to make your date change template more beautiful, you can put all post contents inside it like title and textMessage:
PHP:
...
$post = $this->getModelFromCache('XenForo_Model_Post')->getPostById($postId);
...

PHP:
  public function actionChangedate()
  {
  $postId = $this->_input->filterSingle('post_id', XenForo_Input::UINT);

  $post = $this->getModelFromCache('XenForo_Model_Post')->getPostById($postId);

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

  if (XenForo_Visitor::getInstance()->isSuperAdmin())
  {
  return $this->responseView('Andy_DateChange_ViewPublic_Post','andy_changedate',$viewParams);
  }
  }
Now your template has access to {$post.title} and {$post.thread_id} and all the other informations from $post :)
 
Something like this...

I made the changes. The code is looking great.

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;
     }
     
     $post['post_id'] = $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(new XenForo_Phrase('date_missing'));
     }
     
     // get input text from template
     $newPostTime = $this->_input->filterSingle('new_post_time', XenForo_Input::STRING);
     
     if ($newPostTime == '')
     {
       return $this->responseError(new XenForo_Phrase('time_missing'));
     }     
     
     // convert to unix timestamp
     date_default_timezone_set('America/Los_Angeles');
     $date = $newPostDate . ' ' . $newPostTime;
     $dateline = strtotime($date);
     
     if ($dateline == '')
     {
       return $this->responseError(new XenForo_Phrase('date_or_time_format_incorrect'));
     }
     
     //########################################
     // start database operations
     //########################################
     
     // update the xf_post.post_date
     $dw = XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
     $dw->setExistingData($post['post_id']);
     $dw->set('post_date', $dateline);
     $dw->save();
     
     // update the xf_post.position for each post in the thread
     // also updates other tables with first post and last post information
     $threadId = $dw->get('thread_id');
     $dw = XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
     $dw->setExistingData($threadId);
     $dw->rebuildDiscussion();
     $dw->save();
     
     //########################################
     // return with response redirect
     //########################################
         
     return $this->responseRedirect(
     XenForo_ControllerResponse_Redirect::SUCCESS,
     XenForo_Link::buildPublicLink('posts', $post),'Post Date Changed');       
   }
}

?>
 
If you want to make your date change template more beautiful...

That's good to know, but I think the form displays just enough information. Adding more information like the thread title might actually be confusing.
 
Top Bottom