public function testValidation()
 {
     $validator = new OrderActionsForm_Validator('PaymentMethod');
     $form = new OrderActionsForm(ModelAsController::controller_for($this->checkoutPage), 'ActionsForm', $this->order);
     $validator->setForm($form);
     Form::set_current_action('dopayment');
     $validator->php(array('OrderID' => $this->order->ID, 'PaymentMethod' => 'Dummy', 'type' => 'visa', 'name' => 'Tester Mc. Testerson', 'number' => '4242424242424242'));
     $requiredCount = 0;
     foreach ($validator->getErrors() as $error) {
         if ($error['messageType'] == 'required') {
             $requiredCount++;
         }
     }
     // 3 required fields missing
     $this->assertEquals(3, $requiredCount);
 }
Example #2
0
 /**
  * Handle a form submission.  GET and POST requests behave identically.
  * Populates the form with {@link loadDataFrom()}, calls {@link validate()},
  * and only triggers the requested form action/method
  * if the form is valid.
  */
 public function httpSubmission($request)
 {
     // Strict method check
     if ($this->strictFormMethodCheck) {
         // Throws an error if the method is bad...
         if ($this->formMethod != strtolower($request->httpMethod())) {
             $response = Controller::curr()->getResponse();
             $response->addHeader('Allow', $this->formMethod);
             $this->httpError(405, _t("Form.BAD_METHOD", "This form requires a " . $this->formMethod . " submission"));
         }
         // ...and only uses the vairables corresponding to that method type
         $vars = $this->formMethod == 'get' ? $request->getVars() : $request->postVars();
     } else {
         $vars = $request->requestVars();
     }
     // Populate the form
     $this->loadDataFrom($vars, true);
     // Protection against CSRF attacks
     $token = $this->getSecurityToken();
     if (!$token->checkRequest($request)) {
         $this->httpError(400, _t("Form.CSRF_FAILED_MESSAGE", "There seems to have been a technical problem. Please click the back button," . " refresh your browser, and try again."));
     }
     // Determine the action button clicked
     $funcName = null;
     foreach ($vars as $paramName => $paramVal) {
         if (substr($paramName, 0, 7) == 'action_') {
             // Break off querystring arguments included in the action
             if (strpos($paramName, '?') !== false) {
                 list($paramName, $paramVars) = explode('?', $paramName, 2);
                 $newRequestParams = array();
                 parse_str($paramVars, $newRequestParams);
                 $vars = array_merge((array) $vars, (array) $newRequestParams);
             }
             // Cleanup action_, _x and _y from image fields
             $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName);
             break;
         }
     }
     // If the action wasnt' set, choose the default on the form.
     if (!isset($funcName) && ($defaultAction = $this->defaultAction())) {
         $funcName = $defaultAction->actionName();
     }
     if (isset($funcName)) {
         Form::set_current_action($funcName);
         $this->setButtonClicked($funcName);
     }
     // Permission checks (first on controller, then falling back to form)
     if ($this->controller->hasMethod($funcName) && !$this->controller->checkAccessAction($funcName) && !$this->actions->dataFieldByName('action_' . $funcName)) {
         return $this->httpError(403, sprintf('Action "%s" not allowed on controller (Class: %s)', $funcName, get_class($this->controller)));
     } elseif ($this->hasMethod($funcName) && !$this->checkAccessAction($funcName)) {
         return $this->httpError(403, sprintf('Action "%s" not allowed on form (Name: "%s")', $funcName, $this->name));
     }
     // TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set
     // explicitly in allowed_actions in order to run)
     // Uncomment the following for checking security against running actions on form fields
     /* else {
     			// Try to find a field that has the action, and allows it
     			$fieldsHaveMethod = false;
     			foreach ($this->Fields() as $field){
     				if ($field->hasMethod($funcName) && $field->checkAccessAction($funcName)) {
     					$fieldsHaveMethod = true;
     				}
     			}
     			if (!$fieldsHaveMethod) {
     				return $this->httpError(
     					403, 
     					sprintf('Action "%s" not allowed on any fields of form (Name: "%s")', $funcName, $this->Name())
     				);
     			}
     		}*/
     // Validate the form
     if (!$this->validate()) {
         return $this->getValidationErrorResponse();
     }
     // First, try a handler method on the controller (has been checked for allowed_actions above already)
     if ($this->controller->hasMethod($funcName)) {
         return $this->controller->{$funcName}($vars, $this, $request);
         // Otherwise, try a handler method on the form object.
     } elseif ($this->hasMethod($funcName)) {
         return $this->{$funcName}($vars, $this, $request);
     } elseif ($field = $this->checkFieldsForAction($this->Fields(), $funcName)) {
         return $field->{$funcName}($vars, $this, $request);
     }
     return $this->httpError(404);
 }
Example #3
0
	/**
	 * Handle a form submission.  GET and POST requests behave identically.
	 * Populates the form with {@link loadDataFrom()}, calls {@link validate()},
	 * and only triggers the requested form action/method
	 * if the form is valid.
	 */
	function httpSubmission($request) {
		$vars = $request->requestVars();
		if(isset($funcName)) {
			Form::set_current_action($funcName);
		}
		
		// Populate the form
		$this->loadDataFrom($vars, true);
		
		// Validate the form
		if(!$this->validate()) {
			if(Director::is_ajax()) {
				return FormResponse::respond();
			} else {
				Director::redirectBack();
				return;
			}
		}

		// Protection against CSRF attacks
		if($this->securityTokenEnabled()) {
			$securityID = Session::get('SecurityID');

			if(!$securityID || !isset($vars['SecurityID']) || $securityID != $vars['SecurityID']) {
				$this->httpError(400, "SecurityID doesn't match, possible CRSF attack.");
			}
		}
		
		// Determine the action button clicked
		$funcName = null;
		foreach($vars as $paramName => $paramVal) {
			if(substr($paramName,0,7) == 'action_') {
				// Break off querystring arguments included in the action
				if(strpos($paramName,'?') !== false) {
					list($paramName, $paramVars) = explode('?', $paramName, 2);
					$newRequestParams = array();
					parse_str($paramVars, $newRequestParams);
					$vars = array_merge((array)$vars, (array)$newRequestParams);
				}
				
				// Cleanup action_, _x and _y from image fields
				$funcName = preg_replace(array('/^action_/','/_x$|_y$/'),'',$paramName);
				break;
			}
		}
		
		// If the action wasnt' set, choose the default on the form.
		if(!isset($funcName) && $defaultAction = $this->defaultAction()){
			$funcName = $defaultAction->actionName();
		}
			
		if(isset($funcName)) {
			$this->setButtonClicked($funcName);
		}

		// First, try a handler method on the controller
		if($this->controller->hasMethod($funcName)) {
			return $this->controller->$funcName($vars, $this, $request);

		// Otherwise, try a handler method on the form object
		} else {
			return $this->$funcName($vars, $this, $request);
		}
	}
Example #4
0
 function run($requestParams)
 {
     if (isset($_GET['debug_profile'])) {
         Profiler::mark("Controller", "run");
     }
     $this->pushCurrent();
     $this->response = new HTTPResponse();
     $this->requestParams = $requestParams;
     $this->action = isset($this->urlParams['Action']) ? str_replace("-", "_", $this->urlParams['Action']) : "";
     if (!$this->action) {
         $this->action = 'index';
     }
     // Check security on the controller
     if (!$this->checkAccessAction($this->action)) {
         user_error("Disallowed action: '{$this->action}' on controller '{$this->class}'", E_USER_ERROR);
     }
     // Init
     $this->baseInitCalled = false;
     $this->init();
     if (!$this->baseInitCalled) {
         user_error("init() method on class '{$this->class}' doesn't call Controller::init().  Make sure that you have parent::init() included.", E_USER_WARNING);
     }
     // If we had a redirection or something, halt processing.
     if ($this->response->isFinished()) {
         $this->popCurrent();
         return $this->response;
     }
     // Look at the action variables for forms
     $funcName = null;
     foreach ($this->requestParams as $paramName => $paramVal) {
         if (substr($paramName, 0, 7) == 'action_') {
             // Cleanup action_, _x and _y from image fields
             $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName);
             break;
         }
     }
     // Form handler
     if (isset($this->requestParams['executeForm']) && is_string($this->requestParams['executeForm'])) {
         if (isset($funcName)) {
             Form::set_current_action($funcName);
         }
         // Get the appropraite ocntroller: sometimes we want to get a form from another controller
         if (isset($this->requestParams['formController'])) {
             $formController = Director::getControllerForURL($this->requestParams['formController']);
             while (is_a($formController, 'NestedController')) {
                 $formController = $formController->getNestedController();
             }
         } else {
             $formController = $this;
         }
         // Create the form object
         $form = $formController;
         $formObjParts = explode('.', $this->requestParams['executeForm']);
         foreach ($formObjParts as $formMethod) {
             if (isset($_GET['debug_profile'])) {
                 Profiler::mark("Calling {$formMethod}", "on {$form->class}");
             }
             $form = $form->{$formMethod}();
             if (isset($_GET['debug_profile'])) {
                 Profiler::unmark("Calling {$formMethod}", "on {$form->class}");
             }
             if (!$form) {
                 break;
             }
             //user_error("Form method '" . $this->requestParams['executeForm'] . "' returns null in controller class '$this->class' ($_SERVER[REQUEST_URI])", E_USER_ERROR);
         }
         // Populate the form
         if (isset($_GET['debug_profile'])) {
             Profiler::mark("Controller", "populate form");
         }
         if ($form) {
             $form->loadDataFrom($this->requestParams, true);
             // disregard validation if a single field is called
             if (!isset($_REQUEST['action_callfieldmethod'])) {
                 $valid = $form->beforeProcessing();
                 if (!$valid) {
                     $this->popCurrent();
                     return $this->response;
                 }
             } else {
                 $fieldcaller = $form->dataFieldByName($requestParams['fieldName']);
                 if (is_a($fieldcaller, "TableListField")) {
                     if ($fieldcaller->hasMethod('php')) {
                         $valid = $fieldcaller->php($requestParams);
                         if (!$valid) {
                             exit;
                         }
                     }
                 }
             }
             // If the action wasnt' set, choose the default on the form.
             if (!isset($funcName) && ($defaultAction = $form->defaultAction())) {
                 $funcName = $defaultAction->actionName();
             }
             if (isset($funcName)) {
                 $form->setButtonClicked($funcName);
             }
         } else {
             user_error("No form (" . Session::get('CMSMain.currentPage') . ") returned by {$formController->class}->{$_REQUEST['executeForm']}", E_USER_WARNING);
         }
         if (isset($_GET['debug_profile'])) {
             Profiler::unmark("Controller", "populate form");
         }
         if (!isset($funcName)) {
             user_error("No action button has been clicked in this form executon, and no default has been allowed", E_USER_ERROR);
         }
         // Protection against CSRF attacks
         if ($form->securityTokenEnabled()) {
             $securityID = Session::get('SecurityID');
             if (!$securityID || !isset($this->requestParams['SecurityID']) || $securityID != $this->requestParams['SecurityID']) {
                 // Don't show error on live sites, as spammers create a million of these
                 if (!Director::isLive()) {
                     trigger_error("Security ID doesn't match, possible CRSF attack.", E_USER_ERROR);
                 } else {
                     die;
                 }
             }
         }
         // First, try a handler method on the controller
         if ($this->hasMethod($funcName) || !$form) {
             if (isset($_GET['debug_controller'])) {
                 Debug::show("Found function {$funcName} on the controller");
             }
             if (isset($_GET['debug_profile'])) {
                 Profiler::mark("{$this->class}::{$funcName} (controller action)");
             }
             $result = $this->{$funcName}($this->requestParams, $form);
             if (isset($_GET['debug_profile'])) {
                 Profiler::unmark("{$this->class}::{$funcName} (controller action)");
             }
             // Otherwise, try a handler method on the form object
         } else {
             if (isset($_GET['debug_controller'])) {
                 Debug::show("Found function {$funcName} on the form object");
             }
             if (isset($_GET['debug_profile'])) {
                 Profiler::mark("{$form->class}::{$funcName} (form action)");
             }
             $result = $form->{$funcName}($this->requestParams, $form);
             if (isset($_GET['debug_profile'])) {
                 Profiler::unmark("{$form->class}::{$funcName} (form action)");
             }
         }
         // Normal action
     } else {
         if (!isset($funcName)) {
             $funcName = $this->action;
         }
         if ($this->hasMethod($funcName)) {
             if (isset($_GET['debug_controller'])) {
                 Debug::show("Found function {$funcName} on the {$this->class} controller");
             }
             if (isset($_GET['debug_profile'])) {
                 Profiler::mark("{$this->class}::{$funcName} (controller action)");
             }
             $result = $this->{$funcName}($this->urlParams);
             if (isset($_GET['debug_profile'])) {
                 Profiler::unmark("{$this->class}::{$funcName} (controller action)");
             }
         } else {
             if (isset($_GET['debug_controller'])) {
                 Debug::show("Running default action for {$funcName} on the {$this->class} controller");
             }
             if (isset($_GET['debug_profile'])) {
                 Profiler::mark("Controller::defaultAction({$funcName})");
             }
             $result = $this->defaultAction($funcName, $this->urlParams);
             if (isset($_GET['debug_profile'])) {
                 Profiler::unmark("Controller::defaultAction({$funcName})");
             }
         }
     }
     // If your controller function returns an array, then add that data to the
     // default template
     if (is_array($result)) {
         $extended = $this->customise($result);
         $viewer = $this->getViewer($funcName);
         $result = $viewer->process($extended);
     }
     $this->response->setBody($result);
     if ($result) {
         ContentNegotiator::process($this->response);
     }
     // Set up HTTP cache headers
     HTTP::add_cache_headers($this->response);
     if (isset($_GET['debug_profile'])) {
         Profiler::unmark("Controller", "run");
     }
     $this->popCurrent();
     return $this->response;
 }
Example #5
0
 /**
  * Handle a form submission.  GET and POST requests behave identically.
  * Populates the form with {@link loadDataFrom()}, calls {@link validate()},
  * and only triggers the requested form action/method
  * if the form is valid.
  */
 function httpSubmission($request)
 {
     $vars = $request->requestVars();
     if (isset($funcName)) {
         Form::set_current_action($funcName);
     }
     // Populate the form
     $this->loadDataFrom($vars, true);
     // Protection against CSRF attacks
     if ($this->securityTokenEnabled()) {
         $securityID = Session::get('SecurityID');
         if (!$securityID || !isset($vars['SecurityID']) || $securityID != $vars['SecurityID']) {
             $this->httpError(400, "SecurityID doesn't match, possible CSRF attack.");
         }
     }
     // Determine the action button clicked
     $funcName = null;
     foreach ($vars as $paramName => $paramVal) {
         if (substr($paramName, 0, 7) == 'action_') {
             // Break off querystring arguments included in the action
             if (strpos($paramName, '?') !== false) {
                 list($paramName, $paramVars) = explode('?', $paramName, 2);
                 $newRequestParams = array();
                 parse_str($paramVars, $newRequestParams);
                 $vars = array_merge((array) $vars, (array) $newRequestParams);
             }
             // Cleanup action_, _x and _y from image fields
             $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName);
             break;
         }
     }
     // If the action wasnt' set, choose the default on the form.
     if (!isset($funcName) && ($defaultAction = $this->defaultAction())) {
         $funcName = $defaultAction->actionName();
     }
     if (isset($funcName)) {
         $this->setButtonClicked($funcName);
     }
     // Validate the form
     if (!$this->validate()) {
         if (Director::is_ajax()) {
             // Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through FormResponse)
             if ($this->validator->getJavascriptValidationHandler() == 'prototype') {
                 return FormResponse::respond();
             } else {
                 $acceptType = $request->getHeader('Accept');
                 if (strpos($acceptType, 'application/json') !== FALSE) {
                     // Send validation errors back as JSON with a flag at the start
                     $response = new SS_HTTPResponse(Convert::array2json($this->validator->getErrors()));
                     $response->addHeader('Content-Type', 'application/json');
                 } else {
                     $this->setupFormErrors();
                     // Send the newly rendered form tag as HTML
                     $response = new SS_HTTPResponse($this->forTemplate());
                     $response->addHeader('Content-Type', 'text/html');
                 }
                 return $response;
             }
         } else {
             if ($this->getRedirectToFormOnValidationError()) {
                 if ($pageURL = $request->getHeader('Referer')) {
                     if (Director::is_site_url($pageURL)) {
                         // Remove existing pragmas
                         $pageURL = preg_replace('/(#.*)/', '', $pageURL);
                         return Director::redirect($pageURL . '#' . $this->FormName());
                     }
                 }
             }
             return Director::redirectBack();
         }
     }
     // First, try a handler method on the controller
     if ($this->controller->hasMethod($funcName)) {
         return $this->controller->{$funcName}($vars, $this, $request);
         // Otherwise, try a handler method on the form object
     } else {
         if ($this->hasMethod($funcName)) {
             return $this->{$funcName}($vars, $this, $request);
         }
     }
 }
Example #6
0
	/**
	 * Handle a form submission.  GET and POST requests behave identically.
	 * Populates the form with {@link loadDataFrom()}, calls {@link validate()},
	 * and only triggers the requested form action/method
	 * if the form is valid.
	 */
	public function httpSubmission($request) {
		$vars = $request->requestVars();
		if(isset($funcName)) {
			Form::set_current_action($funcName);
		}
		
		// Populate the form
		$this->loadDataFrom($vars, true);
	
		// Protection against CSRF attacks
		$token = $this->getSecurityToken();
		if(!$token->checkRequest($request)) {
			$this->httpError(400, "Sorry, your session has timed out.");
		}
		
		// Determine the action button clicked
		$funcName = null;
		foreach($vars as $paramName => $paramVal) {
			if(substr($paramName,0,7) == 'action_') {
				// Break off querystring arguments included in the action
				if(strpos($paramName,'?') !== false) {
					list($paramName, $paramVars) = explode('?', $paramName, 2);
					$newRequestParams = array();
					parse_str($paramVars, $newRequestParams);
					$vars = array_merge((array)$vars, (array)$newRequestParams);
				}
				
				// Cleanup action_, _x and _y from image fields
				$funcName = preg_replace(array('/^action_/','/_x$|_y$/'),'',$paramName);
				break;
			}
		}
		
		// If the action wasnt' set, choose the default on the form.
		if(!isset($funcName) && $defaultAction = $this->defaultAction()){
			$funcName = $defaultAction->actionName();
		}
			
		if(isset($funcName)) {
			$this->setButtonClicked($funcName);
		}
		
		// Permission checks (first on controller, then falling back to form)
		if(
			// Ensure that the action is actually a button or method on the form,
			// and not just a method on the controller.
			$this->controller->hasMethod($funcName)
			&& !$this->controller->checkAccessAction($funcName)
			// If a button exists, allow it on the controller
			&& !$this->Actions()->fieldByName('action_' . $funcName)
		) {
			return $this->httpError(
				403, 
				sprintf('Action "%s" not allowed on controller (Class: %s)', $funcName, get_class($this->controller))
			);
		} elseif(
			$this->hasMethod($funcName)
			&& !$this->checkAccessAction($funcName)
			// No checks for button existence or $allowed_actions is performed -
			// all form methods are callable (e.g. the legacy "callfieldmethod()")
		) {
			return $this->httpError(
				403, 
				sprintf('Action "%s" not allowed on form (Name: "%s")', $funcName, $this->Name())
			);
		}
		// TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set explicitly in allowed_actions in order to run)
		// Uncomment the following for checking security against running actions on form fields
		/* else {
			// Try to find a field that has the action, and allows it
			$fieldsHaveMethod = false;
			foreach ($this->Fields() as $field){
				if ($field->hasMethod($funcName) && $field->checkAccessAction($funcName)) {
					$fieldsHaveMethod = true;
				}
			}
			if (!$fieldsHaveMethod) {
				return $this->httpError(
					403, 
					sprintf('Action "%s" not allowed on any fields of form (Name: "%s")', $funcName, $this->Name())
				);
			}
		}*/
		
		// Validate the form
		if(!$this->validate()) {
			if(Director::is_ajax()) {
				// Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through FormResponse)
				$acceptType = $request->getHeader('Accept');
				if(strpos($acceptType, 'application/json') !== FALSE) {
					// Send validation errors back as JSON with a flag at the start
					$response = new SS_HTTPResponse(Convert::array2json($this->validator->getErrors()));
					$response->addHeader('Content-Type', 'application/json');
				} else {
					$this->setupFormErrors();
					// Send the newly rendered form tag as HTML
					$response = new SS_HTTPResponse($this->forTemplate());
					$response->addHeader('Content-Type', 'text/html');
				}
				
				return $response;
			} else {
				if($this->getRedirectToFormOnValidationError()) {
					if($pageURL = $request->getHeader('Referer')) {
						if(Director::is_site_url($pageURL)) {
							// Remove existing pragmas
							$pageURL = preg_replace('/(#.*)/', '', $pageURL);
							return $this->controller->redirect($pageURL . '#' . $this->FormName());
						}
					}
				}
				return $this->controller->redirectBack();
			}
		}
		
		// First, try a handler method on the controller (has been checked for allowed_actions above already)
		if($this->controller->hasMethod($funcName)) {
			return $this->controller->$funcName($vars, $this, $request);
		// Otherwise, try a handler method on the form object.
		} elseif($this->hasMethod($funcName)) {
			return $this->$funcName($vars, $this, $request);
		} elseif($field = $this->checkFieldsForAction($this->Fields(), $funcName)) {
			return $field->$funcName($vars, $this, $request);
		}
		
		return $this->httpError(404);
	}
 function httpSubmission($request)
 {
     $vars = $request->requestVars();
     if (isset($funcName)) {
         Form::set_current_action($funcName);
     }
     // Populate the form
     $this->loadDataFrom($vars, true);
     // Protection against CSRF attacks
     $token = $this->getSecurityToken();
     if (!$token->checkRequest($request)) {
         $this->httpError(400, _t('AdvancedWorkflowFrontendForm.SECURITYTOKENCHECK', "Security token doesn't match, possible CSRF attack."));
     }
     // Determine the action button clicked
     $funcName = null;
     foreach ($vars as $paramName => $paramVal) {
         if (substr($paramName, 0, 7) == 'action_') {
             // Added for frontend workflow form - get / set transitionID on controller,
             // unset action and replace with doFrontEndAction action
             if (substr($paramName, 0, 18) == 'action_transition_') {
                 $this->controller->transitionID = substr($paramName, strrpos($paramName, '_') + 1);
                 unset($vars['action_transition_' . $this->controller->transitionID]);
                 $vars['action_doFrontEndAction'] = 'doFrontEndAction';
                 $paramName = 'action_doFrontEndAction';
                 $paramVal = 'doFrontEndAction';
             }
             // Break off querystring arguments included in the action
             if (strpos($paramName, '?') !== false) {
                 list($paramName, $paramVars) = explode('?', $paramName, 2);
                 $newRequestParams = array();
                 parse_str($paramVars, $newRequestParams);
                 $vars = array_merge((array) $vars, (array) $newRequestParams);
             }
             // Cleanup action_, _x and _y from image fields
             $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName);
             break;
         }
     }
     // If the action wasnt' set, choose the default on the form.
     if (!isset($funcName) && ($defaultAction = $this->defaultAction())) {
         $funcName = $defaultAction->actionName();
     }
     if (isset($funcName)) {
         $this->setButtonClicked($funcName);
     }
     // Permission checks (first on controller, then falling back to form)
     if ($this->controller->hasMethod($funcName) && !$this->controller->checkAccessAction($funcName) && !$this->Actions()->fieldByName('action_' . $funcName)) {
         return $this->httpError(403, sprintf(_t('AdvancedWorkflowFrontendForm.ACTIONCONTROLLERCHECK', 'Action "%s" not allowed on controller (Class: %s)'), $funcName, get_class($this->controller)));
     } elseif ($this->hasMethod($funcName) && !$this->checkAccessAction($funcName)) {
         return $this->httpError(403, sprintf(_t('AdvancedWorkflowFrontendForm.ACTIONFORMCHECK', 'Action "%s" not allowed on form (Name: "%s")'), $funcName, $this->Name()));
     }
     if ($wfTransition = $this->controller->getCurrentTransition()) {
         $wfTransType = $wfTransition->Type;
     } else {
         $wfTransType = null;
         //ie. when a custom Form Action is defined in WorkflowAction
     }
     // Validate the form
     if (!$this->validate() && $wfTransType == 'Active') {
         if (Director::is_ajax()) {
             // Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through FormResponse)
             if ($this->validator->getJavascriptValidationHandler() == 'prototype') {
                 return FormResponse::respond();
             } else {
                 $acceptType = $request->getHeader('Accept');
                 if (strpos($acceptType, 'application/json') !== FALSE) {
                     // Send validation errors back as JSON with a flag at the start
                     $response = new SS_HTTPResponse(Convert::array2json($this->validator->getErrors()));
                     $response->addHeader('Content-Type', 'application/json');
                 } else {
                     $this->setupFormErrors();
                     // Send the newly rendered form tag as HTML
                     $response = new SS_HTTPResponse($this->forTemplate());
                     $response->addHeader('Content-Type', 'text/html');
                 }
                 return $response;
             }
         } else {
             if ($this->getRedirectToFormOnValidationError()) {
                 if ($pageURL = $request->getHeader('Referer')) {
                     if (Director::is_site_url($pageURL)) {
                         // Remove existing pragmas
                         $pageURL = preg_replace('/(#.*)/', '', $pageURL);
                         return Director::redirect($pageURL . '#' . $this->FormName());
                     }
                 }
             }
             return Director::redirectBack();
         }
     }
     // First, try a handler method on the controller (has been checked for allowed_actions above already)
     if ($this->controller->hasMethod($funcName)) {
         return $this->controller->{$funcName}($vars, $this, $request);
         // Otherwise, try a handler method on the form object.
     } elseif ($this->hasMethod($funcName)) {
         return $this->{$funcName}($vars, $this, $request);
     }
     return $this->httpError(404);
 }