mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #9655 from sminnee/pulls/9647-find-lost-records
NEW: Add GridFieldDetailForm::setRedirectMissingRecords()
This commit is contained in:
commit
196752566f
@ -3,8 +3,11 @@
|
|||||||
namespace SilverStripe\Forms\GridField;
|
namespace SilverStripe\Forms\GridField;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
use SilverStripe\Control\HTTPStreamResponse;
|
||||||
use SilverStripe\Control\RequestHandler;
|
use SilverStripe\Control\RequestHandler;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Extensible;
|
use SilverStripe\Core\Extensible;
|
||||||
@ -12,6 +15,7 @@ use SilverStripe\Core\Injector\Injectable;
|
|||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\Validator;
|
use SilverStripe\Forms\Validator;
|
||||||
|
use SilverStripe\ORM\DataList;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\Filterable;
|
use SilverStripe\ORM\Filterable;
|
||||||
|
|
||||||
@ -68,6 +72,12 @@ class GridFieldDetailForm implements GridField_URLHandler
|
|||||||
*/
|
*/
|
||||||
protected $itemRequestClass;
|
protected $itemRequestClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, will redirect to missing records if they are found elsewhere
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $redirectMissingRecords = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var callable With two parameters: $form and $component
|
* @var callable With two parameters: $form and $component
|
||||||
*/
|
*/
|
||||||
@ -113,6 +123,17 @@ class GridFieldDetailForm implements GridField_URLHandler
|
|||||||
$requestHandler = $gridField->getForm()->getController();
|
$requestHandler = $gridField->getForm()->getController();
|
||||||
$record = $this->getRecordFromRequest($gridField, $request);
|
$record = $this->getRecordFromRequest($gridField, $request);
|
||||||
if (!$record) {
|
if (!$record) {
|
||||||
|
// Look for the record elsewhere in the CMS
|
||||||
|
$redirectDest = $this->getLostRecordRedirection($gridField, $request);
|
||||||
|
// Don't allow infinite redirections
|
||||||
|
if ($redirectDest) {
|
||||||
|
// Mark the remainder of the URL as parsed to trigger an immediate redirection
|
||||||
|
while (!$request->allParsed()) {
|
||||||
|
$request->shift();
|
||||||
|
}
|
||||||
|
return (new HTTPResponse())->redirect($redirectDest);
|
||||||
|
}
|
||||||
|
|
||||||
return $requestHandler->httpError(404, 'That record was not found');
|
return $requestHandler->httpError(404, 'That record was not found');
|
||||||
}
|
}
|
||||||
$handler = $this->getItemRequestHandler($gridField, $record, $requestHandler);
|
$handler = $this->getItemRequestHandler($gridField, $record, $requestHandler);
|
||||||
@ -148,6 +169,50 @@ class GridFieldDetailForm implements GridField_URLHandler
|
|||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try and find another URL at which the given record can be edited.
|
||||||
|
* If redirectMissingRecords is true and the record has a CMSEditLink method, that value will be returned.
|
||||||
|
* This only works when the list passed to the GridField is a {@link DataList}.
|
||||||
|
*
|
||||||
|
* @param $gridField The current GridField
|
||||||
|
* @param $id The ID of the DataObject to open
|
||||||
|
*/
|
||||||
|
public function getLostRecordRedirection(GridField $gridField, HTTPRequest $request, ?int $id = null): ?string
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!$this->redirectMissingRecords) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not supplied, look up the ID from the request
|
||||||
|
if ($id === null && is_numeric($request->param('ID'))) {
|
||||||
|
$id = (int)$request->param('ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = $gridField->getList();
|
||||||
|
if (!$list instanceof DataList) {
|
||||||
|
throw new \LogicException('List is not of type DataList, cannot determine redirection target');
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing = DataObject::get($list->dataClass())->byID($id);
|
||||||
|
if ($existing && $existing->hasMethod('CMSEditLink')) {
|
||||||
|
$link = $existing->CMSEditLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($link && $link == $request->getURL()) {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
'Infinite redirection to "%s" detected in GridFieldDetailForm->getLostRecordRedirection()',
|
||||||
|
$link
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a request handler for the given record
|
* Build a request handler for the given record
|
||||||
*
|
*
|
||||||
@ -209,6 +274,28 @@ class GridFieldDetailForm implements GridField_URLHandler
|
|||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable redirection to missing records.
|
||||||
|
*
|
||||||
|
* If a GridField shows a filtered list, and the DataObject is not in the list but exists in the
|
||||||
|
* database, and the DataObject has a CMSEditLink method, then the system will redirect to the
|
||||||
|
* URL returned by that method.
|
||||||
|
*/
|
||||||
|
public function setRedirectMissingRecords(bool $redirectMissingRecords): self
|
||||||
|
{
|
||||||
|
$this->redirectMissingRecords = $redirectMissingRecords;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the status of redirection to missing records.
|
||||||
|
* @see setRedirectMissingRecordssetRedirectMissingRecords
|
||||||
|
*/
|
||||||
|
public function getRedirectMissingRecords(): bool
|
||||||
|
{
|
||||||
|
return $this->redirectMissingRecords;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
@ -623,6 +623,11 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
|
|||||||
// to the same URL (it assumes that its content is already current, and doesn't reload)
|
// to the same URL (it assumes that its content is already current, and doesn't reload)
|
||||||
return $this->edit($controller->getRequest());
|
return $this->edit($controller->getRequest());
|
||||||
} else {
|
} else {
|
||||||
|
// We might be able to redirect to open the record in a different view
|
||||||
|
if ($redirectDest = $this->component->getLostRecordRedirection($this->gridField, $controller->getRequest(), $this->record->ID)) {
|
||||||
|
return $controller->redirect($redirectDest, 302);
|
||||||
|
}
|
||||||
|
|
||||||
// Changes to the record properties might've excluded the record from
|
// Changes to the record properties might've excluded the record from
|
||||||
// a filtered list, so return back to the main view if it can't be found
|
// a filtered list, so return back to the main view if it can't be found
|
||||||
$url = $controller->getRequest()->getURL();
|
$url = $controller->getRequest()->getURL();
|
||||||
|
@ -207,7 +207,7 @@ class GridFieldDetailFormTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
$this->assertFalse($response->isError());
|
$this->assertFalse($response->isError());
|
||||||
|
|
||||||
$person = Person::get()->sort('FirstName')->First();
|
$person = $this->objFromFixture(Person::class, 'jane');
|
||||||
$favouriteGroup = $person->FavouriteGroups()->first();
|
$favouriteGroup = $person->FavouriteGroups()->first();
|
||||||
|
|
||||||
$this->assertInstanceOf(PeopleGroup::class, $favouriteGroup);
|
$this->assertInstanceOf(PeopleGroup::class, $favouriteGroup);
|
||||||
@ -248,7 +248,7 @@ class GridFieldDetailFormTest extends FunctionalTest
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->assertFalse($response->isError());
|
$this->assertFalse($response->isError());
|
||||||
$person = Person::get()->sort('FirstName')->First();
|
$person = $this->objFromFixture(Person::class, 'jane');
|
||||||
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
|
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
@ -271,7 +271,7 @@ class GridFieldDetailFormTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
$this->assertFalse($response->isError());
|
$this->assertFalse($response->isError());
|
||||||
|
|
||||||
$person = Person::get()->sort('FirstName')->First();
|
$person = $this->objFromFixture(Person::class, 'jane');
|
||||||
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
|
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
@ -405,4 +405,34 @@ class GridFieldDetailFormTest extends FunctionalTest
|
|||||||
$this->assertEquals($group->Name, (string) $title[0]);
|
$this->assertEquals($group->Name, (string) $title[0]);
|
||||||
$this->assertEquals($group->ID, (string) $id[0]['value']);
|
$this->assertEquals($group->ID, (string) $id[0]['value']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRedirectMissingRecords()
|
||||||
|
{
|
||||||
|
$origAutoFollow = $this->autoFollowRedirection;
|
||||||
|
$this->autoFollowRedirection = false;
|
||||||
|
|
||||||
|
// GridField is filtered people in "My Group", which does't include "jack"
|
||||||
|
$included = $this->objFromFixture(Person::class, 'joe');
|
||||||
|
$excluded = $this->objFromFixture(Person::class, 'jack');
|
||||||
|
|
||||||
|
$response = $this->get(sprintf(
|
||||||
|
'GridFieldDetailFormTest_Controller/Form/field/testfield/item/%d/edit',
|
||||||
|
$included->ID
|
||||||
|
));
|
||||||
|
$this->assertFalse(
|
||||||
|
$response->isRedirect(),
|
||||||
|
'Existing records are not redirected'
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->get(sprintf(
|
||||||
|
'GridFieldDetailFormTest_Controller/Form/field/testfield/item/%d/edit',
|
||||||
|
$excluded->ID
|
||||||
|
));
|
||||||
|
$this->assertTrue(
|
||||||
|
$response->isRedirect(),
|
||||||
|
'Non-existing records are redirected'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->autoFollowRedirection = $origAutoFollow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\Person:
|
|||||||
jane:
|
jane:
|
||||||
FirstName: Jane
|
FirstName: Jane
|
||||||
Surname: Doe
|
Surname: Doe
|
||||||
|
jack:
|
||||||
|
FirstName: Jack
|
||||||
|
Surname: Doe
|
||||||
|
|
||||||
SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\PeopleGroup:
|
SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\PeopleGroup:
|
||||||
group:
|
group:
|
||||||
|
@ -31,7 +31,7 @@ class CategoryController extends Controller implements TestOnly
|
|||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
// GridField lists categories for a specific person
|
// GridField lists categories for a specific person
|
||||||
$person = Person::get()->sort('FirstName')->First();
|
$person = Person::get()->filter('FirstName', 'Jane')->First();
|
||||||
$detailFields = singleton(Category::class)->getCMSFields();
|
$detailFields = singleton(Category::class)->getCMSFields();
|
||||||
$detailFields->addFieldsToTab(
|
$detailFields->addFieldsToTab(
|
||||||
'Root.Main',
|
'Root.Main',
|
||||||
|
@ -69,4 +69,9 @@ class Person extends DataObject implements TestOnly
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function CMSEditLink()
|
||||||
|
{
|
||||||
|
return sprintf('my-admin/%d', $this->ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,9 @@ class TestController extends Controller implements TestOnly
|
|||||||
$field->getConfig()->addComponent(new GridFieldViewButton());
|
$field->getConfig()->addComponent(new GridFieldViewButton());
|
||||||
$field->getConfig()->addComponent(new GridFieldEditButton());
|
$field->getConfig()->addComponent(new GridFieldEditButton());
|
||||||
/** @skipUpgrade */
|
/** @skipUpgrade */
|
||||||
$field->getConfig()->addComponent($gridFieldForm = new GridFieldDetailForm($this, 'Form'));
|
$gridFieldForm = new GridFieldDetailForm($this, 'Form');
|
||||||
|
$gridFieldForm->setRedirectMissingRecords(true);
|
||||||
|
$field->getConfig()->addComponent($gridFieldForm);
|
||||||
$field->getConfig()->addComponent(new GridFieldEditButton());
|
$field->getConfig()->addComponent(new GridFieldEditButton());
|
||||||
/** @skipUpgrade */
|
/** @skipUpgrade */
|
||||||
return new Form($this, 'Form', new FieldList($field), new FieldList());
|
return new Form($this, 'Form', new FieldList($field), new FieldList());
|
||||||
|
Loading…
Reference in New Issue
Block a user