[solved] External form is using wrong controller

Permalink
I have a weird problem.

I have one external form ("dealer_contact_form.php") in a global area. Said global area is found on about 30 pages.

At one point, the form on one of the pages started using the controller of another form ("general_contact_form.php") when submitting the form. I noticed this because the controllers expect different fields and this caused an error message, which was displayed in the logs along with the filename of the controller.

I tried all kinds of things and the problem disappeared. I don't know why.

A couple of days ago I added a new external form ("eflyer_contact_form.php") and similar problem appeared on another page. Now on one of the pages the form in the global area is trying to use the controller of this new form when submitting. Again, this causes an error on this page, which revealed the situation.

Notes:
- Like I wrote, the form is in a global area but only some of the pages are affected.
- It would seem that only the form in the global area is affected and not the others.
- The correct controller is used when the form is displayed. The wrong controller is used only after submission.
- All the form controllers extend a custom controller, but it makes no reference to controller filenames anywhere.
- I have tried clearing the cache.
- I have tried updating the block (edit & save).
- The filenames in the btExternalForm table seem to be correct.
- Concrete5 version is 5.7.5.13

Does anyone have any idea what might be going on or how I can start to debug this?

Someone is going to ask, so here is the code for the action that the form in the global area uses when submitting:

public function action_send_message(){
   // This method can be triggered without posting the form, so check that
   if ( !$this->isPost() ) {
      return;
   }
   $this->validate();
   if ( count( $this->errors ) === 0 ) {
      if ( $this->send() ) {
         if ( !$this->hasPageController() ) {
            // Standalone form
            $this->redirect( Page::getCurrentPage()->getCollectionPath(), 'email_sent' );
         } else {
            // Form is controlled externally
            $this->isSuccess = true;
            $this->set( 'response', Config::get( $this->thankYouMessage ) );


Any help is greatly appreciated.

Juha
 
Gondwana replied on at Permalink Reply
Gondwana
Have a look at the source that c5 generates for the problematic page, and see if you can spot the block IDs (bID) for the miscreant blocks. If they ain't unique, that could be the problem.
Juha replied on at Permalink Reply
Juha
The global area block and the block for the new form both have different IDs.
Juha replied on at Permalink Best Answer Reply
Juha
Ok, I figured it out.

After investigating this more thoroughly, I noticed that the wrong controller is used only when the form controller does a redirect after a successful post. The redirect is triggered from an action method, action_email_sent().

Each of my external form controllers extends a base class, FormController. Since each form does the same thing after a successful post, I thought it would be a good idea to have the action_email_sent() method in the base class, which basically means that it was found in each instance of the controller.

This wouldn't be problem except for the forms in global areas. When the page controller checks for valid actions, it looks through each block on the page and through each global block, even if they are not on that page (see \Page\Collection::getGlobalBlocks()). When the page controller finds a block with a valid action, it stops looking. So when multiple blocks have similarly named actions, you get the first one that matches, but not necessarily the one you want.

In retrospect, this is reasonable and probably should have occurred to me earlier.

Because all the external form controllers on my site had the same action and I had one form in a global area (now two), it meant that there was always at least two blocks on that list.

The solution, then, was to make sure that each external form controller uses uniquely named action methods, so that there is always only one possible match.

I'm still not quite sure why this problem wasn't manifesting itself on every page, though. Probably has something to do with how the database query results are sorted.