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 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 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 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 HTTPRequest('POST', 'admin/crm', $getVars, $postVars);
     $this->assertEquals($requestVars, $request->requestVars(), 'Nested GET parameters should supplement POST parameters');
 }
 /**
  * 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 HTTPRequest $request
  * @throws 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)) {
         $this->setButtonClicked($funcName);
     }
     // Permission checks (first on controller, then falling back to form)
     if ($this->controller->hasMethod($funcName) && !$this->controller->checkAccessAction($funcName) && !$this->buttonClicked()) {
         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);
 }
 /**
  * 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 HTTPRequest $request
  * @param string $action
  *
  * @return DBHTMLText|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;
 }
 /**
  * This is the action that gets executed when a GridField_AlterAction gets clicked.
  *
  * @param array $data
  * @param Form $form
  * @param HTTPRequest $request
  *
  * @return string
  */
 public function gridFieldAlterAction($data, $form, 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);
     /** @skipUpgrade */
     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();
 }
 /**
  * Fetches a collection of files by ParentID.
  *
  * @param HTTPRequest $request
  * @return HTTPResponse
  */
 public function apiReadFolder(HTTPRequest $request)
 {
     $params = $request->requestVars();
     $items = array();
     $parentId = null;
     $folderID = null;
     if (!isset($params['id']) && !strlen($params['id'])) {
         $this->httpError(400);
     }
     $folderID = (int) $params['id'];
     /** @var Folder $folder */
     $folder = $folderID ? Folder::get()->byID($folderID) : Folder::singleton();
     if (!$folder) {
         $this->httpError(400);
     }
     // TODO Limit results to avoid running out of memory (implement client-side pagination)
     $files = $this->getList()->filter('ParentID', $folderID);
     if ($files) {
         /** @var File $file */
         foreach ($files as $file) {
             if (!$file->canView()) {
                 continue;
             }
             $items[] = $this->getObjectFromData($file);
         }
     }
     // Build parents (for breadcrumbs)
     $parents = [];
     $next = $folder->Parent();
     while ($next && $next->exists()) {
         array_unshift($parents, ['id' => $next->ID, 'title' => $next->getTitle(), 'filename' => $next->getFilename()]);
         if ($next->ParentID) {
             $next = $next->Parent();
         } else {
             break;
         }
     }
     $column = 'title';
     $direction = 'asc';
     if (isset($params['sort'])) {
         list($column, $direction) = explode(',', $params['sort']);
     }
     $multiplier = $direction === 'asc' ? 1 : -1;
     usort($items, function ($a, $b) use($column, $multiplier) {
         if (!isset($a[$column]) || !isset($b[$column])) {
             return 0;
         }
         if ($a['type'] === 'folder' && $b['type'] !== 'folder') {
             return -1;
         }
         if ($b['type'] === 'folder' && $a['type'] !== 'folder') {
             return 1;
         }
         $numeric = is_numeric($a[$column]) && is_numeric($b[$column]);
         $fieldA = $numeric ? floatval($a[$column]) : strtolower($a[$column]);
         $fieldB = $numeric ? floatval($b[$column]) : strtolower($b[$column]);
         if ($fieldA < $fieldB) {
             return $multiplier * -1;
         }
         if ($fieldA > $fieldB) {
             return $multiplier;
         }
         return 0;
     });
     $page = isset($params['page']) ? $params['page'] : 0;
     $limit = isset($params['limit']) ? $params['limit'] : $this->config()->page_length;
     $filteredItems = array_slice($items, $page * $limit, $limit);
     // Build response
     $response = new HTTPResponse();
     $response->addHeader('Content-Type', 'application/json');
     $response->setBody(json_encode(['files' => $filteredItems, 'title' => $folder->getTitle(), 'count' => count($items), 'parents' => $parents, 'parent' => $parents ? $parents[count($parents) - 1] : null, 'parentID' => $folder->exists() ? $folder->ParentID : null, 'folderID' => $folderID, 'canEdit' => $folder->canEdit(), 'canDelete' => $folder->canArchive()]));
     return $response;
 }