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; }