mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Issue 3357 - Add GridField Readonly Transformation
GridField doesn't have a valid readonly state if it's value is set to an Object without `forTemplate()`. The default behaviour is to render a ReadonlyField, but given GridField is a complex type this isn't suitable. This bugfix provides a transformation method to render only components that are whitelisted to provide a readonly state. @see #3357 - https://github.com/silverstripe/silverstripe-framework/issues/3357
This commit is contained in:
parent
37a266f2f0
commit
4c3a068859
@ -105,6 +105,21 @@ class GridField extends FormField
|
|||||||
*/
|
*/
|
||||||
protected $name = '';
|
protected $name = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A whitelist of readonly component classes allowed if performReadonlyTransform is called.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $readonlyComponents = array(
|
||||||
|
GridFieldDetailForm::class,
|
||||||
|
GridFieldDataColumns::class,
|
||||||
|
GridFieldConfig_RecordViewer::class,
|
||||||
|
GridFieldToolbarHeader::class,
|
||||||
|
GridFieldPageCount::class,
|
||||||
|
GridFieldPaginator::class,
|
||||||
|
GridState_Component::class
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pattern used for looking up
|
* Pattern used for looking up
|
||||||
*/
|
*/
|
||||||
@ -193,6 +208,60 @@ class GridField extends FormField
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overload the readonly components for this gridfield.
|
||||||
|
*
|
||||||
|
* @param array $components an array map of component class references to whitelist for a readonly version.
|
||||||
|
*/
|
||||||
|
public function setReadonlyComponents(array $components)
|
||||||
|
{
|
||||||
|
$this->readonlyComponents = $components;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the readonly components
|
||||||
|
*
|
||||||
|
* @return array a map of component classes.
|
||||||
|
*/
|
||||||
|
public function getReadonlyComponents()
|
||||||
|
{
|
||||||
|
return $this->readonlyComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Readonly transformation to remove actions which shouldn't be present for a readonly state.
|
||||||
|
*
|
||||||
|
* @return GridField
|
||||||
|
*/
|
||||||
|
public function performReadonlyTransformation()
|
||||||
|
{
|
||||||
|
$copy = clone $this;
|
||||||
|
$copy->setReadonly(true);
|
||||||
|
|
||||||
|
// get the whitelist for allowable readonly components
|
||||||
|
$allowedComponents = $this->getReadonlyComponents();
|
||||||
|
foreach ($this->getConfig()->getComponents() as $component) {
|
||||||
|
|
||||||
|
// if a component doesn't exist, remove it from the readonly version.
|
||||||
|
if (!in_array(get_class($component), $allowedComponents)) {
|
||||||
|
$copy->getConfig()->removeComponent($component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disabling the gridfield should have the same affect as making it readonly (removing all action items).
|
||||||
|
*
|
||||||
|
* @return GridField
|
||||||
|
*/
|
||||||
|
public function performDisabledTransformation(){
|
||||||
|
parent::performDisabledTransformation();
|
||||||
|
|
||||||
|
return $this->performReadonlyTransformation();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return GridFieldConfig
|
* @return GridFieldConfig
|
||||||
*/
|
*/
|
||||||
|
87
tests/php/Forms/GridField/GridFieldReadonlyTest.php
Normal file
87
tests/php/Forms/GridField/GridFieldReadonlyTest.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Forms\Tests\GridField;
|
||||||
|
|
||||||
|
use SilverStripe\Forms\GridField\GridField_ActionMenu;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldAddNewButton;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldButtonRow;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldPageCount;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldPaginator;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldToolbarHeader;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Cheerleader;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Team;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
|
use SilverStripe\Versioned\VersionedGridFieldState\VersionedGridFieldState;
|
||||||
|
|
||||||
|
class GridFieldReadonlyTest extends SapphireTest
|
||||||
|
{
|
||||||
|
protected static $fixture_file = 'GridFieldReadonlyTest.yml';
|
||||||
|
|
||||||
|
protected static $extra_dataobjects = array(
|
||||||
|
Team::class,
|
||||||
|
Cheerleader::class,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CMS can set the value of a GridField to be a hasMany relation, which needs a readonly state.
|
||||||
|
* This test ensures GridField has a readonly transformation.
|
||||||
|
*/
|
||||||
|
public function testReadOnlyTransformation()
|
||||||
|
{
|
||||||
|
// Build a hasMany Relation via getComponents like ModelAdmin does.
|
||||||
|
$components = Team::get_one(Team::class)
|
||||||
|
->getComponents('Cheerleaders');
|
||||||
|
|
||||||
|
$gridConfig = GridFieldConfig_RelationEditor::create();
|
||||||
|
|
||||||
|
// Build some commonly used components to make sure we're only allowing the correct components
|
||||||
|
$gridConfig->addComponent(new GridFieldButtonRow('before'));
|
||||||
|
$gridConfig->addComponent(new GridFieldAddNewButton('buttons-before-left'));
|
||||||
|
$gridConfig->addComponent(new GridFieldAddExistingAutocompleter('buttons-before-right'));
|
||||||
|
$gridConfig->addComponent(new GridFieldToolbarHeader());
|
||||||
|
$gridConfig->addComponent($sort = new GridFieldSortableHeader());
|
||||||
|
$gridConfig->addComponent($filter = new GridFieldFilterHeader());
|
||||||
|
$gridConfig->addComponent(new GridFieldDataColumns());
|
||||||
|
$gridConfig->addComponent(new GridFieldEditButton());
|
||||||
|
$gridConfig->addComponent(new GridFieldDeleteAction(true));
|
||||||
|
$gridConfig->addComponent(new GridField_ActionMenu());
|
||||||
|
$gridConfig->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||||
|
$gridConfig->addComponent($pagination = new GridFieldPaginator(2));
|
||||||
|
$gridConfig->addComponent(new GridFieldDetailForm());
|
||||||
|
$gridConfig->addComponent(new GridFieldDeleteAction());
|
||||||
|
$gridConfig->addComponent(new VersionedGridFieldState());
|
||||||
|
|
||||||
|
$gridField = GridField::create(
|
||||||
|
'Cheerleaders',
|
||||||
|
'Cheerleaders',
|
||||||
|
$components,
|
||||||
|
$gridConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
// Model Admin sets the value of the GridField directly to the relation, which doesn't have a forTemplate()
|
||||||
|
// function, if we rely on FormField to render into a ReadonlyField we'll get an error as HasManyRelation
|
||||||
|
// doesn't have a forTemplate() function.
|
||||||
|
$gridField->setValue($components);
|
||||||
|
$gridField->setModelClass(Cheerleader::class);
|
||||||
|
|
||||||
|
// This function is called by $form->makeReadonly().
|
||||||
|
$readonlyGridField = $gridField->performReadonlyTransformation();
|
||||||
|
|
||||||
|
// if we've made it this far, then the GridField is at least transforming correctly.
|
||||||
|
$readonlyComponents = $readonlyGridField->getReadonlyComponents();
|
||||||
|
|
||||||
|
// assert that all the components in the readonly version are present in the whitelist.
|
||||||
|
foreach($readonlyGridField->getConfig()->getComponents() as $component){
|
||||||
|
$this->assertTrue(in_array(get_class($component), $readonlyComponents));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
tests/php/Forms/GridField/GridFieldReadonlyTest.yml
Normal file
27
tests/php/Forms/GridField/GridFieldReadonlyTest.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
SilverStripe\Forms\Tests\GridField\GridFieldTest\Team:
|
||||||
|
team1:
|
||||||
|
Name: Team 1
|
||||||
|
City: Cologne
|
||||||
|
team2:
|
||||||
|
Name: Team 2
|
||||||
|
City: Wellington
|
||||||
|
team3:
|
||||||
|
Name: Team 3
|
||||||
|
City: Auckland
|
||||||
|
team4:
|
||||||
|
Name: Team 4
|
||||||
|
City: Melbourne
|
||||||
|
|
||||||
|
SilverStripe\Forms\Tests\GridField\GridFieldTest\Cheerleader:
|
||||||
|
cheerleader1:
|
||||||
|
Name: Heather
|
||||||
|
Team: =>SilverStripe\Forms\Tests\GridField\GridFieldTest\Team.team1
|
||||||
|
cheerleader2:
|
||||||
|
Name: Bob
|
||||||
|
Team: =>SilverStripe\Forms\Tests\GridField\GridFieldTest\Team.team1
|
||||||
|
cheerleader3:
|
||||||
|
Name: Jenny
|
||||||
|
Team: =>SilverStripe\Forms\Tests\GridField\GridFieldTest\Team.team1
|
||||||
|
cheerleader4:
|
||||||
|
Name: Sam
|
||||||
|
Team: =>SilverStripe\Forms\Tests\GridField\GridFieldTest\Team.team1
|
Loading…
Reference in New Issue
Block a user