• This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.

The correct way of creating a custom POST Request page (like add-thread)


Well-known member
When you look at something like the "Create Thread" button

It sends a POST request to "forums/development-tutorials-and-resources.xx/add-thread"

This reqest page seems to be able to deal with the Header before the html is rendered (it can do a php redirect depending on the response from the POST)

This "add-thread" doesn't seem to exist as a file anywhere, and I'm not sure where it is (so I cant use it as an example to learn from). What's the correct method of creating the "add-thread" page to capture and "deal" with a POST request within XenForo?

I could just create my own independent "add-thead.php" file and deal with any post request there, but I would like to use XenForo functionality / do it the correct way

Are there any tutorials that deal with custom POST/GET requests?


Well-known member
Thanks ragtek, unfortunately I'm still stuck, is there any documentation I can look at related to this?

I may not have been clear to start with, but the POST request I want to do is from an external xenfor page that I created using this tutorial:

What the "Create Thread" button usually does is:

<form action="{xen:link 'forums/add-thread', $forum}" method="post"
class="xenForm Preview AutoValidator"
data-previewUrl="{xen:link 'forums/create-thread/preview', $forum}"
<input type="text" name="myparam" value="default" />

I'm not sure how it gets from forums/add-thread to ControllerPublic_Forum_actionAddThread (presumably listeners / routes, but I haven't managed to find this particular one in the ACP yet)

If I can figure that out, I can send a post request to an 'external' page that then updated the db and redirects to wherever I want depending on the response ( I want to avoid adding any template info / routing to a custom template since I want to update the header using the request page)

If I wanted to use my own post request "Do This", inside my external page, I would use
... {xen:link 'forums/do-this', $forum} method="post" ...

I'm then a bit lost at how I map my 'forums/do-this' to the request page

Okay, I may not need this. If I can send a post request, and be absoultely sure that the url of that post request is the same place for all peoples installs, then I can just send the request to a custom php file

This issue is that the external pages often contain the index.php? in the url
so <xen:link full:etc will link to myforum/index.php?ect instead of myforum/ect
If there is a command like "<xen:link ful"l that just returns the forum url, then this solves my issue

I've asked this question here:


Well-known member
Okay.. I'm still back at this porblem.

I'm still not sure about the magic from getting from:

<form action="{xen:link 'forums/add-thread', $forum}" method="post"
class="xenForm Preview AutoValidator"
data-previewUrl="{xen:link 'forums/create-thread/preview', $forum}"
<input type="text" name="myparam" value="default" />

to the file ControllerPublic_Forum_actionAddThread . Why is it sent to this file, and how can you tell (apart from the names sounding similar)
I've written some nasty hacks to work around my lack of knowledge in this area, the plug-in works, but I'm now trying to figure out how to do it the right way... As yet, I have no idea.

From what I can dig into, (trying to work forwards, and backwards to point out my gap in knowledge)

1) On clicking the create thread buttons, all of the hidden (and visible) params get submitted (via POST), and they are sent to: "forums/add-thread"

title=test&message_html=<p>test</p>&_xfRelativeResolver=http//www.yourforum.co.uk/forums/forumname/create-thread&attachment_hash=<some uuid> &watch_thread_state=1&discussion_open=1&_set[discussion_open]=1&_set[sticky]=1&poll[question]=&poll[responses][]=&poll[responses][]=&_xfToken=<a bunch of uuids>&_xfRequestUri=/forums/forumname /create-thread&_xfNoRedirect=1&_xfToken=<a bunch of uuids>&_xfResponseType=json

(on creation .. so this should be at the end) The JSON response is:
{"_redirectStatus":"ok","_redirectTarget":"\/threads\/test.196\/","_redirectMessage":"Your thread has been posted."}

2) forums/add-thread must have some sort of mechanism to get to the required class, after all, there is no such file as "forums/add-thread" ... so where exactly does this request go? It must go somewhere

3)... <gap in knowledge>
4)... <gap in knowledge>
5)... <gap in knowledge>

6) the public function actionAddThread() is found in the file Forum.php, with a Class and location: XenForo_ControllerPublic_Forum, this class extends XenForo_ControllerPublic_Abstract

7) This function actionAddThread() grabs all of the params and puts them into a filtered array:

$input = $this->_input->filter(array(

'title' => XenForo_Input::STRING,

'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,

'poll' => XenForo_Input::ARRAY_SIMPLE, // filtered below

it seems to do this by using " $this->_input"

8) XenForo_ControllerPublic_Abstract extends XenForo_Controller

9) _input is a protected object of XenForo_Controller
(some how, in step 3/4/5 all the POST data has already been added to this _input object)

10) This then all gets stuffed into a few objects, errors are checked and this all get stuffed into the $writer object (sorry for over simplifying), if no errors, this gets saved

and a response is returned:

return $this->responseRedirect(



new XenForo_Phrase('your_thread_has_been_posted')

11) At this point, the response is sent back to the form (who is sitting patiently, waiting for an ajax response) and the response is handled accordingly (it returns to the return target as defined in point 1)
All this to say, I don't understand when a GET/POST is submitted, how it is possible to work out where it goes, and how the protected _input is populated (and hence I cant build my own GET/POST handler right now)