/**
  * This method passes through an HTTP request to another webserver. 
  * This proxy is used to avoid any cross domain issues. The proxy
  * uses a white-list of domains to minimize security risks. 
  *
  * @param SS_HTTPRequest $data array of parameters
  *
  * $data['u']:         URL (complete request string)
  * $data['no_header']: set to '1' to avoid sending header information 
  *                     directly. 
  * @return the CURL response
  */
 public function dorequest($data)
 {
     $headers = array();
     $vars = $data->requestVars();
     $no_header = false;
     if (!isset($vars['u'])) {
         return "Invalid request: unknown proxy destination.";
     }
     $url = $vars['u'];
     if (isset($vars['no_header']) && $vars['no_header'] == '1') {
         $no_header = true;
     }
     $checkUrl = explode("/", $url);
     if (!in_array($checkUrl[2], self::get_allowed_host())) {
         return "Access denied to ({$url}).";
     }
     // Open the Curl session
     $session = curl_init($url);
     // If it's a POST, put the POST data in the body
     $isPost = $data->isPOST();
     if ($isPost) {
         $postvars = '';
         $vars = $data->getBody();
         if ($vars) {
             $postvars = "body=" . $vars;
         } else {
             $vars = $data->postVars();
             if ($vars) {
                 foreach ($vars as $k => $v) {
                     $postvars .= $k . '=' . $v . '&';
                 }
             }
         }
         $headers[] = 'Content-type: text/xml';
         curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
         curl_setopt($session, CURLOPT_POST, true);
         curl_setopt($session, CURLOPT_POSTFIELDS, $postvars);
     }
     // Don't return HTTP headers. Do return the contents of the call
     curl_setopt($session, CURLOPT_HEADER, false);
     curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
     // Make the call
     $xml = curl_exec($session);
     // The web service returns XML. Set the Content-Type appropriately
     if ($no_header == false) {
         header("Content-Type: text/xml");
     }
     curl_close($session);
     return $xml;
 }
 /**
  * @param SS_HTTPRequest $req
  * @return string
  */
 public function search_suggest(SS_HTTPRequest $req)
 {
     /** @var SS_HTTPResponse $response */
     $response = $this->owner->getResponse();
     $callback = $req->requestVar('callback');
     // convert the search results into usable json for search-as-you-type
     if (ShopSearch::config()->search_as_you_type_enabled) {
         $searchVars = $req->requestVars();
         $searchVars[ShopSearch::config()->qs_query] = $searchVars['term'];
         unset($searchVars['term']);
         $results = ShopSearch::inst()->suggestWithResults($searchVars);
     } else {
         $results = array('suggestions' => ShopSearch::inst()->suggest($req->requestVar('term')));
     }
     if ($callback) {
         $response->addHeader('Content-type', 'application/javascript');
         $response->setBody($callback . '(' . json_encode($results) . ');');
     } else {
         $response->addHeader('Content-type', 'application/json');
         $response->setBody(json_encode($results));
     }
     return $response;
 }
 public function testRequestVars()
 {
     $getVars = array('first' => 'a', 'second' => 'b');
     $postVars = array('third' => 'c', 'fourth' => 'd');
     $requestVars = array('first' => 'a', 'second' => 'b', 'third' => 'c', 'fourth' => 'd');
     $request = new SS_HTTPRequest('POST', 'admin/crm', $getVars, $postVars);
     $this->assertEquals($requestVars, $request->requestVars(), 'GET parameters should supplement POST parameters');
     $getVars = array('first' => 'a', 'second' => 'b');
     $postVars = array('first' => 'c', 'third' => 'd');
     $requestVars = array('first' => 'c', 'second' => 'b', 'third' => 'd');
     $request = new SS_HTTPRequest('POST', 'admin/crm', $getVars, $postVars);
     $this->assertEquals($requestVars, $request->requestVars(), 'POST parameters should override GET parameters');
     $getVars = array('first' => array('first' => 'a'), 'second' => array('second' => 'b'));
     $postVars = array('first' => array('first' => 'c'), 'third' => array('third' => 'd'));
     $requestVars = array('first' => array('first' => 'c'), 'second' => array('second' => 'b'), 'third' => array('third' => 'd'));
     $request = new SS_HTTPRequest('POST', 'admin/crm', $getVars, $postVars);
     $this->assertEquals($requestVars, $request->requestVars(), 'Nested POST parameters should override GET parameters');
     $getVars = array('first' => array('first' => 'a'), 'second' => array('second' => 'b'));
     $postVars = array('first' => array('second' => 'c'), 'third' => array('third' => 'd'));
     $requestVars = array('first' => array('first' => 'a', 'second' => 'c'), 'second' => array('second' => 'b'), 'third' => array('third' => 'd'));
     $request = new SS_HTTPRequest('POST', 'admin/crm', $getVars, $postVars);
     $this->assertEquals($requestVars, $request->requestVars(), 'Nested GET parameters should supplement POST parameters');
 }
Exemple #4
0
 /**
  * @param SS_HTTPRequest $request
  *
  * @return string
  */
 public function deploySummary(SS_HTTPRequest $request)
 {
     // Performs canView permission check by limiting visible projects
     $project = $this->getCurrentProject();
     if (!$project) {
         return $this->project404Response();
     }
     // Performs canView permission check by limiting visible projects
     $environment = $this->getCurrentEnvironment($project);
     if (!$environment) {
         return $this->environment404Response();
     }
     // Plan the deployment.
     $strategy = $environment->Backend()->planDeploy($environment, $request->requestVars());
     $data = $strategy->toArray();
     // Add in a URL for comparing from->to code changes. Ensure that we have
     // two proper 40 character SHAs, otherwise we can't show the compare link.
     $interface = $project->getRepositoryInterface();
     if (!empty($interface) && !empty($interface->URL) && !empty($data['changes']['Code version']['from']) && strlen($data['changes']['Code version']['from']) == '40' && !empty($data['changes']['Code version']['to']) && strlen($data['changes']['Code version']['to']) == '40') {
         $compareurl = sprintf('%s/compare/%s...%s', $interface->URL, $data['changes']['Code version']['from'], $data['changes']['Code version']['to']);
         $data['changes']['Code version']['compareUrl'] = $compareurl;
     }
     // Append json to response
     $token = SecurityToken::inst();
     $data['SecurityID'] = $token->getValue();
     return json_encode($data);
 }
Exemple #5
0
 /**
  * This is the action that gets executed when a GridField_AlterAction gets clicked.
  *
  * @param array $data
  * @param Form $form
  * @param SS_HTTPRequest $request
  *
  * @return string
  */
 public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request)
 {
     $data = $request->requestVars();
     $name = $this->getName();
     $fieldData = null;
     if (isset($data[$name])) {
         $fieldData = $data[$name];
     }
     $state = $this->getState(false);
     if (isset($fieldData['GridState'])) {
         $state->setValue($fieldData['GridState']);
     }
     foreach ($data as $dataKey => $dataValue) {
         if (preg_match('/^action_gridFieldAlterAction\\?StateID=(.*)/', $dataKey, $matches)) {
             $stateChange = Session::get($matches[1]);
             $actionName = $stateChange['actionName'];
             $arguments = array();
             if (isset($stateChange['args'])) {
                 $arguments = $stateChange['args'];
             }
             $html = $this->handleAlterAction($actionName, $arguments, $data);
             if ($html) {
                 return $html;
             }
         }
     }
     if ($request->getHeader('X-Pjax') === 'CurrentField') {
         return $this->FieldHolder();
     }
     return $form->forTemplate();
 }
Exemple #6
0
	/**
	 * This is the action that gets executed when a GridField_AlterAction gets clicked.
	 *
	 * @param array $data
	 * @return string 
	 */
	public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) {
		$html = '';
		$data = $request->requestVars();
		$fieldData = @$data[$this->getName()];

		// Update state from client
		$state = $this->getState(false);
		if(isset($fieldData['GridState'])) $state->setValue($fieldData['GridState']);

		// Try to execute alter action
		foreach($data as $k => $v) {
			if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $k, $matches)) {
				$id = $matches[1];
				$stateChange = Session::get($id);
				$actionName = $stateChange['actionName'];
				$args = isset($stateChange['args']) ? $stateChange['args'] : array();
				$html = $this->handleAction($actionName, $args, $data);
				// A field can optionally return its own HTML
				if($html) return $html;
			}
		}
		
		switch($request->getHeader('X-Pjax')) {
			case 'CurrentField':
				return $this->FieldHolder();
				break;

			case 'CurrentForm':
				return $form->forTemplate();
				break;

			default:
				return $form->forTemplate();
				break;
		}
	}
 /**
  * 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.
  *
  * @param SS_HTTPRequest $request
  * @throws SS_HTTPResponse_Exception
  */
 public function httpSubmission($request)
 {
     // Strict method check
     if ($this->strictFormMethodCheck) {
         // Throws an error if the method is bad...
         if ($this->formMethod != $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 variables 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)) {
         $securityID = $token->getName();
         if (empty($vars[$securityID])) {
             $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."));
         } else {
             // Clear invalid token on refresh
             $data = $this->getData();
             unset($data[$securityID]);
             Session::set("FormInfo.{$this->FormName()}.data", $data);
             Session::set("FormInfo.{$this->FormName()}.errors", array());
             $this->sessionMessage(_t("Form.CSRF_EXPIRED_MESSAGE", "Your session has expired. Please re-submit the form."), "warning");
             return $this->controller->redirectBack();
         }
     }
     // 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 wasn't 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);
 }
 /**
  * Handles all contexts
  * !TODO - handle more than one model in one request
  *
  * @param SS_HTTPRequest $req
  * @return SS_HTTPResponse
  * @throws Exception
  */
 public function index(SS_HTTPRequest $req)
 {
     $model = $req->requestVar('model');
     if (!$model) {
         return $this->fail(400);
     }
     // allow different date formats for different clients (eg. android)
     if ($df = $req->requestVar('date_format')) {
         self::$date_format = $df;
     }
     // this just makes the crossdomain ajax stuff simpler and
     // keeps anything weird from happening there.
     if (self::$allow_crossdomain && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
         //			$response = $this->getResponse();
         //			$response->addHeader("Access-Control-Allow-Origin", "*");
         //			$response->addHeader("Access-Control-Allow-Headers", "X-Requested-With");
         //			return $response;
         header("Access-Control-Allow-Origin: *");
         header("Access-Control-Allow-Headers: X-Requested-With");
         exit;
     }
     //Debug::log(print_r($req->requestVars(),true));
     // find the configuration
     $context = SyncContext::current();
     if (!$context) {
         return $this->fail(400);
     }
     $cfg = $context->getConfig($model);
     // is syncing this model allowed?
     if (!$cfg || !is_array($cfg) || !isset($cfg['type']) || $cfg['type'] == SYNC_NONE) {
         return $this->fail(403, 'Access denied');
     }
     $fields = isset($cfg['fields']) ? explode(',', $cfg['fields']) : array_keys(singleton($model)->db());
     if (count($fields) == 0) {
         return $this->fail(403, 'Access denied');
     }
     $fieldFilters = SyncFilterHelper::process_fields($fields);
     $fieldFilters['ID'] = false;
     $fieldFilters['LastEdited'] = false;
     $fields = array_keys($fieldFilters);
     // do we need to swap out for a parent table or anything?
     if (isset($cfg['model'])) {
         $model = $cfg['model'];
     }
     // build up the rest of the config with defaults
     if (!isset($cfg['filter'])) {
         $cfg['filter'] = array();
     }
     if (!isset($cfg['join'])) {
         $cfg['join'] = array();
     }
     if (!isset($cfg['sort'])) {
         $cfg['sort'] = '';
     }
     if (!isset($cfg['limit'])) {
         $cfg['limit'] = '';
     }
     // check authentication
     if (!$context->checkAuth($req->requestVars())) {
         return $this->fail(403, 'Incorrect or invalid authentication');
     }
     // there are a few magic values that can be used in the filters:
     // :future
     // :last X days
     $cfg['filter'] = SyncFilterHelper::process_filters($cfg['filter']);
     // fill in any blanks in the filters based on the request input
     $replacements = $context->getFilterVariables($req->requestVars());
     $cfg['filter'] = str_replace(array_keys($replacements), array_values($replacements), $cfg['filter']);
     // input arrays
     $insert = $req->requestVar('insert') ? json_decode($req->requestVar('insert'), true) : array();
     $check = $req->requestVar('check') ? json_decode($req->requestVar('check'), true) : array();
     $update = $req->requestVar('update') ? json_decode($req->requestVar('update'), true) : array();
     // output arrays
     $clientSend = array();
     $clientInsert = array();
     $clientUpdate = array();
     $clientDelete = array();
     // check modification times on any existing records
     // NOTE: if update is set we assume this is the second request (#3 above)
     if (count($update) == 0) {
         if ($cfg['type'] == SYNC_DOWN || $cfg['type'] == SYNC_FULL) {
             $list = DataObject::get($model);
             if ($cfg['filter']) {
                 $list = $list->filter($cfg['filter']);
             }
             if ($cfg['sort']) {
                 $list = $list->sort($cfg['sort']);
             }
             if ($cfg['limit']) {
                 $list = $list->limit($cfg['limit']);
             }
             if ($cfg['join'] && count($cfg['join']) > 0) {
                 if (!is_array($cfg['join'])) {
                     throw new Exception('Invalid join syntax');
                 }
                 $fn = count($cfg['join']) > 2 ? $cfg['join'] . 'Join' : 'innerJoin';
                 $list = $list->{$fn}($cfg['join'][0], $cfg['join'][1]);
             }
             //$map = $list->map('ID', 'LastEdited');
             $map = array();
             $objMap = array();
             foreach ($list as $rec) {
                 $map[$rec->ID] = strtotime($rec->LastEdited);
                 $objMap[$rec->ID] = $rec;
             }
             // take out the id's that are up-to-date form the map
             // also add any inserts and deletes at this point
             if (is_array($check)) {
                 foreach ($check as $rec) {
                     if (isset($map[$rec['ID']])) {
                         $serverTS = $map[$rec['ID']];
                         $clientTS = max($rec['TS'], 0);
                         if ($serverTS > $clientTS) {
                             // the server is newer than the client
                             // mark it to be sent back as a clientUpdate
                             $clientUpdate[] = self::to_array($objMap[$rec['ID']], $fields, $fieldFilters);
                         } elseif ($clientTS > $serverTS) {
                             // the version on the client is newer than the server
                             // add it to the clientSend list (i.e. request the data back from the client)
                             $clientSend[] = $rec['ID'];
                         } else {
                             // the versions are the same, leave well enough alone
                         }
                         // $objMap is now our insert list, so we remove this id from it
                         unset($objMap[$rec['ID']]);
                     } else {
                         // if it's present on the client WITH an ID but not present
                         // on the server, it means we've deleted it and need to notify
                         // the client
                         $clientDelete[] = $rec['ID'];
                     }
                 }
             }
             // anything left on the $map right now needs to be inserted
             if (count($objMap) > 0) {
                 foreach ($objMap as $id => $obj) {
                     $clientInsert[] = self::to_array($obj, $fields, $fieldFilters);
                 }
             }
         }
         // insert any new records
         if (($cfg['type'] == SYNC_FULL || $cfg['type'] == SYNC_UP) && is_array($insert)) {
             foreach ($insert as $rec) {
                 unset($rec['ID']);
                 unset($rec['LocalID']);
                 $obj = new $model();
                 $obj->castedUpdate(self::filter_fields($rec, $fields));
                 $obj->write();
                 // send the object back so it gets an id, etc
                 if ($cfg['type'] == SYNC_FULL) {
                     $clientInsert[] = self::to_array($obj, $fields, $fieldFilters);
                 }
             }
         }
         // NOTE: for SYNC_UP, if there do happen to be any records left
         // on the client, we want to tell it to delete them. that probably
         // means the model has changed from sync_full to sync_up OR
         // there was a bug at some point. Best to clean up the mess.
         if ($cfg['type'] == SYNC_UP && is_array($check) && count($check) > 0) {
             foreach ($check as $rec) {
                 $clientDelete[] = $rec['ID'];
             }
         }
     } else {
         if (($cfg['type'] == SYNC_FULL || $cfg['type'] == SYNC_UP) && is_array($update)) {
             // update records
             foreach ($update as $rec) {
                 $obj = DataObject::get_by_id($model, $rec['ID']);
                 unset($rec['ID']);
                 unset($rec['LocalID']);
                 unset($rec['ClassName']);
                 $obj->castedUpdate(self::filter_fields($rec, $fields));
                 $obj->write();
             }
         }
     }
     // respond
     return $this->respond(array('ok' => 1, 'send' => $clientSend, 'update' => $clientUpdate, 'insert' => $clientInsert, 'del' => $clientDelete));
 }
 /**
  * This is the action that gets executed when a GridField_AlterAction gets clicked.
  *
  * @param array $data
  * @param Form $form
  * @param SS_HTTPRequest $request
  *
  * @return string
  */
 public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request)
 {
     $data = $request->requestVars();
     // Protection against CSRF attacks
     $token = $this->getForm()->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."));
     }
     $name = $this->getName();
     $fieldData = null;
     if (isset($data[$name])) {
         $fieldData = $data[$name];
     }
     $state = $this->getState(false);
     if (isset($fieldData['GridState'])) {
         $state->setValue($fieldData['GridState']);
     }
     foreach ($data as $dataKey => $dataValue) {
         if (preg_match('/^action_gridFieldAlterAction\\?StateID=(.*)/', $dataKey, $matches)) {
             $stateChange = Session::get($matches[1]);
             $actionName = $stateChange['actionName'];
             $arguments = array();
             if (isset($stateChange['args'])) {
                 $arguments = $stateChange['args'];
             }
             $html = $this->handleAlterAction($actionName, $arguments, $data);
             if ($html) {
                 return $html;
             }
         }
     }
     if ($request->getHeader('X-Pjax') === 'CurrentField') {
         return $this->FieldHolder();
     }
     return $form->forTemplate();
 }
 /**
  * Handles requests to reorder a set of IDs in a specific order.
  *
  * @param GridField $grid
  * @param SS_HTTPRequest $request
  * @return SS_HTTPResponse
  */
 public function handleReorder($grid, $request)
 {
     $list = $grid->getList();
     $modelClass = $grid->getModelClass();
     if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
         $this->httpError(403);
     } else {
         if (!$list instanceof ManyManyList && !singleton($modelClass)->canEdit()) {
             $this->httpError(403);
         }
     }
     $ids = $request->postVar('order');
     $field = $this->getSortField();
     if (!is_array($ids)) {
         $this->httpError(400);
     }
     $sortterm = '';
     if ($this->extraSortFields) {
         if (is_array($this->extraSortFields)) {
             foreach ($this->extraSortFields as $col => $dir) {
                 $sortterm .= "{$col} {$dir}, ";
             }
         } else {
             $sortterm = $this->extraSortFields . ', ';
         }
     }
     $sortterm .= '"' . $this->getSortTable($list) . '"."' . $field . '"';
     $items = $list->filter('ID', $ids)->sort($sortterm);
     // Ensure that each provided ID corresponded to an actual object.
     if (count($items) != count($ids)) {
         $this->httpError(404);
     }
     // Save any un-comitted changes to the gridfield
     if (($form = $grid->getForm()) && ($record = $form->getRecord())) {
         $form->loadDataFrom($request->requestVars(), true);
         $grid->saveInto($record);
     }
     // Populate each object we are sorting with a sort value.
     $this->populateSortValues($items);
     // Generate the current sort values.
     if ($items instanceof ManyManyList) {
         $current = array();
         foreach ($items->toArray() as $record) {
             // NOTE: _SortColumn0 is the first ->sort() field
             //		 used by SS when functions are detected in a SELECT
             //	     or CASE WHEN.
             if (isset($record->_SortColumn0)) {
                 $current[$record->ID] = $record->_SortColumn0;
             } else {
                 $current[$record->ID] = $record->{$field};
             }
         }
     } else {
         $current = $items->map('ID', $field)->toArray();
     }
     // Perform the actual re-ordering.
     $this->reorderItems($list, $current, $ids);
     return $grid->FieldHolder();
 }
 /**
  * Method to determine what to do with the request and returns what will be processed
  * by the output processor
  * @param  \SS_HTTPRequest $request
  * @return array
  */
 public function process(\SS_HTTPRequest $request)
 {
     $data = $request->requestVars();
     $shippingFields = $this->shippingHandler->getDynamicMethods();
     // populate defaults
     foreach ($data as $key => $value) {
         if (in_array($key, $shippingFields)) {
             if ($key === 'Country') {
                 $this->shippingHandler->setCountry(new Identifier($value));
             } else {
                 $this->shippingHandler->{$key} = $value;
             }
         }
     }
     // errors
     $errors = [];
     if (!isset($data['BillingFirstName']) || !$data['BillingFirstName']) {
         $errors['BillingFirstName_Error'] = ['Error' => 'Please enter a first name.'];
     }
     if (!isset($data['BillingSurname']) || !$data['BillingSurname']) {
         $errors['BillingSurname_Error'] = ['Error' => 'Please enter a surname.'];
     }
     if (!isset($data['BillingEmail']) || !filter_var($data['BillingEmail'], FILTER_VALIDATE_EMAIL)) {
         $errors['BillingEmail_Error'] = ['Error' => 'Please enter an email address.'];
     }
     if (!isset($data['BillingAddressLine1']) || !$data['BillingAddressLine1']) {
         $errors['BillingAddressLine1_Error'] = ['Error' => 'Please enter an address.'];
     }
     if (!isset($data['BillingCity']) || !$data['BillingAddressLine1']) {
         $errors['BillingCity_Error'] = ['Error' => 'Please enter a city.'];
     }
     if (!isset($data['BillingPostcode']) || !$data['BillingPostcode']) {
         $errors['BillingPostcode_Error'] = ['Error' => 'Please enter a postcode.'];
     }
     if (!isset($data['BillingCountry']) || !$data['BillingCountry']) {
         $errors['BillingCountry_Error'] = ['Error' => 'Please select a country.'];
     }
     // if the delivery is billing, populate those fields that we can
     if ($data['delivery'] == 'billing') {
         $this->shippingHandler->BillingAsShipping = true;
         $this->shippingHandler->FirstName = $data['BillingFirstName'];
         $this->shippingHandler->Surname = $data['BillingSurname'];
         $this->shippingHandler->Email = $data['BillingEmail'];
         $this->shippingHandler->AddressLine1 = $data['BillingAddressLine1'];
         $this->shippingHandler->AddressLine2 = $data['BillingAddressLine2'];
         $this->shippingHandler->City = $data['BillingCity'];
         $this->shippingHandler->Postcode = $data['BillingPostcode'];
         $this->shippingHandler->Country = $data['BillingCountry'];
     } else {
         if (!isset($data['FirstName']) || !$data['FirstName']) {
             $errors['FirstName_Error'] = ['Error' => 'Please enter a first name.'];
         }
         if (!isset($data['Surname']) || !$data['Surname']) {
             $errors['Surname_Error'] = ['Error' => 'Please enter a surname.'];
         }
         if (!isset($data['Email']) || !filter_var($data['Email'], FILTER_VALIDATE_EMAIL)) {
             $errors['Email_Error'] = ['Error' => 'Please enter an email address.'];
         }
         if (!isset($data['AddressLine1']) || !$data['AddressLine1']) {
             $errors['AddressLine1_Error'] = ['Error' => 'Please enter an address.'];
         }
         if (!isset($data['City']) || !$data['City']) {
             $errors['City_Error'] = ['Error' => 'Please enter a city.'];
         }
         if (!isset($data['Postcode']) || !$data['Postcode']) {
             $errors['Postcode_Error'] = ['Error' => 'Please enter a postcode.'];
         }
         if (!isset($data['Country']) || !$data['Country']) {
             $errors['Country_Error'] = ['Error' => 'Please select a country.'];
         }
         $this->shippingHandler->BillingAsShipping = false;
         $this->shippingHandler->FirstName = $data['BillingFirstName'];
         $this->shippingHandler->Surname = $data['BillingSurname'];
         $this->shippingHandler->Email = $data['BillingEmail'];
     }
     if (count($errors)) {
         return array_merge(['success' => false, 'Errors' => $errors], $data);
     }
     $this->shippingHandler->saveState();
     return ['success' => true];
 }
 /**
  * Controller's default action handler.  It will call the method named in "$Action", if that method
  * exists. If "$Action" isn't given, it will use "index" as a default.
  *
  * @param SS_HTTPRequest $request
  * @param string $action
  *
  * @return DBHTMLText|SS_HTTPResponse
  */
 protected function handleAction($request, $action)
 {
     foreach ($request->latestParams() as $k => $v) {
         if ($v || !isset($this->urlParams[$k])) {
             $this->urlParams[$k] = $v;
         }
     }
     $this->action = $action;
     $this->requestParams = $request->requestVars();
     if ($this->hasMethod($action)) {
         $result = parent::handleAction($request, $action);
         // If the action returns an array, customise with it before rendering the template.
         if (is_array($result)) {
             return $this->getViewer($action)->process($this->customise($result));
         } else {
             return $result;
         }
     }
     // Fall back to index action with before/after handlers
     $beforeResult = $this->extend('beforeCallActionHandler', $request, $action);
     if ($beforeResult) {
         return reset($beforeResult);
     }
     $result = $this->getViewer($action)->process($this);
     $afterResult = $this->extend('afterCallActionHandler', $request, $action, $result);
     if ($afterResult) {
         return reset($afterResult);
     }
     return $result;
 }
 /**
  * @param array $data
  * @param Form $form
  * @param SS_HTTPRequest $request
  * @return HTML|HTMLText|mixed
  */
 public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request)
 {
     $data = $request->requestVars();
     $stateHash = $this->getStateHash();
     // Check if we have encountered a reset action. We need to clear the state here before
     // the other components start accessing it.
     foreach ($data as $dataKey => $dataValue) {
         if (preg_match('/^action_gridFieldAlterAction\\?StateID=(.*)/', $dataKey, $matches)) {
             $stateChange = Session::get($matches[1]);
             $actionName = $stateChange['actionName'];
             if ($actionName === 'ResetState') {
                 Session::set($stateHash, null);
                 $this->state = new GridState($this);
             }
         }
     }
     foreach ($data as $dataKey => $dataValue) {
         if (preg_match('/^action_gridFieldAlterAction\\?StateID=(.*)/', $dataKey, $matches)) {
             $stateChange = Session::get($matches[1]);
             $actionName = $stateChange['actionName'];
             $arguments = array();
             if (isset($stateChange['args'])) {
                 $arguments = $stateChange['args'];
             }
             $html = $this->handleAlterAction($actionName, $arguments, $data);
             if ($html) {
                 return $html;
             }
         }
     }
     // The state is stored in the session so that we can access it on the next page load
     $this->setStateHash($this->state->Value());
     if ($request->getHeader('X-Pjax') === 'CurrentField') {
         return $this->FieldHolder();
     }
     return $form->forTemplate();
 }
 /**
  * Handles requests to reorder a set of IDs in a specific order.
  *
  * @param GridField $grid
  * @param SS_HTTPRequest $request
  * @return SS_HTTPResponse
  */
 public function handleReorder($grid, $request)
 {
     if (!$this->immediateUpdate) {
         $this->httpError(400);
     }
     $list = $grid->getList();
     $modelClass = $grid->getModelClass();
     if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
         $this->httpError(403);
     } else {
         if (!$list instanceof ManyManyList && !singleton($modelClass)->canEdit()) {
             $this->httpError(403);
         }
     }
     // Save any un-committed changes to the gridfield
     if (($form = $grid->getForm()) && ($record = $form->getRecord())) {
         $form->loadDataFrom($request->requestVars(), true);
         $grid->saveInto($record);
     }
     // Get records from the `GridFieldEditableColumns` column
     $data = $request->postVar($grid->getName());
     $sortedIDs = $this->getSortedIDs($data);
     if (!$this->executeReorder($grid, $sortedIDs)) {
         $this->httpError(400);
     }
     Controller::curr()->getResponse()->addHeader('X-Status', rawurlencode('Records reordered.'));
     return $grid->FieldHolder();
 }
 /**
  * @param SS_HTTPRequest $request
  *
  * @return DeploymentStrategy
  */
 public function getDeployStrategy(\SS_HTTPRequest $request)
 {
     return $this->Backend()->planDeploy($this, $request->requestVars());
 }