mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Create GridField Actions Menu component (#8083)
* WIP GridField action menu work, the gist of the idea is using a new gridfield component * Add delete action to actions menu * Actions are added automatically to action menu (allows for extension) * Add test and minor changes * Add docs and minor changes * Refactor ActionMenuItem into distinct types, general ActionMenu cleanup * Add icons and fix title * Pass columnName, so it can be used by components * Update test to open and find action menu buttons * Add section in changelog upgrade section for GridField_ActionMenu
This commit is contained in:
parent
a0d0564369
commit
385e9e105c
@ -157,6 +157,7 @@ $component = $config->getComponentByType(GridFieldFilterHeader::class)
|
|||||||
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
|
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
|
||||||
modules and extensions.
|
modules and extensions.
|
||||||
|
|
||||||
|
- [GridField_ActionMenu](api:SilverStripe\Forms\GridField\GridField_ActionMenu)
|
||||||
- [GridFieldToolbarHeader](api:SilverStripe\Forms\GridField\GridFieldToolbarHeader)
|
- [GridFieldToolbarHeader](api:SilverStripe\Forms\GridField\GridFieldToolbarHeader)
|
||||||
- [GridFieldSortableHeader](api:SilverStripe\Forms\GridField\GridFieldSortableHeader)
|
- [GridFieldSortableHeader](api:SilverStripe\Forms\GridField\GridFieldSortableHeader)
|
||||||
- [GridFieldFilterHeader](api:SilverStripe\Forms\GridField\GridFieldFilterHeader)
|
- [GridFieldFilterHeader](api:SilverStripe\Forms\GridField\GridFieldFilterHeader)
|
||||||
@ -266,6 +267,33 @@ This configuration adds the ability to searched for existing records and add a r
|
|||||||
Records created or deleted through the `GridFieldConfig_RelationEditor` automatically update the relationship in the
|
Records created or deleted through the `GridFieldConfig_RelationEditor` automatically update the relationship in the
|
||||||
database.
|
database.
|
||||||
|
|
||||||
|
## GridField_ActionMenu
|
||||||
|
|
||||||
|
The `GridField_ActionMenu` component provides a dropdown menu which automatically bundles GridField actions into a react based dropdown. It is included by default on `GridFieldConfig_RecordEditor` and `GridFieldConfig_RelationEditor` configs.
|
||||||
|
|
||||||
|
To add it to a GridField, add the `GridField_ActionMenu` component and any action(s) that implement `GridField_ActionMenuItem` (such as `GridFieldEditButton` or `GridFieldDeleteAction`) to the `GridFieldConfig`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||||
|
|
||||||
|
// `GridFieldConfig::create()` will create an empty configuration (no components).
|
||||||
|
$config = GridFieldConfig::create();
|
||||||
|
|
||||||
|
// add a component
|
||||||
|
$config->addComponent();
|
||||||
|
|
||||||
|
$config->addComponents(
|
||||||
|
new GridFieldDataColumns(),
|
||||||
|
new GridFieldEditButton(),
|
||||||
|
new GridField_ActionMenu()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the GridField with our custom configuration
|
||||||
|
$gridField->setConfig($config);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## GridFieldDetailForm
|
## GridFieldDetailForm
|
||||||
|
|
||||||
The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from
|
The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from
|
||||||
|
@ -153,6 +153,51 @@ called method is available as a parameter.
|
|||||||
To finish off our basic example, the `handleAction` method simply returns a
|
To finish off our basic example, the `handleAction` method simply returns a
|
||||||
message to the user interface indicating a successful message.
|
message to the user interface indicating a successful message.
|
||||||
|
|
||||||
|
## Add the GridFieldCustomAction to the `GridField_ActionMenu`
|
||||||
|
|
||||||
|
For an action to be included in the action menu dropdown, which appears on each row if `GridField_ActionMenu` is included in the `GridFieldConfig`, it must implement `GridField_ActionMenuItem` and relevant `get` functions to provide information to the frontend react action menu component.
|
||||||
|
|
||||||
|
## Basic GridFieldCustomAction boilerplate implementing GridField_ActionMenuItem
|
||||||
|
|
||||||
|
```php
|
||||||
|
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
|
||||||
|
use SilverStripe\Forms\GridField\GridField_ActionProvider;
|
||||||
|
use SilverStripe\Forms\GridField\GridField_ActionMenuItem;
|
||||||
|
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
|
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
|
||||||
|
{
|
||||||
|
|
||||||
|
public function augmentColumns($gridField, &$columns)
|
||||||
|
{
|
||||||
|
if(!in_array('Actions', $columns)) {
|
||||||
|
$columns[] = 'Actions';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle($gridField, $record)
|
||||||
|
{
|
||||||
|
return _t(__CLASS__ . '.Delete', "Delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroup($gridField, $record)
|
||||||
|
{
|
||||||
|
return GridField_ActionMenuItem::DEFAULT_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtraData($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
if ($field) {
|
||||||
|
return $field->getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...rest of boilerplate code
|
||||||
|
```
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
* [GridField Reference](/developer_guides/forms/field_types/gridfield)
|
* [GridField Reference](/developer_guides/forms/field_types/gridfield)
|
||||||
|
@ -148,3 +148,26 @@ cd ~/my-project-root
|
|||||||
upgrade-code environment --write
|
upgrade-code environment --write
|
||||||
upgrade-code reorganise --write
|
upgrade-code reorganise --write
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### New GridField Action Menu
|
||||||
|
|
||||||
|
A new `GridField_ActionMenu` is included by default in GridFields configured with `GridFieldConfig_RecordEditor`
|
||||||
|
or `GridFieldConfig_RelationEditor`.
|
||||||
|
In addition to this `GridFieldDeleteAction` and `GridFieldEditButton` now implement `GridField_ActionMenuItem`,
|
||||||
|
this means that any GridField that uses a config of or based on `GridFieldConfig_RecordEditor`
|
||||||
|
or `GridFieldConfig_RelationEditor` will have an action menu on each item row with
|
||||||
|
the 'Delete/Unlink' and 'Edit' actions moved into it.
|
||||||
|
|
||||||
|
If you wish to opt out of having this menu and the respective actions moved into it, you can remove the `GridField_ActionMenu`
|
||||||
|
component from the config that is passed into your GridField.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// method 1: removing GridField_ActionMenu from a new GridField
|
||||||
|
$config = GridFieldConfig_RecordEditor::create();
|
||||||
|
$config->removeComponentsByType(GridField_ActionMenu);
|
||||||
|
|
||||||
|
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
||||||
|
|
||||||
|
// method 2: removing GridField_ActionMenu from an existing GridField
|
||||||
|
$gridField->getConfig()->removeComponentsByType(GridField_ActionMenu);
|
||||||
|
```
|
||||||
|
@ -98,7 +98,7 @@ en:
|
|||||||
DeletePermissionsFailure: 'No delete permissions'
|
DeletePermissionsFailure: 'No delete permissions'
|
||||||
Deleted: 'Deleted {type} {name}'
|
Deleted: 'Deleted {type} {name}'
|
||||||
Save: Save
|
Save: Save
|
||||||
SilverStripe\Forms\GridField\GridFieldEditButton_ss:
|
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||||
EDIT: Edit
|
EDIT: Edit
|
||||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||||
UnlinkSelfFailure: 'Cannot remove yourself from this group, you will lose admin rights'
|
UnlinkSelfFailure: 'Cannot remove yourself from this group, you will lose admin rights'
|
||||||
|
@ -22,7 +22,8 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig
|
|||||||
$this->addComponent($filter = new GridFieldFilterHeader());
|
$this->addComponent($filter = new GridFieldFilterHeader());
|
||||||
$this->addComponent(new GridFieldDataColumns());
|
$this->addComponent(new GridFieldDataColumns());
|
||||||
$this->addComponent(new GridFieldEditButton());
|
$this->addComponent(new GridFieldEditButton());
|
||||||
$this->addComponent(new GridFieldDeleteAction());
|
$this->addComponent(new GridFieldDeleteAction(true));
|
||||||
|
$this->addComponent(new GridField_ActionMenu());
|
||||||
$this->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
$this->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||||
$this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
|
$this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
|
||||||
$this->addComponent(new GridFieldDetailForm());
|
$this->addComponent(new GridFieldDetailForm());
|
||||||
|
@ -38,6 +38,7 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig
|
|||||||
$this->addComponent(new GridFieldDataColumns());
|
$this->addComponent(new GridFieldDataColumns());
|
||||||
$this->addComponent(new GridFieldEditButton());
|
$this->addComponent(new GridFieldEditButton());
|
||||||
$this->addComponent(new GridFieldDeleteAction(true));
|
$this->addComponent(new GridFieldDeleteAction(true));
|
||||||
|
$this->addComponent(new GridField_ActionMenu());
|
||||||
$this->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
$this->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||||
$this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
|
$this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
|
||||||
$this->addComponent(new GridFieldDetailForm());
|
$this->addComponent(new GridFieldDetailForm());
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Forms\GridField;
|
namespace SilverStripe\Forms\GridField;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\ValidationException;
|
use SilverStripe\ORM\ValidationException;
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ use SilverStripe\ORM\ValidationException;
|
|||||||
* $action = new GridFieldDeleteAction(true);
|
* $action = new GridFieldDeleteAction(true);
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider
|
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,6 +45,47 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
$this->setRemoveRelation($removeRelation);
|
$this->setRemoveRelation($removeRelation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getTitle($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
$field = $this->getRemoveAction($gridField, $record, $columnName);
|
||||||
|
|
||||||
|
if ($field) {
|
||||||
|
return $field->getAttribute('title');
|
||||||
|
}
|
||||||
|
|
||||||
|
return _t(__CLASS__ . '.Delete', "Delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getGroup($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return GridField_ActionMenuItem::DEFAULT_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
* @param string $columnName
|
||||||
|
* @return string|null the attribles for the action
|
||||||
|
*/
|
||||||
|
public function getExtraData($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
|
||||||
|
$field = $this->getRemoveAction($gridField, $record, $columnName);
|
||||||
|
|
||||||
|
if ($field) {
|
||||||
|
return $field->getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a column 'Delete'
|
* Add a column 'Delete'
|
||||||
*
|
*
|
||||||
@ -67,7 +109,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
*/
|
*/
|
||||||
public function getColumnAttributes($gridField, $record, $columnName)
|
public function getColumnAttributes($gridField, $record, $columnName)
|
||||||
{
|
{
|
||||||
return array('class' => 'grid-field__col-compact');
|
return ['class' => 'grid-field__col-compact'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +122,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
public function getColumnMetadata($gridField, $columnName)
|
public function getColumnMetadata($gridField, $columnName)
|
||||||
{
|
{
|
||||||
if ($columnName == 'Actions') {
|
if ($columnName == 'Actions') {
|
||||||
return array('title' => '');
|
return ['title' => ''];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +134,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
*/
|
*/
|
||||||
public function getColumnsHandled($gridField)
|
public function getColumnsHandled($gridField)
|
||||||
{
|
{
|
||||||
return array('Actions');
|
return ['Actions'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +145,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
*/
|
*/
|
||||||
public function getActions($gridField)
|
public function getActions($gridField)
|
||||||
{
|
{
|
||||||
return array('deleterecord', 'unlinkrelation');
|
return ['deleterecord', 'unlinkrelation'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,43 +153,17 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
* @param GridField $gridField
|
* @param GridField $gridField
|
||||||
* @param DataObject $record
|
* @param DataObject $record
|
||||||
* @param string $columnName
|
* @param string $columnName
|
||||||
* @return string the HTML for the column
|
* @return string|null the HTML for the column
|
||||||
*/
|
*/
|
||||||
public function getColumnContent($gridField, $record, $columnName)
|
public function getColumnContent($gridField, $record, $columnName)
|
||||||
{
|
{
|
||||||
if ($this->getRemoveRelation()) {
|
$field = $this->getRemoveAction($gridField, $record, $columnName);
|
||||||
if (!$record->canEdit()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$title = _t(__CLASS__ . '.UnlinkRelation', "Unlink");
|
|
||||||
|
|
||||||
$field = GridField_FormAction::create(
|
if ($field) {
|
||||||
$gridField,
|
return $field->Field();
|
||||||
'UnlinkRelation' . $record->ID,
|
|
||||||
false,
|
|
||||||
"unlinkrelation",
|
|
||||||
array('RecordID' => $record->ID)
|
|
||||||
)
|
|
||||||
->addExtraClass('btn btn--no-text btn--icon-md font-icon-link-broken grid-field__icon-action gridfield-button-unlink')
|
|
||||||
->setAttribute('title', $title)
|
|
||||||
->setAttribute('aria-label', $title);
|
|
||||||
} else {
|
|
||||||
if (!$record->canDelete()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$field = GridField_FormAction::create(
|
|
||||||
$gridField,
|
|
||||||
'DeleteRecord' . $record->ID,
|
|
||||||
false,
|
|
||||||
"deleterecord",
|
|
||||||
array('RecordID' => $record->ID)
|
|
||||||
)
|
|
||||||
->addExtraClass('gridfield-button-delete btn--icon-md font-icon-trash-bin btn--no-text grid-field__icon-action')
|
|
||||||
->setAttribute('title', _t(__CLASS__ . '.Delete', "Delete"))
|
|
||||||
->setDescription(_t(__CLASS__ . '.DELETE_DESCRIPTION', 'Delete'));
|
|
||||||
}
|
}
|
||||||
return $field->Field();
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,6 +204,54 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
* @param string $columnName
|
||||||
|
* @return GridField_FormAction|null
|
||||||
|
*/
|
||||||
|
private function getRemoveAction($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
if ($this->getRemoveRelation()) {
|
||||||
|
if (!$record->canEdit()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$title = _t(__CLASS__ . '.UnlinkRelation', "Unlink");
|
||||||
|
|
||||||
|
$field = GridField_FormAction::create(
|
||||||
|
$gridField,
|
||||||
|
'UnlinkRelation' . $record->ID,
|
||||||
|
false,
|
||||||
|
"unlinkrelation",
|
||||||
|
['RecordID' => $record->ID]
|
||||||
|
)
|
||||||
|
->addExtraClass('btn btn--no-text btn--icon-md font-icon-link-broken grid-field__icon-action gridfield-button-unlink action-menu--handled')
|
||||||
|
->setAttribute('classNames', 'gridfield-button-unlink font-icon-link-broken')
|
||||||
|
->setDescription($title)
|
||||||
|
->setAttribute('aria-label', $title);
|
||||||
|
} else {
|
||||||
|
if (!$record->canDelete()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$title = _t(__CLASS__ . '.Delete', "Delete");
|
||||||
|
|
||||||
|
$field = GridField_FormAction::create(
|
||||||
|
$gridField,
|
||||||
|
'DeleteRecord' . $record->ID,
|
||||||
|
false,
|
||||||
|
"deleterecord",
|
||||||
|
['RecordID' => $record->ID]
|
||||||
|
)
|
||||||
|
->addExtraClass('gridfield-button-delete btn--icon-md font-icon-trash-bin btn--no-text grid-field__icon-action action-menu--handled')
|
||||||
|
->setAttribute('classNames', 'gridfield-button-delete font-icon-trash')
|
||||||
|
->setDescription($title)
|
||||||
|
->setAttribute('aria-label', $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether to remove or delete the relation
|
* Get whether to remove or delete the relation
|
||||||
*
|
*
|
||||||
|
@ -17,7 +17,7 @@ use SilverStripe\View\SSViewer;
|
|||||||
* The default routing applies to the {@link GridFieldDetailForm} component,
|
* The default routing applies to the {@link GridFieldDetailForm} component,
|
||||||
* which has to be added separately to the {@link GridField} configuration.
|
* which has to be added separately to the {@link GridField} configuration.
|
||||||
*/
|
*/
|
||||||
class GridFieldEditButton implements GridField_ColumnProvider
|
class GridFieldEditButton implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuLink
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* HTML classes to be added to GridField edit buttons
|
* HTML classes to be added to GridField edit buttons
|
||||||
@ -27,9 +27,44 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
protected $extraClass = [
|
protected $extraClass = [
|
||||||
'grid-field__icon-action--hidden-on-hover' => true,
|
'grid-field__icon-action--hidden-on-hover' => true,
|
||||||
'font-icon-edit' => true,
|
'font-icon-edit' => true,
|
||||||
'btn--icon-large' => true
|
'btn--icon-large' => true,
|
||||||
|
'action-menu--handled' => true
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getTitle($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return _t(__CLASS__ . '.EDIT', "Edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getGroup($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return GridField_ActionMenuItem::DEFAULT_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getExtraData($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"classNames" => "font-icon-edit action-detail edit-link"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getUrl($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return Controller::join_links($gridField->Link('item'), $record->ID, 'edit');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a column 'Delete'
|
* Add a column 'Delete'
|
||||||
*
|
*
|
||||||
@ -53,7 +88,7 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
*/
|
*/
|
||||||
public function getColumnAttributes($gridField, $record, $columnName)
|
public function getColumnAttributes($gridField, $record, $columnName)
|
||||||
{
|
{
|
||||||
return array('class' => 'grid-field__col-compact');
|
return ['class' => 'grid-field__col-compact'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +101,7 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
public function getColumnMetadata($gridField, $columnName)
|
public function getColumnMetadata($gridField, $columnName)
|
||||||
{
|
{
|
||||||
if ($columnName == 'Actions') {
|
if ($columnName == 'Actions') {
|
||||||
return array('title' => '');
|
return ['title' => ''];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -79,7 +114,7 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
*/
|
*/
|
||||||
public function getColumnsHandled($gridField)
|
public function getColumnsHandled($gridField)
|
||||||
{
|
{
|
||||||
return array('Actions');
|
return ['Actions'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +125,7 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
*/
|
*/
|
||||||
public function getActions($gridField)
|
public function getActions($gridField)
|
||||||
{
|
{
|
||||||
return array();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,10 +139,10 @@ class GridFieldEditButton implements GridField_ColumnProvider
|
|||||||
// No permission checks, handled through GridFieldDetailForm,
|
// No permission checks, handled through GridFieldDetailForm,
|
||||||
// which can make the form readonly if no edit permissions are available.
|
// which can make the form readonly if no edit permissions are available.
|
||||||
|
|
||||||
$data = new ArrayData(array(
|
$data = new ArrayData([
|
||||||
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit'),
|
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit'),
|
||||||
'ExtraClass' => $this->getExtraClass()
|
'ExtraClass' => $this->getExtraClass()
|
||||||
));
|
]);
|
||||||
|
|
||||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||||
return $data->renderWith($template);
|
return $data->renderWith($template);
|
||||||
|
@ -14,16 +14,16 @@ use SilverStripe\View\SSViewer;
|
|||||||
class GridFieldViewButton implements GridField_ColumnProvider
|
class GridFieldViewButton implements GridField_ColumnProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
public function augmentColumns($field, &$cols)
|
public function augmentColumns($field, &$columns)
|
||||||
{
|
{
|
||||||
if (!in_array('Actions', $cols)) {
|
if (!in_array('Actions', $columns)) {
|
||||||
$cols[] = 'Actions';
|
$columns[] = 'Actions';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnsHandled($field)
|
public function getColumnsHandled($field)
|
||||||
{
|
{
|
||||||
return array('Actions');
|
return ['Actions'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnContent($field, $record, $col)
|
public function getColumnContent($field, $record, $col)
|
||||||
@ -31,20 +31,20 @@ class GridFieldViewButton implements GridField_ColumnProvider
|
|||||||
if (!$record->canView()) {
|
if (!$record->canView()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$data = new ArrayData(array(
|
$data = new ArrayData([
|
||||||
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
||||||
));
|
]);
|
||||||
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
$template = SSViewer::get_templates_by_class($this, '', __CLASS__);
|
||||||
return $data->renderWith($template);
|
return $data->renderWith($template);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnAttributes($field, $record, $col)
|
public function getColumnAttributes($field, $record, $col)
|
||||||
{
|
{
|
||||||
return array('class' => 'grid-field__col-compact');
|
return ['class' => 'grid-field__col-compact'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnMetadata($gridField, $col)
|
public function getColumnMetadata($gridField, $col)
|
||||||
{
|
{
|
||||||
return array('title' => null);
|
return ['title' => null];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
src/Forms/GridField/GridField_ActionMenu.php
Normal file
101
src/Forms/GridField/GridField_ActionMenu.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Forms\GridField;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
|
use SilverStripe\View\ArrayData;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups exiting actions in the Actions column in to a menu
|
||||||
|
*/
|
||||||
|
class GridField_ActionMenu implements GridField_ColumnProvider, GridField_ActionProvider
|
||||||
|
{
|
||||||
|
public function augmentColumns($gridField, &$columns)
|
||||||
|
{
|
||||||
|
if (!in_array('Actions', $columns)) {
|
||||||
|
$columns[] = 'Actions';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumnsHandled($gridField)
|
||||||
|
{
|
||||||
|
return ['Actions'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumnContent($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
$items = $this->getItems($gridField);
|
||||||
|
|
||||||
|
if (!$items) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = array_map(function (GridField_ActionMenuItem $item) use ($gridField, $record, $columnName) {
|
||||||
|
return [
|
||||||
|
'type' => $item instanceof GridField_ActionMenuLink ? 'link' : 'submit',
|
||||||
|
'title' => $item->getTitle($gridField, $record, $columnName),
|
||||||
|
'url' => $item instanceof GridField_ActionMenuLink ? $item->getUrl($gridField, $record, $columnName) : null,
|
||||||
|
'group' => $item->getGroup($gridField, $record, $columnName),
|
||||||
|
'data' => $item->getExtraData($gridField, $record, $columnName),
|
||||||
|
];
|
||||||
|
}, $items);
|
||||||
|
|
||||||
|
$templateData = ArrayData::create([
|
||||||
|
'Schema' => Convert::raw2json($schema),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$template = SSViewer::get_templates_by_class($this, '', static::class);
|
||||||
|
|
||||||
|
return $templateData->renderWith($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumnAttributes($gridField, $record, $columnName)
|
||||||
|
{
|
||||||
|
return ['class' => 'grid-field__col-compact action-menu'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumnMetadata($gridField, $columnName)
|
||||||
|
{
|
||||||
|
return ['title' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActions($gridField)
|
||||||
|
{
|
||||||
|
$actions = [];
|
||||||
|
|
||||||
|
foreach ($this->getItems($gridField) as $item) {
|
||||||
|
if ($item instanceof GridField_ActionProvider) {
|
||||||
|
$actions = array_merge($actions, $item->getActions($gridField));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
|
||||||
|
{
|
||||||
|
foreach ($this->getItems($gridField) as $item) {
|
||||||
|
$actions = [];
|
||||||
|
if ($item instanceof GridField_ActionProvider) {
|
||||||
|
$actions = $item->getActions($gridField);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($actionName, $actions)) {
|
||||||
|
$item->handleAction($gridField, $actionName, $arguments, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of items setup
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getItems($gridfield)
|
||||||
|
{
|
||||||
|
$items = $gridfield->config->getComponentsByType(GridField_ActionMenuItem::class)->items;
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
}
|
53
src/Forms/GridField/GridField_ActionMenuItem.php
Normal file
53
src/Forms/GridField/GridField_ActionMenuItem.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Forms\GridField;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GridField action menu item interface, this provides data so the action
|
||||||
|
* will be included if there is a {@see GridField_ActionMenu}
|
||||||
|
*/
|
||||||
|
interface GridField_ActionMenuItem extends GridFieldComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Default group name
|
||||||
|
*/
|
||||||
|
const DEFAULT_GROUP = 'Default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title for this menu item
|
||||||
|
*
|
||||||
|
* @see {@link GridField_ActionMenu->getColumnContent()}
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
*
|
||||||
|
* @return string $title
|
||||||
|
*/
|
||||||
|
public function getTitle($gridField, $record, $columnName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets any extra data that could go in to the schema that the menu generates
|
||||||
|
*
|
||||||
|
* @see {@link GridField_ActionMenu->getColumnContent()}
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
*
|
||||||
|
* @return array $data
|
||||||
|
*/
|
||||||
|
public function getExtraData($gridField, $record, $columnName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the group this menu item will belong to
|
||||||
|
*
|
||||||
|
* @see {@link GridField_ActionMenu->getColumnContent()}
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
*
|
||||||
|
* @return string $group
|
||||||
|
*/
|
||||||
|
public function getGroup($gridField, $record, $columnName);
|
||||||
|
}
|
21
src/Forms/GridField/GridField_ActionMenuLink.php
Normal file
21
src/Forms/GridField/GridField_ActionMenuLink.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Forms\GridField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows GridField_ActionMenuItem to act as a link
|
||||||
|
*/
|
||||||
|
interface GridField_ActionMenuLink extends GridField_ActionMenuItem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Gets the action url for this menu item
|
||||||
|
*
|
||||||
|
* @see {@link GridField_ActionMenu->getColumnContent()}
|
||||||
|
*
|
||||||
|
* @param GridField $gridField
|
||||||
|
* @param DataObject $record
|
||||||
|
*
|
||||||
|
* @return string $url
|
||||||
|
*/
|
||||||
|
public function getUrl($gridField, $record, $columnName);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<a
|
<a
|
||||||
class="grid-field__icon-action {$ExtraClass} action action-detail edit-link"
|
class="grid-field__icon-action {$ExtraClass} action action-detail edit-link"
|
||||||
href="$Link" title="<%t SilverStripe\\Forms\\GridField\\GridFieldEditButton_ss.EDIT 'Edit' %>"
|
href="$Link" title="<%t SilverStripe\\Forms\\GridField\\GridFieldEditButton.EDIT 'Edit' %>"
|
||||||
>
|
>
|
||||||
<span class="sr-only"><%t SilverStripe\\Forms\\GridField\\GridFieldEditButton_ss.EDIT 'Edit' %></span>
|
<span class="sr-only"><%t SilverStripe\\Forms\\GridField\\GridFieldEditButton.EDIT 'Edit' %></span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1 +1,3 @@
|
|||||||
<a class="grid-field__icon-action font-icon-right-open btn--icon-large action action-detail view-link" href="$Link"><span class="sr-only">View</span></a>
|
<a class="grid-field__icon-action font-icon-right-open btn--icon-large action action-detail view-link" href="$Link">
|
||||||
|
<span class="sr-only">View</span>
|
||||||
|
</a>
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<div class="gridfield-actionmenu__container" data-schema="$Schema"></div>
|
@ -416,7 +416,11 @@ JS;
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$button = $name->getParent()->find('xpath', sprintf('//*[@aria-label="%s"]', $buttonLabel));
|
if ($dropdownButton = $name->getParent()->find('css', '.action-menu__toggle')) {
|
||||||
|
$dropdownButton->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
$button = $name->getParent()->find('named', array('link_or_button', $buttonLabel));
|
||||||
|
|
||||||
return $button;
|
return $button;
|
||||||
}
|
}
|
||||||
|
94
tests/php/Forms/GridField/GridFieldActionMenuTest.php
Normal file
94
tests/php/Forms/GridField/GridFieldActionMenuTest.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Forms\Tests\GridField;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Dev\CSSContentParser;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Forms\FieldList;
|
||||||
|
use SilverStripe\Forms\Form;
|
||||||
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
|
use SilverStripe\Forms\GridField\GridField_ActionMenu;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldConfig;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldEditButton;
|
||||||
|
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Cheerleader;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Permissions;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Player;
|
||||||
|
use SilverStripe\Forms\Tests\GridField\GridFieldTest\Team;
|
||||||
|
use SilverStripe\ORM\ArrayList;
|
||||||
|
use SilverStripe\ORM\DataList;
|
||||||
|
use SilverStripe\Security\Member;
|
||||||
|
use SilverStripe\Security\Security;
|
||||||
|
|
||||||
|
class GridFieldActionMenuTest extends SapphireTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ArrayList
|
||||||
|
*/
|
||||||
|
protected $list;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GridField
|
||||||
|
*/
|
||||||
|
protected $gridField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Form
|
||||||
|
*/
|
||||||
|
protected $form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $fixture_file = 'GridFieldActionTest.yml';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $extra_dataobjects = array(
|
||||||
|
Team::class,
|
||||||
|
Cheerleader::class,
|
||||||
|
Player::class,
|
||||||
|
Permissions::class,
|
||||||
|
);
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->list = new DataList(Team::class);
|
||||||
|
$config = GridFieldConfig::create()
|
||||||
|
->addComponent(new GridFieldEditButton())
|
||||||
|
->addComponent(new GridFieldDeleteAction())
|
||||||
|
->addComponent(new GridField_ActionMenu());
|
||||||
|
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
|
||||||
|
$this->form = new Form(null, 'mockform', new FieldList(array($this->gridField)), new FieldList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testShowActionMenu()
|
||||||
|
{
|
||||||
|
if (Security::getCurrentUser()) {
|
||||||
|
Security::setCurrentUser(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = new CSSContentParser($this->gridField->FieldHolder());
|
||||||
|
// Check that there are content
|
||||||
|
$this->assertEquals(4, count($content->getBySelector('.ss-gridfield-item')));
|
||||||
|
// Make sure that there are edit links, even though the user doesn't have "edit" permissions
|
||||||
|
// (they can still view the records)
|
||||||
|
$this->assertEquals(
|
||||||
|
3,
|
||||||
|
count($content->getBySelector('.gridfield-actionmenu__container')),
|
||||||
|
'Edit links should show when not logged in.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testShowEditLinksWithAdminPermission()
|
||||||
|
{
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$content = new CSSContentParser($this->gridField->FieldHolder());
|
||||||
|
$editLinks = $content->getBySelector('.gridfield-actionmenu__container');
|
||||||
|
$this->assertEquals(3, count($editLinks), 'Edit links should show when logged in.');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user