Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Archive /

Our old forums are still available as a read-only archive.

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo

infinite loop in form validator because of redirectBack


Go to End


2008 Views

Avatar
jahbini

Community Member, 13 Posts

29 August 2008 at 5:53am

This post might come under the general heading of "best practices/worst practices," and I'm putting it here to help save some other programmer from hours of debugging.

I have a single checkbox (named 'Tracking') in a form field. It needs to be checked on submission to see if that user has proper credentials (if it fails the validation I want the user to see a message on what to do next.) There is also a field that is mandatory ('Entry') and must be filled in.

When the check box gets selected, the validator signaled a reject. Unfortunately, when the validator returns with the invalid response code (false), the calling Form object will redirectBack to the previous page: which just happens to be the exact page we are on, and the URL ends with '?executeForm=...' . This caused an infinite redirect loop that took me about 8 hours to figure out.

The '?executeForm=...' was causing a reload of the form data from a 'GET' rather than a 'POST' (redirectBack won't be handled as a post). This caused the form data to get loaded from the non-existent POST data. This resulted in a blank field (the mandatory 'Entry'). That new request got rejected and the cycle went on till the browser gave up.

Fortunately, the redirectBack can be routed by setting a 'magic' request array value: $_REQUEST['_REDIRECT_BACK_URL']. This allowed me to force a 'good' destination when the validator returns the reject code.

I also needed the check box 'Tracking' field needed to be unset. That was not obvious (even reading TFM and looking at the sapphire code).

So when you need to reset the value of Tracking to 0 from within a validator, I called:

$this -> form -> resetData( 'Tracking' , 0);

This 'seems' to work. Maybe.

Things that did NOT work:
// unset($data['Tracking]);
// $this -> form -> unsetDataFieldByName($this->checkboxFieldName);
// unset ($data[$this->checkboxFieldName]);
// unset ($_REQUEST[$this->checkboxFieldName]);

The final 'working' code in the validator was:

class myValidator extends Validator {
 var $thisPage;  // passed in as a new argument on construction
 var $entryFieldName;
 var $checkboxFieldName;

function javascript() { return ""; } // no javascript needed.

function __construct($thisPage,$entryFieldName,$checkboxFieldName) {   parent::__construct($entryFieldName,$checkboxFieldName);
   $this->thisPage = $thisPage;
   $this->entryFieldName = $entryFieldName;
   $this->checkboxFieldName = $checkboxFieldName;
   }

function php ($data) {
   ...
  $accept = true;
   if($data[$this->entryFieldName] == "")  {
      $this->validationError( $this->entryFieldName,
                                 sprintf( _t('Form.FIELDISREQUIRED'),
                                               strip_tags($this->entryFieldName)),
                                          "required");
   $accept=false;
   }
   if($data['Tracking'] && !Permission::check('SITE_WATCHER') )
      {
         $accept = false;
         $this->validationError($this->checkboxFieldName, "only Registered Voters and above may check this box", "permission" );
      }

      set $accept if we like this data
   ...
   if( $accept ) return true;
      //avoid infinite loop when previous page is ALSO a form execution on this page

//Debug::message($this->checkboxFieldName);
//       $this -> form -> unsetDataFieldByName($this->checkboxFieldName);
//       unset ($data[$this->checkboxFieldName]);
//       unset ($_REQUEST[$this->checkboxFieldName]);
 $this->form->resetData($this->checkboxFieldName,0);

$_REQUEST['_REDIRECT_BACK_URL'] = Director::link($this->thisPage->URLSegment);
  // we reject the data
return false;

}

The call to create the validator from the Form() function:


function Form() {

... lots of form kind of stuff to create the $fields and the $actions

// the entryValidator needs to know the page we are on because it has the URLSegment (and some other stuff that may not be important for this exposition)

// the Validator (parent class) needs to know the field names we are looking at
   $vv = new entryValidator($this, "Entry", "Tracking" );
   $f= new Form($this, 'EntrySuggestionForm', $fields, $actions,$vv);
   return $f;
}