mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW Port betterbuttons to framework (#8569)
* MINOR: Add `Previous`, `Next` and `Create New` actions in edit form If the form is opened via a grid field, the filters will be retained so the previous/next record opened will be correct * MINOR: Add ability to customise the visibility of the `Previous`, `Next` and `Add` buttons at a `GridField` level * Fix invalid action when pressing the `New` button in an edit form unless `betterbuttons` module was installed * - Merge `showPrevious` and `showNext` to `showPagination` for grid fields - Update documentation - Improve performance for next/previous buttons by not fetching all list records - Refactoring * Refactor to fail gracefully on GridFieldPaginator
This commit is contained in:
parent
cc5c59e39d
commit
cc712892a9
7
_config/buttons.yml
Normal file
7
_config/buttons.yml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
Name: buttons
|
||||
---
|
||||
SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest:
|
||||
formActions:
|
||||
showPagination: true
|
||||
showAdd: true
|
@ -124,6 +124,27 @@ SilverStripe\Admin\LeftAndMain:
|
||||
'Feedback': ''
|
||||
```
|
||||
|
||||
## Customising the CMS form actions
|
||||
|
||||
The `Previous`, `Next` and `Add` actions on the edit form are visible by default but can be hidden globally by adding the following `.yml` config:
|
||||
|
||||
```yml
|
||||
SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest:
|
||||
formActions:
|
||||
showPagination: false
|
||||
showAdd: false
|
||||
```
|
||||
|
||||
You can also override this for a specific `GridField` instance when using the `GridFieldConfig_RecordEditor` constructor:
|
||||
|
||||
```php
|
||||
$grid = new GridField(
|
||||
"pages",
|
||||
"All Pages",
|
||||
SiteTree::get(),
|
||||
GridFieldConfig_RecordEditor::create(null, false, false));
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
* [How to extend the CMS interface](extend_cms_interface)
|
||||
|
@ -10,8 +10,10 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig
|
||||
/**
|
||||
*
|
||||
* @param int $itemsPerPage - How many items per page should show up
|
||||
* @param bool $showPagination Whether the `Previous` and `Next` buttons should display or not, leave as null to use default
|
||||
* @param bool $showAdd Whether the `Add` button should display or not, leave as null to use default
|
||||
*/
|
||||
public function __construct($itemsPerPage = null)
|
||||
public function __construct($itemsPerPage = null, $showPagination = null, $showAdd = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@ -26,7 +28,7 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig
|
||||
$this->addComponent(new GridField_ActionMenu());
|
||||
$this->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||
$this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
|
||||
$this->addComponent(new GridFieldDetailForm());
|
||||
$this->addComponent(new GridFieldDetailForm(null, $showPagination, $showAdd));
|
||||
|
||||
$sort->setThrowExceptionOnBadDataType(false);
|
||||
$filter->setThrowExceptionOnBadDataType(false);
|
||||
|
@ -38,11 +38,20 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
protected $template = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $showPagination;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $showAdd;
|
||||
|
||||
/**
|
||||
* @var Validator The form validator used for both add and edit fields.
|
||||
*/
|
||||
@ -79,10 +88,14 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
* controller who wants to display the getCMSFields
|
||||
*
|
||||
* @param string $name The name of the edit form to place into the pop-up form
|
||||
* @param bool $showPagination Whether the `Previous` and `Next` buttons should display or not, leave as null to use default
|
||||
* @param bool $showAdd Whether the `Add` button should display or not, leave as null to use default
|
||||
*/
|
||||
public function __construct($name = 'DetailForm')
|
||||
public function __construct($name = null, $showPagination = null, $showAdd = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->setName($name ?: 'DetailForm');
|
||||
$this->setShowPagination($showPagination);
|
||||
$this->setShowAdd($showAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,6 +106,10 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
*/
|
||||
public function handleItem($gridField, $request)
|
||||
{
|
||||
if ($gridStateStr = $request->getVar('gridState')) {
|
||||
$gridField->getState(false)->setValue($gridStateStr);
|
||||
}
|
||||
|
||||
// Our getController could either give us a true Controller, if this is the top-level GridField.
|
||||
// It could also give us a RequestHandler in the form of GridFieldDetailForm_ItemRequest if this is a
|
||||
// nested GridField.
|
||||
@ -102,7 +119,7 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
if (is_numeric($request->param('ID'))) {
|
||||
/** @var Filterable $dataList */
|
||||
$dataList = $gridField->getList();
|
||||
$record = $dataList->byID($request->param("ID"));
|
||||
$record = $dataList->byID($request->param('ID'));
|
||||
} else {
|
||||
$record = Injector::inst()->create($gridField->getModelClass());
|
||||
}
|
||||
@ -179,6 +196,68 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function getDefaultShowPagination()
|
||||
{
|
||||
$formActionsConfig = GridFieldDetailForm_ItemRequest::config()->get('formActions');
|
||||
return isset($formActionsConfig['showPagination']) ? (boolean) $formActionsConfig['showPagination'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getShowPagination()
|
||||
{
|
||||
if ($this->showPagination === null) {
|
||||
return $this->getDefaultShowPagination();
|
||||
}
|
||||
|
||||
return (boolean) $this->showPagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $showPagination
|
||||
* @return GridFieldDetailForm
|
||||
*/
|
||||
public function setShowPagination($showPagination)
|
||||
{
|
||||
$this->showPagination = $showPagination;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function getDefaultShowAdd()
|
||||
{
|
||||
$formActionsConfig = GridFieldDetailForm_ItemRequest::config()->get('formActions');
|
||||
return isset($formActionsConfig['showAdd']) ? (boolean) $formActionsConfig['showAdd'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getShowAdd()
|
||||
{
|
||||
if ($this->showAdd === null) {
|
||||
return $this->getDefaultShowAdd();
|
||||
}
|
||||
|
||||
return (boolean) $this->showAdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $showAdd
|
||||
* @return GridFieldDetailForm
|
||||
*/
|
||||
public function setShowAdd($showAdd)
|
||||
{
|
||||
$this->showAdd = $showAdd;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @return $this
|
||||
@ -232,8 +311,8 @@ class GridFieldDetailForm implements GridField_URLHandler
|
||||
{
|
||||
if ($this->itemRequestClass) {
|
||||
return $this->itemRequestClass;
|
||||
} elseif (ClassInfo::exists(static::class . "_ItemRequest")) {
|
||||
return static::class . "_ItemRequest";
|
||||
} elseif (ClassInfo::exists(static::class . '_ItemRequest')) {
|
||||
return static::class . '_ItemRequest';
|
||||
} else {
|
||||
return GridFieldDetailForm_ItemRequest::class;
|
||||
}
|
||||
|
@ -7,9 +7,12 @@ use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\RequestHandler;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Forms\CompositeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
@ -278,6 +281,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
|
||||
$canEdit = $this->record->canEdit();
|
||||
$canDelete = $this->record->canDelete();
|
||||
$actions = new FieldList();
|
||||
|
||||
if ($this->record->ID !== 0) {
|
||||
if ($canEdit) {
|
||||
$actions->push(FormAction::create('doSave', _t('SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Save', 'Save'))
|
||||
@ -290,6 +294,45 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
|
||||
->setUseButtonTag(true)
|
||||
->addExtraClass('btn-outline-danger btn-hide-outline font-icon-trash-bin action--delete'));
|
||||
}
|
||||
|
||||
$gridStateStr = $this->getRequest()->requestVar('gridState');
|
||||
|
||||
$this->gridField->getState(false)->setValue($gridStateStr);
|
||||
$actions->push(HiddenField::create('gridState', null, $gridStateStr));
|
||||
|
||||
$rightGroup = CompositeField::create()->setName('RightGroup');
|
||||
$rightGroup->addExtraClass('right');
|
||||
$rightGroup->setFieldHolderTemplate(get_class($rightGroup) . '_holder_buttongroup');
|
||||
|
||||
$previousAndNextGroup = CompositeField::create()->setName('PreviousAndNextGroup');
|
||||
$previousAndNextGroup->addExtraClass('rounded');
|
||||
$previousAndNextGroup->setFieldHolderTemplate(get_class($previousAndNextGroup) . '_holder_buttongroup');
|
||||
|
||||
|
||||
$component = $this->gridField->getConfig()->getComponentByType(GridFieldDetailForm::class);
|
||||
|
||||
if ($component->getShowPagination()) {
|
||||
$previousAndNextGroup->push(FormAction::create('doPrevious')
|
||||
->setUseButtonTag(true)
|
||||
->setAttribute('data-grid-state', $gridStateStr)
|
||||
->setDisabled(!$this->getPreviousRecordID())
|
||||
->addExtraClass('btn btn-secondary font-icon-left-open action--previous discard-confirmation'));
|
||||
|
||||
$previousAndNextGroup->push(FormAction::create('doNext')
|
||||
->setUseButtonTag(true)
|
||||
->setAttribute('data-grid-state', $gridStateStr)
|
||||
->setDisabled(!$this->getNextRecordID())
|
||||
->addExtraClass('btn btn-secondary font-icon-right-open action--next discard-confirmation'));
|
||||
}
|
||||
|
||||
$rightGroup->push($previousAndNextGroup);
|
||||
|
||||
if ($component->getShowAdd()) {
|
||||
$rightGroup->push(FormAction::create('doNew')
|
||||
->setUseButtonTag(true)
|
||||
->setAttribute('data-grid-state', $this->getRequest()->getVar('gridState'))
|
||||
->addExtraClass('btn btn-primary font-icon-plus rounded action--new discard-confirmation'));
|
||||
}
|
||||
} else { // adding new record
|
||||
//Change the Save label to 'Create'
|
||||
$actions->push(FormAction::create('doSave', _t('SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Create', 'Create'))
|
||||
@ -309,7 +352,13 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
|
||||
$actions->push(new LiteralField('cancelbutton', $text));
|
||||
}
|
||||
}
|
||||
|
||||
$this->extend('updateFormActions', $actions);
|
||||
|
||||
if (isset($rightGroup)) {
|
||||
$actions->push($rightGroup);
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
@ -410,6 +459,108 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler
|
||||
return $this->redirectAfterSave($isNewRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to the previous record
|
||||
* @param array $data The form data
|
||||
* @param Form $form The Form object
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function doPrevious($data, $form)
|
||||
{
|
||||
$this->getToplevelController()->getResponse()->addHeader('X-Pjax', 'Content');
|
||||
$link = $this->getEditLink($this->getPreviousRecordID());
|
||||
return $this->redirect($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to the next record
|
||||
* @param array $data The form data
|
||||
* @param Form $form The Form object
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function doNext($data, $form)
|
||||
{
|
||||
$this->getToplevelController()->getResponse()->addHeader('X-Pjax', 'Content');
|
||||
$link = $this->getEditLink($this->getNextRecordID());
|
||||
return $this->redirect($link);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new record. If you're already creating a new record,
|
||||
* this forces the URL to change.
|
||||
*
|
||||
* @param array $data The form data
|
||||
* @param Form $form The Form object
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function doNew($data, $form)
|
||||
{
|
||||
return $this->redirect(Controller::join_links($this->gridField->Link('item'), 'new'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the edit link for a record
|
||||
*
|
||||
* @param int $id The ID of the record in the GridField
|
||||
* @return string
|
||||
*/
|
||||
public function getEditLink($id)
|
||||
{
|
||||
return Controller::join_links(
|
||||
$this->gridField->Link(),
|
||||
'item',
|
||||
$id,
|
||||
'?gridState=' . urlencode($this->gridField->getState(false)->Value())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset The offset from the current record
|
||||
* @return int|bool
|
||||
*/
|
||||
private function getAdjacentRecordID($offset)
|
||||
{
|
||||
$gridField = $this->getGridField();
|
||||
$gridStateStr = $this->getRequest()->requestVar('gridState');
|
||||
$state = $gridField->getState(false);
|
||||
$state->setValue($gridStateStr);
|
||||
$data = $state->getData();
|
||||
$paginator = $data->getData('GridFieldPaginator');
|
||||
if (!$paginator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentPage = $data->getData('GridFieldPaginator')->getData('currentPage');
|
||||
$itemsPerPage = $data->getData('GridFieldPaginator')->getData('itemsPerPage');
|
||||
|
||||
$limit = $itemsPerPage + 2;
|
||||
$limitOffset = max(0, $itemsPerPage * ($currentPage-1) -1);
|
||||
|
||||
$map = $gridField->getManipulatedList()->limit($limit, $limitOffset)->column('ID');
|
||||
$index = array_search($this->record->ID, $map);
|
||||
return isset($map[$index+$offset]) ? $map[$index+$offset] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the previous record in the list.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPreviousRecordID()
|
||||
{
|
||||
return $this->getAdjacentRecordID(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the next record in the list.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNextRecordID()
|
||||
{
|
||||
return $this->getAdjacentRecordID(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Response object for this request after a successful save
|
||||
*
|
||||
|
@ -62,7 +62,12 @@ class GridFieldEditButton implements GridField_ColumnProvider, GridField_ActionP
|
||||
*/
|
||||
public function getUrl($gridField, $record, $columnName)
|
||||
{
|
||||
return Controller::join_links($gridField->Link('item'), $record->ID, 'edit');
|
||||
return Controller::join_links(
|
||||
$gridField->Link('item'),
|
||||
$record->ID,
|
||||
'edit',
|
||||
'?gridState=' . urlencode($gridField->getState(false)->Value())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +144,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
|
||||
|
||||
// Force the state to the initial page if none is set
|
||||
$state->currentPage(1);
|
||||
$state->itemsPerPage($this->getItemsPerPage());
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user