FIX Provide alternatives to session for storing GridField_FormAction state

This commit is contained in:
Guy Marriott 2018-11-21 16:55:57 +13:00
parent 41dc9229bf
commit b4c8f699eb
No known key found for this signature in database
GPG Key ID: A80F9ACCB86D3DA7
6 changed files with 176 additions and 14 deletions

6
_config/gridfield.yml Normal file
View File

@ -0,0 +1,6 @@
---
Name: gridfieldconfig
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Forms\GridField\FormAction\StateStore:
class: SilverStripe\Forms\GridField\FormAction\SessionStore

View File

@ -0,0 +1,51 @@
<?php
namespace SilverStripe\Forms\GridField\FormAction;
use SilverStripe\Control\HTTPRequest;
/**
* Stores GridField action state on an attribute on the action and then analyses request parameters to load it back
*/
class AttributeStore implements StateStore
{
/**
* @var HTTPRequest
*/
protected $request;
/**
* @param HTTPRequest $request
*/
public function __construct(HTTPRequest $request)
{
$this->request = $request;
}
/**
* Save the given state against the given ID returning an associative array to be added as attributes on the form
* action
*
* @param string $id
* @param array $state
* @return array
*/
public function save($id, array $state)
{
// Just save the state in the attributes of the action
return [
'data-action-state' => json_encode($state),
];
}
/**
* Load state for a given ID
*
* @param string $id
* @return mixed
*/
public function load($id)
{
// Check the request
return json_decode($this->request->requestVar('ActionState'), true);
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace SilverStripe\Forms\GridField\FormAction;
use SilverStripe\Control\HTTPRequest;
/**
* Stores GridField action state in the session in exactly the same way it has in the past
*/
class SessionStore implements StateStore
{
/**
* @var HTTPRequest
*/
protected $request;
/**
* @param HTTPRequest $request
*/
public function __construct(HTTPRequest $request)
{
$this->request = $request;
}
/**
* Save the given state against the given ID returning an associative array to be added as attributes on the form
* action
*
* @param string $id
* @param array $state
* @return array
*/
public function save($id, array $state)
{
$this->request->getSession()->set($id, $state);
// This adapter does not require any additional attributes...
return [];
}
/**
* Load state for a given ID
*
* @param string $id
* @return mixed
*/
public function load($id)
{
return $this->request->getSession()->get($id);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace SilverStripe\Forms\GridField\FormAction;
use SilverStripe\Control\HTTPRequest;
interface StateStore
{
/**
* @param HTTPRequest $request
*/
public function __construct(HTTPRequest $request);
/**
* Save the given state against the given ID returning an associative array to be added as attributes on the form
* action
*
* @param string $id
* @param array $state
* @return array
*/
public function save($id, array $state);
/**
* Load state for a given ID
*
* @param string $id
* @return mixed
*/
public function load($id);
}

View File

@ -9,8 +9,11 @@ use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\RequestHandler; use SilverStripe\Control\RequestHandler;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\Form; use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormField; use SilverStripe\Forms\FormField;
use SilverStripe\Forms\GridField\FormAction\SessionStore;
use SilverStripe\Forms\GridField\FormAction\StateStore;
use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
@ -1009,9 +1012,14 @@ class GridField extends FormField
$state->setValue($fieldData['GridState']); $state->setValue($fieldData['GridState']);
} }
// Fetch the store for the "state" of actions (not the GridField)
/** @var StateStore $store */
$store = Injector::inst()->create(StateStore::class . '.' . $this->getName(), $request);
foreach ($data as $dataKey => $dataValue) { foreach ($data as $dataKey => $dataValue) {
if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) { if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) {
$stateChange = $request->getSession()->get($matches[1]); $stateChange = $store->load($matches[1]);
$actionName = $stateChange['actionName']; $actionName = $stateChange['actionName'];
$arguments = array(); $arguments = array();

View File

@ -3,8 +3,10 @@
namespace SilverStripe\Forms\GridField; namespace SilverStripe\Forms\GridField;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\Form; use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormAction; use SilverStripe\Forms\FormAction;
use SilverStripe\Forms\GridField\FormAction\StateStore;
/** /**
* This class is the base class when you want to have an action that alters the state of the * This class is the base class when you want to have an action that alters the state of the
@ -12,6 +14,11 @@ use SilverStripe\Forms\FormAction;
*/ */
class GridField_FormAction extends FormAction class GridField_FormAction extends FormAction
{ {
/**
* A common string prefix for keys generated to store form action "state" against
*/
const STATE_KEY_PREFIX = 'gf_';
/** /**
* @var GridField * @var GridField
*/ */
@ -80,29 +87,39 @@ class GridField_FormAction extends FormAction
*/ */
public function getAttributes() public function getAttributes()
{ {
// Store state in session, and pass ID to client side. // Determine the state that goes with this action
$state = array( $state = array(
'grid' => $this->getNameFromParent(), 'grid' => $this->getNameFromParent(),
'actionName' => $this->actionName, 'actionName' => $this->actionName,
'args' => $this->args, 'args' => $this->args,
); );
// Ensure $id doesn't contain only numeric characters // Generate a key and attach it to the action name
$id = 'gf_' . substr(md5(serialize($state)), 0, 8); $key = static::STATE_KEY_PREFIX . substr(md5(serialize($state)), 0, 8);
// Note: This field needs to be less than 65 chars, otherwise Suhosin security patch will strip it
$name = 'action_gridFieldAlterAction?StateID=' . $key;
$session = Controller::curr()->getRequest()->getSession(); // Define attributes
$session->set($id, $state); $attributes = array(
$actionData['StateID'] = $id; 'name' => $name,
return array_merge(
parent::getAttributes(),
array(
// Note: This field needs to be less than 65 chars, otherwise Suhosin security patch
// will strip it from the requests
'name' => 'action_gridFieldAlterAction' . '?' . http_build_query($actionData),
'data-url' => $this->gridField->Link(), 'data-url' => $this->gridField->Link(),
'type' => "button", 'type' => "button",
) );
// Create a "store" for the "state" of this action
/** @var StateStore $store */
$store = Injector::inst()->create(
StateStore::class . '.' . $this->gridField->getName(),
// For some reason `getRequest` on GridField_FormAction does not return the correct request
Controller::curr()->getRequest()
);
// Store the state and update attributes as required
$attributes += $store->save($key, $state);
// Return attributes
return array_merge(
parent::getAttributes(),
$attributes
); );
} }