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); }
/** * 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); }
/** * 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); } }
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; }
/** * 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); } } }
/** * 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); }