From b4c8f699eb4dd75089100c1b180cd0df25146206 Mon Sep 17 00:00:00 2001 From: Guy Marriott Date: Wed, 21 Nov 2018 16:55:57 +1300 Subject: [PATCH] FIX Provide alternatives to session for storing GridField_FormAction state --- _config/gridfield.yml | 6 +++ .../GridField/FormAction/AttributeStore.php | 51 +++++++++++++++++++ .../GridField/FormAction/SessionStore.php | 50 ++++++++++++++++++ src/Forms/GridField/FormAction/StateStore.php | 30 +++++++++++ src/Forms/GridField/GridField.php | 10 +++- src/Forms/GridField/GridField_FormAction.php | 43 +++++++++++----- 6 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 _config/gridfield.yml create mode 100644 src/Forms/GridField/FormAction/AttributeStore.php create mode 100644 src/Forms/GridField/FormAction/SessionStore.php create mode 100644 src/Forms/GridField/FormAction/StateStore.php diff --git a/_config/gridfield.yml b/_config/gridfield.yml new file mode 100644 index 000000000..a47da229f --- /dev/null +++ b/_config/gridfield.yml @@ -0,0 +1,6 @@ +--- +Name: gridfieldconfig +--- +SilverStripe\Core\Injector\Injector: + SilverStripe\Forms\GridField\FormAction\StateStore: + class: SilverStripe\Forms\GridField\FormAction\SessionStore diff --git a/src/Forms/GridField/FormAction/AttributeStore.php b/src/Forms/GridField/FormAction/AttributeStore.php new file mode 100644 index 000000000..60f736d74 --- /dev/null +++ b/src/Forms/GridField/FormAction/AttributeStore.php @@ -0,0 +1,51 @@ +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); + } +} diff --git a/src/Forms/GridField/FormAction/SessionStore.php b/src/Forms/GridField/FormAction/SessionStore.php new file mode 100644 index 000000000..35f2337b8 --- /dev/null +++ b/src/Forms/GridField/FormAction/SessionStore.php @@ -0,0 +1,50 @@ +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); + } +} diff --git a/src/Forms/GridField/FormAction/StateStore.php b/src/Forms/GridField/FormAction/StateStore.php new file mode 100644 index 000000000..e415a518b --- /dev/null +++ b/src/Forms/GridField/FormAction/StateStore.php @@ -0,0 +1,30 @@ +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) { if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) { - $stateChange = $request->getSession()->get($matches[1]); + $stateChange = $store->load($matches[1]); + $actionName = $stateChange['actionName']; $arguments = array(); diff --git a/src/Forms/GridField/GridField_FormAction.php b/src/Forms/GridField/GridField_FormAction.php index 10dc772bc..a253d8b3b 100644 --- a/src/Forms/GridField/GridField_FormAction.php +++ b/src/Forms/GridField/GridField_FormAction.php @@ -3,8 +3,10 @@ namespace SilverStripe\Forms\GridField; use SilverStripe\Control\Controller; +use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\Form; 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 @@ -12,6 +14,11 @@ use SilverStripe\Forms\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 */ @@ -80,29 +87,39 @@ class GridField_FormAction extends FormAction */ public function getAttributes() { - // Store state in session, and pass ID to client side. + // Determine the state that goes with this action $state = array( 'grid' => $this->getNameFromParent(), 'actionName' => $this->actionName, 'args' => $this->args, ); - // Ensure $id doesn't contain only numeric characters - $id = 'gf_' . substr(md5(serialize($state)), 0, 8); + // Generate a key and attach it to the action name + $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(); - $session->set($id, $state); - $actionData['StateID'] = $id; + // Define attributes + $attributes = array( + 'name' => $name, + 'data-url' => $this->gridField->Link(), + '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(), - 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(), - 'type' => "button", - ) + $attributes ); }