Compare commits
5 Commits
654b7294fa
...
fc40420daa
Author | SHA1 | Date |
---|---|---|
Niklas Forsdahl | fc40420daa | |
Niklas Forsdahl | 4fc20fb771 | |
Niklas Forsdahl | f8c777dcc5 | |
Niklas Forsdahl | c415d43731 | |
Niklas Forsdahl | c043220949 |
|
@ -612,6 +612,8 @@
|
|||
$('.ss-gridfield-orderable.has-nested > .grid-field__table > tbody, .ss-gridfield-orderable.nested > .grid-field__table > tbody').entwine({
|
||||
onadd: function() {
|
||||
this._super();
|
||||
let preventReorderUpdate = false;
|
||||
let updateTimeouts = [];
|
||||
let gridField = this.getGridField();
|
||||
if (gridField.data("url-movetoparent")) {
|
||||
let parentID = 0;
|
||||
|
@ -638,7 +640,7 @@
|
|||
window.clearTimeout(timeout);
|
||||
}
|
||||
let childID = ui.item.attr('data-id');
|
||||
let parentIntoChild = $(e.target).closest('.grid-field[data-name*="[GridFieldNestedForm]['+childID+']"]').length;
|
||||
let parentIntoChild = $(e.target).closest('.grid-field[data-name*="-GridFieldNestedForm-'+childID+'"]').length;
|
||||
if (parentIntoChild) {
|
||||
// parent dragged into child, cancel sorting
|
||||
ui.sender.sortable("cancel");
|
||||
|
|
|
@ -8,12 +8,16 @@ use SilverStripe\Control\Controller;
|
|||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_DataManipulator;
|
||||
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
|
||||
use SilverStripe\Forms\GridField\GridField_SaveHandler;
|
||||
use SilverStripe\Forms\GridField\GridField_URLHandler;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
|
||||
use SilverStripe\Forms\GridField\GridFieldStateAware;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
|
@ -26,19 +30,27 @@ use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
|
|||
/**
|
||||
* Gridfield component for nesting GridFields
|
||||
*/
|
||||
class GridFieldNestedForm extends GridFieldDetailForm implements
|
||||
class GridFieldNestedForm extends AbstractGridFieldComponent implements
|
||||
GridField_URLHandler,
|
||||
GridField_ColumnProvider,
|
||||
GridField_SaveHandler,
|
||||
GridField_HTMLProvider,
|
||||
GridField_DataManipulator
|
||||
{
|
||||
use Configurable, GridFieldStateAware;
|
||||
|
||||
const POST_KEY = 'GridFieldNestedForm';
|
||||
|
||||
private static $allowed_actions = [
|
||||
'handleNestedItem'
|
||||
];
|
||||
|
||||
private static $max_nesting_level = 10;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
protected $expandNested = false;
|
||||
protected $forceCloseNested = false;
|
||||
protected $gridField = null;
|
||||
|
@ -46,6 +58,7 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
protected $relationName = 'Children';
|
||||
protected $inlineEditable = false;
|
||||
protected $canExpandCheck = null;
|
||||
protected $maxNestingLevel = null;
|
||||
|
||||
public function __construct($name = 'NestedForm')
|
||||
{
|
||||
|
@ -130,6 +143,32 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum nesting level allowed for nested grid fields
|
||||
* @param int $level
|
||||
*/
|
||||
public function setMaxNestingLevel($level)
|
||||
{
|
||||
$this->maxNestingLevel = $level;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxNestingLevel()
|
||||
{
|
||||
return $this->maxNestingLevel ?: $this->config()->max_nesting_level;
|
||||
}
|
||||
|
||||
protected function getNestingLevel($gridField)
|
||||
{
|
||||
$level = 0;
|
||||
$c = $gridField->getForm()->getController();
|
||||
while ($c && $c instanceof GridFieldDetailForm_ItemRequest) {
|
||||
$c = $c->getController();
|
||||
$level++;
|
||||
}
|
||||
return $level;
|
||||
}
|
||||
|
||||
public function getColumnMetadata($gridField, $columnName)
|
||||
{
|
||||
return ['title' => ''];
|
||||
|
@ -154,6 +193,10 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
|
||||
public function getColumnContent($gridField, $record, $columnName)
|
||||
{
|
||||
$nestingLevel = $this->getNestingLevel($gridField);
|
||||
if ($nestingLevel >= $this->getMaxNestingLevel()) {
|
||||
return '';
|
||||
}
|
||||
$gridField->addExtraClass('has-nested');
|
||||
if ($record->ID && $record->exists()) {
|
||||
$this->gridField = $gridField;
|
||||
|
@ -163,9 +206,14 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
return '';
|
||||
}
|
||||
if ($this->canExpandCheck) {
|
||||
if (is_callable($this->canExpandCheck) && !call_user_func($this->canExpandCheck, $record)) {
|
||||
if (is_callable($this->canExpandCheck)
|
||||
&& !call_user_func($this->canExpandCheck, $record)
|
||||
) {
|
||||
return '';
|
||||
} elseif (is_string($this->canExpandCheck) && $record->hasMethod($this->canExpandCheck) && !$this->record->{$this->canExpandCheck}($record)) {
|
||||
} elseif (is_string($this->canExpandCheck)
|
||||
&& $record->hasMethod($this->canExpandCheck)
|
||||
&& !$this->record->{$this->canExpandCheck}($record)
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +221,11 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
$className = str_replace('\\', '-', get_class($record));
|
||||
$state = $gridField->State->GridFieldNestedForm;
|
||||
$stateRelation = $className.'-'.$record->ID.'-'.$this->relationName;
|
||||
if (!$this->forceCloseNested && (($this->expandNested && $record->$relationName()->count() > 0) || ($state && (int)$state->getData($stateRelation) === 1))) {
|
||||
$openState = $state && (int)$state->getData($stateRelation) === 1;
|
||||
$forceExpand = $this->expandNested && $record->$relationName()->count() > 0;
|
||||
if (!$this->forceCloseNested
|
||||
&& ($forceExpand || $openState)
|
||||
) {
|
||||
$toggle = 'open';
|
||||
}
|
||||
|
||||
|
@ -219,7 +271,11 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
if ($id) {
|
||||
// should be possible either on parent or child grid field, or nested grid field from parent
|
||||
$parent = $to ? $list->byID($to) : null;
|
||||
if (!$parent && $to && $gridField->getForm()->getController() instanceof GridFieldNestedFormItemRequest && $gridField->getForm()->getController()->getRecord()->ID == $to) {
|
||||
if (!$parent
|
||||
&& $to
|
||||
&& $gridField->getForm()->getController() instanceof GridFieldNestedFormItemRequest
|
||||
&& $gridField->getForm()->getController()->getRecord()->ID == $to
|
||||
) {
|
||||
$parent = $gridField->getForm()->getController()->getRecord();
|
||||
}
|
||||
$child = $list->byID($id);
|
||||
|
@ -264,6 +320,10 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
|
||||
public function handleNestedItem(GridField $gridField, $request = null, $record = null)
|
||||
{
|
||||
$nestingLevel = $this->getNestingLevel($gridField);
|
||||
if ($nestingLevel >= $this->getMaxNestingLevel()) {
|
||||
throw new Exception('Max nesting level reached');
|
||||
}
|
||||
if (!$record && $request) {
|
||||
$recordID = $request->param('RecordID');
|
||||
$record = $gridField->getList()->byID($recordID);
|
||||
|
@ -276,12 +336,19 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
return '';
|
||||
}
|
||||
$manager = $this->getStateManager();
|
||||
if ($gridStateStr = $manager->getStateFromRequest($gridField, $request ?: $gridField->getForm()->getRequestHandler()->getRequest())) {
|
||||
$stateRequest = $request ?: $gridField->getForm()->getRequestHandler()->getRequest();
|
||||
if ($gridStateStr = $manager->getStateFromRequest($gridField, $stateRequest)) {
|
||||
$gridField->getState(false)->setValue($gridStateStr);
|
||||
}
|
||||
$this->gridField = $gridField;
|
||||
$this->record = $record;
|
||||
$itemRequest = GridFieldNestedFormItemRequest::create($gridField, $this, $record, $gridField->getForm()->getController(), $this->name);
|
||||
$itemRequest = GridFieldNestedFormItemRequest::create(
|
||||
$gridField,
|
||||
$this,
|
||||
$record,
|
||||
$gridField->getForm()->getController(),
|
||||
$this->name
|
||||
);
|
||||
if ($request) {
|
||||
$pjaxFragment = $request->getHeader('X-Pjax');
|
||||
$targetPjaxFragment = str_replace('\\', '-', get_class($record)).'-'.$record->ID.'-'.$this->relationName;
|
||||
|
@ -330,32 +397,37 @@ class GridFieldNestedForm extends GridFieldDetailForm implements
|
|||
|
||||
public function handleSave(GridField $gridField, DataObjectInterface $record)
|
||||
{
|
||||
$postKey = self::POST_KEY;
|
||||
$value = $gridField->Value();
|
||||
if (!isset($value[self::POST_KEY]) || !is_array($value[self::POST_KEY])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($value['GridState']) && $value['GridState']) {
|
||||
// set grid state from value, to store open/closed toggle state for nested forms
|
||||
$gridField->getState(false)->setValue($value['GridState']);
|
||||
}
|
||||
$manager = $this->getStateManager();
|
||||
if ($gridStateStr = $manager->getStateFromRequest($gridField, $gridField->getForm()->getRequestHandler()->getRequest())) {
|
||||
$request = $gridField->getForm()->getRequestHandler()->getRequest();
|
||||
if ($gridStateStr = $manager->getStateFromRequest($gridField, $request)) {
|
||||
$gridField->getState(false)->setValue($gridStateStr);
|
||||
}
|
||||
foreach ($value[self::POST_KEY] as $recordID => $nestedData) {
|
||||
$record = $gridField->getList()->byID($recordID);
|
||||
if ($record) {
|
||||
$nestedGridField = $this->handleNestedItem($gridField, null, $record);
|
||||
$nestedGridField->setValue($nestedData);
|
||||
$nestedGridField->saveInto($record);
|
||||
foreach ($request->postVars() as $key => $val) {
|
||||
if (preg_match("/{$gridField->getName()}-{$postKey}-(\d+)/", $key, $matches)) {
|
||||
$recordID = $matches[1];
|
||||
$nestedData = $val;
|
||||
$record = $gridField->getList()->byID($recordID);
|
||||
if ($record) {
|
||||
$nestedGridField = $this->handleNestedItem($gridField, null, $record);
|
||||
$nestedGridField->setValue($nestedData);
|
||||
$nestedGridField->saveInto($record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getManipulatedData(GridField $gridField, SS_List $dataList)
|
||||
{
|
||||
if ($this->relationName == 'Children' && DataObject::has_extension($gridField->getModelClass(), Hierarchy::class) && $gridField->getForm()->getController() instanceof ModelAdmin) {
|
||||
if ($this->relationName == 'Children'
|
||||
&& DataObject::has_extension($gridField->getModelClass(), Hierarchy::class)
|
||||
&& $gridField->getForm()->getController() instanceof ModelAdmin
|
||||
) {
|
||||
$dataList = $dataList->filter('ParentID', 0);
|
||||
}
|
||||
return $dataList;
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
namespace Symbiote\GridFieldExtensions;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldAddNewButton;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
|
||||
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
|
||||
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
||||
|
@ -17,6 +19,7 @@ use SilverStripe\Forms\GridField\GridFieldSortableHeader;
|
|||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\HasManyList;
|
||||
use SilverStripe\ORM\Hierarchy\Hierarchy;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewInlineButton;
|
||||
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
|
||||
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
|
||||
|
@ -32,10 +35,19 @@ class GridFieldNestedFormItemRequest extends GridFieldDetailForm_ItemRequest
|
|||
public function ItemEditForm()
|
||||
{
|
||||
$config = new GridFieldConfig_RecordEditor();
|
||||
/** @var GridFieldDetailForm */
|
||||
$detailForm = $config->getComponentByType(GridFieldDetailForm::class);
|
||||
$detailForm->setItemEditFormCallback(function (Form $form, $itemRequest) {
|
||||
$breadcrumbs = $itemRequest->Breadcrumbs(false);
|
||||
if ($breadcrumbs && $breadcrumbs->exists()) {
|
||||
$form->Backlink = $breadcrumbs->first()->Link;
|
||||
}
|
||||
});
|
||||
$relationName = $this->component->getRelationName();
|
||||
$list = $this->record->$relationName();
|
||||
if ($relationName == 'Children' && $this->record->hasExtension(Hierarchy::class)) {
|
||||
// we really need a HasManyList for Hierarchy objects, otherwise adding new items will not properly set the ParentID
|
||||
// we really need a HasManyList for Hierarchy objects,
|
||||
// otherwise adding new items will not properly set the ParentID
|
||||
$list = HasManyList::create(get_class($this->record), 'ParentID')
|
||||
->setDataQueryParam($this->record->getInheritableQueryParams())
|
||||
->forForeignID($this->record->ID);
|
||||
|
@ -53,6 +65,10 @@ class GridFieldNestedFormItemRequest extends GridFieldDetailForm_ItemRequest
|
|||
if ($relationClass == get_class($this->record)) {
|
||||
$config->removeComponentsByType(GridFieldSortableHeader::class);
|
||||
$config->removeComponentsByType(GridFieldFilterHeader::class);
|
||||
|
||||
if ($this->gridField->getConfig()->getComponentByType(GridFieldOrderableRows::class)) {
|
||||
$config->addComponent(new GridFieldOrderableRows());
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->record->hasExtension(Hierarchy::class)) {
|
||||
|
@ -82,7 +98,17 @@ class GridFieldNestedFormItemRequest extends GridFieldDetailForm_ItemRequest
|
|||
$title = _t(get_class($this->record).'.'.strtoupper($relationName), ' ');
|
||||
|
||||
$fields = new FieldList(
|
||||
$gridField = new GridField($this->component->getGridField()->getName().'['.GridFieldNestedForm::POST_KEY.']['.$this->record->ID.']', $title, $list, $config)
|
||||
$gridField = new GridField(
|
||||
sprintf(
|
||||
'%s-%s-%s',
|
||||
$this->component->getGridField()->getName(),
|
||||
GridFieldNestedForm::POST_KEY,
|
||||
$this->record->ID
|
||||
),
|
||||
$title,
|
||||
$list,
|
||||
$config
|
||||
)
|
||||
);
|
||||
if (!trim($title)) {
|
||||
$gridField->addExtraClass('empty-title');
|
||||
|
@ -113,7 +139,34 @@ class GridFieldNestedFormItemRequest extends GridFieldDetailForm_ItemRequest
|
|||
|
||||
/** @var ArrayList $items */
|
||||
$items = $this->popupController->Breadcrumbs($unlinked);
|
||||
|
||||
|
||||
if (!$items) {
|
||||
$items = ArrayList::create();
|
||||
}
|
||||
|
||||
if ($this->record && $this->record->ID) {
|
||||
$title = ($this->record->Title) ? $this->record->Title : "#{$this->record->ID}";
|
||||
$items->push(ArrayData::create([
|
||||
'Title' => $title,
|
||||
'Link' => parent::Link()
|
||||
]));
|
||||
} else {
|
||||
$items->push(ArrayData::create([
|
||||
'Title' => _t(
|
||||
'SilverStripe\\Forms\\GridField\\GridField.NewRecord',
|
||||
'New {type}',
|
||||
['type' => $this->record->i18n_singular_name()]
|
||||
),
|
||||
'Link' => false
|
||||
]));
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->Link) {
|
||||
$item->Link = $this->gridField->addAllStateToUrl(Director::absoluteURL($item->Link));
|
||||
}
|
||||
}
|
||||
|
||||
$this->extend('updateBreadcrumbs', $items);
|
||||
return $items;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
|
|||
use SilverStripe\ORM\ArrayList;
|
||||
use Symbiote\GridFieldExtensions\GridFieldNestedForm;
|
||||
use Symbiote\GridFieldExtensions\Tests\Stub\StubHierarchy;
|
||||
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered;
|
||||
use Symbiote\GridFieldExtensions\Tests\Stub\StubParent;
|
||||
use Symbiote\GridFieldExtensions\Tests\Stub\TestController;
|
||||
|
||||
class GridFieldNestedFormTest extends SapphireTest
|
||||
|
@ -18,7 +20,9 @@ class GridFieldNestedFormTest extends SapphireTest
|
|||
protected static $fixture_file = 'GridFieldNestedFormTest.yml';
|
||||
|
||||
protected static $extra_dataobjects = [
|
||||
StubHierarchy::class
|
||||
StubHierarchy::class,
|
||||
StubParent::class,
|
||||
StubOrdered::class
|
||||
];
|
||||
|
||||
public function testHierarchy()
|
||||
|
@ -55,4 +59,30 @@ class GridFieldNestedFormTest extends SapphireTest
|
|||
$child2 = $this->objFromFixture(StubHierarchy::class, 'item1_1_1');
|
||||
$this->assertEquals($child2->ID, $list->first()->ID);
|
||||
}
|
||||
|
||||
public function testHasManyRelation()
|
||||
{
|
||||
// test that GridFieldNestedForm works with HasMany relations
|
||||
$parent = $this->objFromFixture(StubParent::class, 'parent1');
|
||||
$list = new ArrayList([$parent]);
|
||||
$config = new GridFieldConfig_RecordEditor();
|
||||
$config->addComponent($nestedForm = new GridFieldNestedForm());
|
||||
$nestedForm->setRelationName('MyHasMany');
|
||||
|
||||
$controller = new TestController('Test');
|
||||
$form = new Form($controller, 'TestForm', new FieldList(
|
||||
$gridField = new GridField(__FUNCTION__, 'test', $list, $config)
|
||||
), new FieldList());
|
||||
|
||||
$request = new HTTPRequest('GET', '/');
|
||||
$itemEditForm = $nestedForm->handleNestedItem($gridField, $request, $parent);
|
||||
$this->assertNotNull($itemEditForm);
|
||||
$nestedGridField = $itemEditForm->Fields()->first();
|
||||
$this->assertNotNull($nestedGridField);
|
||||
$list = $nestedGridField->getList();
|
||||
$this->assertEquals(2, $list->count());
|
||||
|
||||
$child1 = $this->objFromFixture(StubOrdered::class, 'child1');
|
||||
$this->assertEquals($child1->ID, $list->first()->ID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,14 @@ Symbiote\GridFieldExtensions\Tests\Stub\StubHierarchy:
|
|||
ParentID: =>Symbiote\GridFieldExtensions\Tests\Stub\StubHierarchy.item1
|
||||
item1_1_1:
|
||||
Title: 'Item 1.1.1'
|
||||
ParentID: =>Symbiote\GridFieldExtensions\Tests\Stub\StubHierarchy.item1_1
|
||||
ParentID: =>Symbiote\GridFieldExtensions\Tests\Stub\StubHierarchy.item1_1
|
||||
Symbiote\GridFieldExtensions\Tests\Stub\StubParent:
|
||||
parent1:
|
||||
Title: 'Parent 1'
|
||||
Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered:
|
||||
child1:
|
||||
Title: 'Child 1'
|
||||
ParentID: =>Symbiote\GridFieldExtensions\Tests\Stub\StubParent.parent1
|
||||
child2:
|
||||
Title: 'Child 2'
|
||||
ParentID: =>Symbiote\GridFieldExtensions\Tests\Stub\StubParent.parent1
|
||||
|
|
Loading…
Reference in New Issue