mirror of
https://github.com/symbiote/silverstripe-gridfieldextensions.git
synced 2024-10-22 17:05:39 +02:00
Merge branch '2.0' into 3.0
This commit is contained in:
commit
51dd4cab65
@ -5,9 +5,10 @@ sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=4
|
||||
|
59
README.md
59
README.md
@ -1,29 +1,30 @@
|
||||
# SilverStripe Grid Field Extensions Module
|
||||
|
||||
[![Build Status](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions.svg?branch=master)](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions)
|
||||
[![Latest Stable Version](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/version.svg)](https://github.com/symbiote/silverstripe-gridfieldextensions/releases)
|
||||
[![Latest Unstable Version](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/v/unstable.svg)](https://packagist.org/packages/symbiote/silverstripe-gridfieldextensions)
|
||||
[![Total Downloads](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/downloads.svg)](https://packagist.org/packages/symbiote/silverstripe-gridfieldextensions)
|
||||
[![License](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/license.svg)](https://github.com/symbiote/silverstripe-gridfieldextensions/blob/master/LICENSE.md)
|
||||
|
||||
This module provides a number of useful grid field components:
|
||||
|
||||
* `GridFieldAddExistingSearchButton` - a more advanced search form for adding
|
||||
items.
|
||||
* `GridFieldAddNewInlineButton` - builds on `GridFieldEditableColumns` to allow
|
||||
inline creation of records.
|
||||
* `GridFieldAddNewMultiClass` - lets the user select from a list of classes to
|
||||
create a new record from.
|
||||
* `GridFieldEditableColumns` - allows inline editing of records.
|
||||
* `GridFieldOrderableRows` - drag and drop re-ordering of rows.
|
||||
* `GridFieldRequestHandler` - a basic utility class which can be used to build
|
||||
custom grid field detail views including tabs, breadcrumbs and other CMS
|
||||
features.
|
||||
* `GridFieldTitleHeader` - a simple header which displays column titles.
|
||||
|
||||
This branch will aim for compatibility with SilverStripe 4.x.
|
||||
|
||||
For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/1).
|
||||
|
||||
|
||||
See [docs/en/index.md](docs/en/index.md) for documentation and examples.
|
||||
# SilverStripe Grid Field Extensions Module
|
||||
|
||||
[![Build Status](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions.svg?branch=master)](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions)
|
||||
[![Latest Stable Version](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/version.svg)](https://github.com/symbiote/silverstripe-gridfieldextensions/releases)
|
||||
[![Latest Unstable Version](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/v/unstable.svg)](https://packagist.org/packages/symbiote/silverstripe-gridfieldextensions)
|
||||
[![Total Downloads](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/downloads.svg)](https://packagist.org/packages/symbiote/silverstripe-gridfieldextensions)
|
||||
[![License](https://poser.pugx.org/symbiote/silverstripe-gridfieldextensions/license.svg)](https://github.com/symbiote/silverstripe-gridfieldextensions/blob/master/LICENSE.md)
|
||||
|
||||
This module provides a number of useful grid field components:
|
||||
|
||||
* `GridFieldAddExistingSearchButton` - a more advanced search form for adding
|
||||
items.
|
||||
* `GridFieldAddNewInlineButton` - builds on `GridFieldEditableColumns` to allow
|
||||
inline creation of records.
|
||||
* `GridFieldAddNewMultiClass` - lets the user select from a list of classes to
|
||||
create a new record from.
|
||||
* `GridFieldEditableColumns` - allows inline editing of records.
|
||||
* `GridFieldOrderableRows` - drag and drop re-ordering of rows.
|
||||
* `GridFieldRequestHandler` - a basic utility class which can be used to build
|
||||
custom grid field detail views including tabs, breadcrumbs and other CMS
|
||||
features.
|
||||
* `GridFieldTitleHeader` - a simple header which displays column titles.
|
||||
* `GridFieldConfigurablePaginator` - a paginator for GridField that allows customisable page sizes.
|
||||
|
||||
This branch will aim for compatibility with SilverStripe 4.x.
|
||||
|
||||
For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/1).
|
||||
|
||||
|
||||
See [docs/en/index.md](docs/en/index.md) for documentation and examples.
|
||||
|
@ -1,184 +1,206 @@
|
||||
/**
|
||||
* GridFieldAddExistingSearchButton
|
||||
*/
|
||||
|
||||
.add-existing-search-dialog {
|
||||
min-width: inherit !important;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .field {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .field label {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .Actions {
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li a {
|
||||
background: #FFF;
|
||||
border-bottom-width: 1px;
|
||||
border-color: #CCC;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-style: solid;
|
||||
display: block;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li:first-child a {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li:last-child a {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li a:hover {
|
||||
background: #F4F4F4;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-pagination li {
|
||||
background: #FFF;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
margin-top: 12px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldAddNewInlineButton
|
||||
*/
|
||||
|
||||
.ss-gridfield-inline-new {
|
||||
background: #EFE;
|
||||
}
|
||||
|
||||
.ss-gridfield-inline-new:nth-child(2n) {
|
||||
background: #DFD;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldAddNewMultiClass
|
||||
*/
|
||||
|
||||
.ss-gridfield-add-new-multi-class {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-multi-class .field {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
float: left;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldEditableColumns
|
||||
*/
|
||||
|
||||
.ss-gridfield-editable .readonly {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-editable input.text,
|
||||
.ss-gridfield-editable textarea,
|
||||
.ss-gridfield-editable select,
|
||||
.ss-gridfield-editable .TreeDropdownField {
|
||||
margin: 0 !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-editable select.dropdown {
|
||||
border: 1px solid #b3b3b3;
|
||||
background-color: #fff;
|
||||
padding: 7px 7px;
|
||||
padding-left: 4px;
|
||||
line-height: 16px;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline span.readonly {
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline .col-buttons {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldOrderableRows
|
||||
*/
|
||||
|
||||
.ss-gridfield-orderable thead tr th.col-Reorder span {
|
||||
padding: 0 !important;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder {
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder .handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder .handle .icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 5px;
|
||||
height: 11px;
|
||||
margin: -5px 0 0 -2px;
|
||||
background-image: url('../../framework/admin/thirdparty/jquery-ui-themes/smoothness/images/ui-icons_222222_256x240.png');
|
||||
background-position: -5px -227px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderhelper {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .1);
|
||||
border-top: 1px solid rgba(0, 0, 0, .1);
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, .4);
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ui-droppable {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ui-droppable-active {
|
||||
background-color: #D4CF90 !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ss-gridfield-previouspage {
|
||||
background-position: -16px 9px !important;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ss-gridfield-nextpage {
|
||||
background-position: -40px 9px !important;
|
||||
margin-right: 0;
|
||||
}
|
||||
/**
|
||||
* GridFieldAddExistingSearchButton
|
||||
*/
|
||||
|
||||
.add-existing-search-dialog {
|
||||
min-width: inherit !important;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .field {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .field label {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-form .Actions {
|
||||
margin-top: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li a {
|
||||
background: #FFF;
|
||||
border-bottom-width: 1px;
|
||||
border-color: #CCC;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-style: solid;
|
||||
display: block;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li:first-child a {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li:last-child a {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-items li a:hover {
|
||||
background: #F4F4F4;
|
||||
}
|
||||
|
||||
.add-existing-search-dialog .add-existing-search-pagination li {
|
||||
background: #FFF;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
margin-top: 12px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldAddNewInlineButton
|
||||
*/
|
||||
|
||||
.ss-gridfield-inline-new {
|
||||
background: #EFE;
|
||||
}
|
||||
|
||||
.ss-gridfield-inline-new:nth-child(2n) {
|
||||
background: #DFD;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldAddNewMultiClass
|
||||
*/
|
||||
|
||||
.ss-gridfield-add-new-multi-class {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-multi-class .field {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
float: left;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldEditableColumns
|
||||
*/
|
||||
|
||||
.ss-gridfield-editable .readonly {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-editable input.text,
|
||||
.ss-gridfield-editable textarea,
|
||||
.ss-gridfield-editable select,
|
||||
.ss-gridfield-editable .TreeDropdownField {
|
||||
margin: 0 !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-editable select.dropdown {
|
||||
border: 1px solid #b3b3b3;
|
||||
background-color: #fff;
|
||||
padding: 7px 7px;
|
||||
padding-left: 4px;
|
||||
line-height: 16px;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline span.readonly {
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-add-new-inline .col-buttons {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldOrderableRows
|
||||
*/
|
||||
|
||||
.ss-gridfield-orderable thead tr th.col-Reorder span {
|
||||
padding: 0 !important;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder {
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder .handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable .col-reorder .handle .icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 5px;
|
||||
height: 11px;
|
||||
margin: -5px 0 0 -2px;
|
||||
background-image: url('../../framework/thirdparty/jquery-ui-themes/smoothness/images/ui-icons_222222_256x240.png');
|
||||
background-position: -5px -227px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderhelper {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .1);
|
||||
border-top: 1px solid rgba(0, 0, 0, .1);
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, .4);
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ui-droppable {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ui-droppable-active {
|
||||
background-color: #D4CF90 !important;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ss-gridfield-previouspage {
|
||||
background-position: -16px 9px !important;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ss-gridfield-orderable tfoot .ss-gridfield-nextpage {
|
||||
background-position: -40px 9px !important;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* GridFieldConfigurablePaginator
|
||||
*/
|
||||
.ss-gridfield-configurable-paginator .pagination-page-size {
|
||||
color: #fff;
|
||||
float: left;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.ss-gridfield-configurable-paginator .pagination-page-size-select {
|
||||
margin-left: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ss-gridfield-configurable-paginator .ss-gridfield-pagesize-submit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ss-gridfield-configurable-paginator .pagination-page-number input {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -106,3 +106,36 @@ class Item extends DataObject {
|
||||
|
||||
**Please NOTE:** There is a limitation when using `GridFieldOrderableRows` on unsaved data objects; namely, that it doesn't work as without data being saved, the list of related objects has no context. Please check `$this->ID` before adding the `GridFieldOrderableRows` component to the grid field config (or even, before adding the gridfield at all).
|
||||
|
||||
Configurable Paginator
|
||||
----------------------
|
||||
|
||||
The `GridFieldConfigurablePaginator` component allows you to have a page size dropdown added to your GridField
|
||||
pagination controls. The page sizes are configurable via the configuration system, or at call time using the public API.
|
||||
To use this component you should remove the original paginator component first:
|
||||
|
||||
```php
|
||||
$gridField->getConfig()
|
||||
->removeComponentsByType('GridFieldPaginator')
|
||||
->addComponent(new GridFieldConfigurablePaginator());
|
||||
```
|
||||
|
||||
You can configure the page sizes with the configuration system. Note that merging is the default strategy, so to replace
|
||||
the default sizes with your own you will need to unset the original first, for example:
|
||||
|
||||
```php
|
||||
# File: mysite/_config.php
|
||||
Config::inst()->remove('GridFieldConfigurablePaginator', 'default_page_sizes');
|
||||
Config::inst()->update('GridFieldConfigurablePaginator', 'default_page_sizes', array(100, 200, 500));
|
||||
```
|
||||
|
||||
You can also override these at call time:
|
||||
|
||||
```php
|
||||
$paginator = new GridFieldConfigurablePaginator(100, array(100, 200, 500));
|
||||
|
||||
$paginator->setPageSizes(array(200, 500, 1000));
|
||||
$paginator->setItemsPerPage(500);
|
||||
```
|
||||
|
||||
The first shown record will be maintained across page size changes, and the number of pages and current page will be
|
||||
recalculated on each request, based on the current first shown record and page size.
|
||||
|
@ -387,5 +387,14 @@
|
||||
if(this.hasClass("ui-droppable")) this.droppable("destroy");
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GridFieldConfigurablePaginator
|
||||
*/
|
||||
$('.ss-gridfield-configurable-paginator .pagination-page-size-select').entwine({
|
||||
onchange: function () {
|
||||
this.parent().find('.ss-gridfield-pagesize-submit').trigger('click');
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace Symbiote\GridFieldExtensions;
|
||||
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Object;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
|
||||
@ -109,7 +109,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
|
||||
$handled = array_keys($editable->getDisplayFields($grid));
|
||||
|
||||
if ($grid->getList()) {
|
||||
$record = Object::create($grid->getModelClass());
|
||||
$record = Injector::inst()->create($grid->getModelClass());
|
||||
} else {
|
||||
$record = null;
|
||||
}
|
||||
@ -168,7 +168,6 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
|
||||
$editable = $grid->getConfig()->getComponentByType('Symbiote\\GridFieldExtensions\\GridFieldEditableColumns');
|
||||
/** @var GridFieldOrderableRows $sortable */
|
||||
$sortable = $grid->getConfig()->getComponentByType('Symbiote\\GridFieldExtensions\\GridFieldOrderableRows');
|
||||
$form = $editable->getForm($grid, $record);
|
||||
|
||||
if (!singleton($class)->canCreate()) {
|
||||
return;
|
||||
@ -178,6 +177,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
|
||||
$item = $class::create();
|
||||
$extra = array();
|
||||
|
||||
$form = $editable->getForm($grid, $record);
|
||||
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
|
||||
$form->saveInto($item);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Symbiote\GridFieldExtensions;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
@ -145,7 +146,12 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
$sanitised = array();
|
||||
foreach($result as $class=>$title) {
|
||||
$sanitised[$this->sanitiseClassName($class)] = $title;
|
||||
}
|
||||
|
||||
return $sanitised;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,7 +185,7 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL
|
||||
* Handles adding a new instance of a selected class.
|
||||
*
|
||||
* @param GridField $grid
|
||||
* @param SS_HTTPRequest $request
|
||||
* @param HTTPRequest $request
|
||||
* @return GridFieldAddNewMultiClassHandler
|
||||
*/
|
||||
public function handleAdd($grid, $request)
|
||||
@ -196,11 +202,12 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL
|
||||
throw new HTTPResponse_Exception(400);
|
||||
}
|
||||
|
||||
$unsanitisedClass = $this->unsanitiseClassName($class);
|
||||
$handler = Injector::inst()->create(
|
||||
$this->itemRequestClass,
|
||||
$grid,
|
||||
$component,
|
||||
new $class(),
|
||||
new $unsanitisedClass(),
|
||||
$grid->getForm()->getController(),
|
||||
'add-multi-class'
|
||||
);
|
||||
@ -222,7 +229,7 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL
|
||||
|
||||
GridFieldExtensions::include_requirements();
|
||||
|
||||
$field = new DropdownField(sprintf('%s[ClassName]', __CLASS__), '', $classes, $this->defaultClass);
|
||||
$field = new DropdownField(sprintf('%s[%s]', __CLASS__, $grid->getName()), '', $classes, $this->defaultClass);
|
||||
if (Config::inst()->get(__CLASS__, 'showEmptyString')) {
|
||||
$field->setEmptyString(_t('GridFieldExtensions.SELECTTYPETOCREATE', '(Select type to create)'));
|
||||
}
|
||||
@ -254,4 +261,20 @@ class GridFieldAddNewMultiClass implements GridField_HTMLProvider, GridField_URL
|
||||
$this->itemRequestClass = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitise a model class' name for inclusion in a link
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitiseClassName($class) {
|
||||
return str_replace('\\', '-', $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsanitise a model class' name from a URL param
|
||||
* @return string
|
||||
*/
|
||||
protected function unsanitiseClassName($class) {
|
||||
return str_replace('-', '\\', $class);
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,16 @@ class GridFieldAddNewMultiClassHandler extends GridFieldDetailForm_ItemRequest
|
||||
return Controller::join_links(
|
||||
$this->gridField->Link(),
|
||||
'add-multi-class',
|
||||
get_class($this->record)
|
||||
$this->sanitiseClassName(get_class($this->record))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitise a model class' name for inclusion in a link
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitiseClassName($class) {
|
||||
return str_replace('\\', '-', $class);
|
||||
}
|
||||
}
|
||||
|
458
src/GridFieldConfigurablePaginator.php
Normal file
458
src/GridFieldConfigurablePaginator.php
Normal file
@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
namespace Symbiote\GridFieldExtensions;
|
||||
|
||||
use Exception;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||
use SilverStripe\Forms\GridField\GridFieldPaginator;
|
||||
use SilverStripe\Forms\GridField\GridState_Data;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\Limitable;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\ORM\UnsavedRelationList;
|
||||
use SilverStripe\View\ArrayData;
|
||||
|
||||
/**
|
||||
* GridFieldConfigurablePaginator paginates the {@link GridField} list and adds controls to the bottom of
|
||||
* the {@link GridField}. The page sizes are configurable.
|
||||
*/
|
||||
class GridFieldConfigurablePaginator extends GridFieldPaginator
|
||||
{
|
||||
/**
|
||||
* Specifies default page sizes
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $default_page_sizes = array(15, 30, 60);
|
||||
|
||||
/**
|
||||
* Which template to use for rendering
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $itemClass = GridFieldConfigurablePaginator::class;
|
||||
|
||||
/**
|
||||
* @var GridField
|
||||
*/
|
||||
protected $gridField;
|
||||
|
||||
/**
|
||||
* @var GridState_Data
|
||||
*/
|
||||
protected $gridFieldState;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $pageSizes = array();
|
||||
|
||||
/**
|
||||
* @param int $itemsPerPage How many items should be displayed per page
|
||||
* @param int $pageSizes The page sizes to show in the dropdown
|
||||
*/
|
||||
public function __construct($itemsPerPage = null, $pageSizes = null)
|
||||
{
|
||||
$this->setPageSizes($pageSizes ?: self::config()->get('default_page_sizes'));
|
||||
|
||||
if (!$itemsPerPage) {
|
||||
$itemsPerPage = $this->pageSizes[0];
|
||||
}
|
||||
|
||||
parent::__construct($itemsPerPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of records in the list
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalRecords()
|
||||
{
|
||||
return (int) $this->getGridField()->getList()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first shown record number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFirstShown()
|
||||
{
|
||||
$firstShown = $this->getGridPagerState()->firstShown ?: 1;
|
||||
// Prevent visiting a page with an offset higher than the total number of items
|
||||
if ($firstShown > $this->getTotalRecords()) {
|
||||
$this->getGridPagerState()->firstShown = $firstShown = 1;
|
||||
}
|
||||
return $firstShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the first shown record number. Will be stored in the state.
|
||||
*
|
||||
* @param int $firstShown
|
||||
* @return $this
|
||||
*/
|
||||
public function setFirstShown($firstShown = 1)
|
||||
{
|
||||
$this->getGridPagerState()->firstShown = (int) $firstShown;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last shown record number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastShown()
|
||||
{
|
||||
return min($this->getTotalRecords(), $this->getFirstShown() + $this->getItemsPerPage() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of pages, given the current number of items per page. The total
|
||||
* pages might be higher than <totalitems> / <itemsperpage> if the first shown record
|
||||
* is half way through a standard page break point.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalPages()
|
||||
{
|
||||
// Pages before
|
||||
$pages = ceil(($this->getFirstShown() - 1) / $this->getItemsPerPage());
|
||||
|
||||
// Current page
|
||||
$pages++;
|
||||
|
||||
// Pages after
|
||||
$pages += ceil(($this->getTotalRecords() - $this->getLastShown()) / $this->getItemsPerPage());
|
||||
|
||||
return (int) $pages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the page currently active. This is calculated by adding one to the previous number
|
||||
* of pages calculated via the "first shown record" position.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentPage()
|
||||
{
|
||||
return (int) ceil(($this->getFirstShown() - 1) / $this->getItemsPerPage()) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next page number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNextPage()
|
||||
{
|
||||
return min($this->getTotalPages(), $this->getCurrentPage() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous page number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPreviousPage()
|
||||
{
|
||||
return max(1, $this->getCurrentPage() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the page sizes to use in the "Show x" dropdown
|
||||
*
|
||||
* @param array $pageSizes
|
||||
* @return $this
|
||||
*/
|
||||
public function setPageSizes(array $pageSizes)
|
||||
{
|
||||
$this->pageSizes = $pageSizes;
|
||||
|
||||
// Reset items per page
|
||||
$this->setItemsPerPage(current($pageSizes));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sizes for the "Show x" dropdown
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPageSizes()
|
||||
{
|
||||
return $this->pageSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of page sizes for use in templates as a dropdown
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getPageSizesAsList()
|
||||
{
|
||||
$pageSizes = ArrayList::create();
|
||||
$perPage = $this->getItemsPerPage();
|
||||
foreach ($this->getPageSizes() as $pageSize) {
|
||||
$pageSizes->push(array(
|
||||
'Size' => $pageSize,
|
||||
'Selected' => $pageSize == $perPage
|
||||
));
|
||||
}
|
||||
return $pageSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GridField used in this request
|
||||
*
|
||||
* @return GridField
|
||||
* @throws Exception If the GridField has not been previously set
|
||||
*/
|
||||
public function getGridField()
|
||||
{
|
||||
if ($this->gridField) {
|
||||
return $this->gridField;
|
||||
}
|
||||
throw new Exception('No GridField available yet for this request!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the GridField so it can be used in other parts of the component during this request
|
||||
*
|
||||
* @param GridField $gridField
|
||||
* @return $this
|
||||
*/
|
||||
public function setGridField(GridField $gridField)
|
||||
{
|
||||
$this->gridField = $gridField;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
|
||||
{
|
||||
$this->setGridField($gridField);
|
||||
|
||||
if ($actionName !== 'paginate') {
|
||||
return;
|
||||
}
|
||||
$state = $this->getGridPagerState();
|
||||
|
||||
$state->firstShown = (int) $arguments['first-shown'];
|
||||
$state->pageSize = $data[$gridField->getName()]['page-sizes'];
|
||||
|
||||
$this->setItemsPerPage($state->pageSize);
|
||||
}
|
||||
|
||||
public function getManipulatedData(GridField $gridField, SS_List $dataList)
|
||||
{
|
||||
// Assign the GridField to the class so it can be used later in the request
|
||||
$this->setGridField($gridField);
|
||||
|
||||
// Retain page sizes during actions provided by other components
|
||||
$state = $this->getGridPagerState();
|
||||
if (is_numeric($state->pageSize)) {
|
||||
$this->setItemsPerPage($state->pageSize);
|
||||
}
|
||||
|
||||
if (!($dataList instanceof Limitable) || ($dataList instanceof UnsavedRelationList)) {
|
||||
return $dataList;
|
||||
}
|
||||
|
||||
return $dataList->limit($this->getItemsPerPage(), $this->getFirstShown() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the configurable page size options to the template data
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param GridField $gridField
|
||||
* @return ArrayList|null
|
||||
*/
|
||||
public function getTemplateParameters(GridField $gridField)
|
||||
{
|
||||
$state = $this->getGridPagerState();
|
||||
if (is_numeric($state->pageSize)) {
|
||||
$this->setItemsPerPage($state->pageSize);
|
||||
}
|
||||
$arguments = $this->getPagerArguments();
|
||||
|
||||
// Figure out which page and record range we're on
|
||||
if (!$arguments['total-rows']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Define a list of the FormActions that should be generated for pager controls (see getPagerActions())
|
||||
$controls = array(
|
||||
'first' => array(
|
||||
'title' => 'First',
|
||||
'args' => array('first-shown' => 1),
|
||||
'extra-class' => 'ss-gridfield-firstpage',
|
||||
'disable-previous' => ($this->getCurrentPage() == 1)
|
||||
),
|
||||
'prev' => array(
|
||||
'title' => 'Previous',
|
||||
'args' => array('first-shown' => $arguments['first-shown'] - $this->getItemsPerPage()),
|
||||
'extra-class' => 'ss-gridfield-previouspage',
|
||||
'disable-previous' => ($this->getCurrentPage() == 1)
|
||||
),
|
||||
'next' => array(
|
||||
'title' => 'Next',
|
||||
'args' => array('first-shown' => $arguments['first-shown'] + $this->getItemsPerPage()),
|
||||
'extra-class' => 'ss-gridfield-nextpage',
|
||||
'disable-next' => ($this->getCurrentPage() == $arguments['total-pages'])
|
||||
),
|
||||
'last' => array(
|
||||
'title' => 'Last',
|
||||
'args' => array('first-shown' => ($this->getTotalPages() - 1) * $this->getItemsPerPage() + 1),
|
||||
'extra-class' => 'ss-gridfield-lastpage',
|
||||
'disable-next' => ($this->getCurrentPage() == $arguments['total-pages'])
|
||||
),
|
||||
'pagesize' => array(
|
||||
'title' => 'Page Size',
|
||||
'args' => array('first-shown' => $arguments['first-shown']),
|
||||
'extra-class' => 'ss-gridfield-pagesize-submit'
|
||||
),
|
||||
);
|
||||
|
||||
if ($controls['prev']['args']['first-shown'] < 1) {
|
||||
$controls['prev']['args']['first-shown'] = 1;
|
||||
}
|
||||
|
||||
$actions = $this->getPagerActions($controls, $gridField);
|
||||
|
||||
// Render in template
|
||||
return ArrayData::create(array(
|
||||
'OnlyOnePage' => ($arguments['total-pages'] == 1),
|
||||
'FirstPage' => $actions['first'],
|
||||
'PreviousPage' => $actions['prev'],
|
||||
'NextPage' => $actions['next'],
|
||||
'LastPage' => $actions['last'],
|
||||
'PageSizesSubmit' => $actions['pagesize'],
|
||||
'CurrentPageNum' => $this->getCurrentPage(),
|
||||
'NumPages' => $arguments['total-pages'],
|
||||
'FirstShownRecord' => $arguments['first-shown'],
|
||||
'LastShownRecord' => $arguments['last-shown'],
|
||||
'NumRecords' => $arguments['total-rows'],
|
||||
'PageSizes' => $this->getPageSizesAsList(),
|
||||
'PageSizesName' => $gridField->getName() . '[page-sizes]',
|
||||
));
|
||||
}
|
||||
|
||||
public function getHTMLFragments($gridField)
|
||||
{
|
||||
GridFieldExtensions::include_requirements();
|
||||
|
||||
$gridField->addExtraClass('ss-gridfield-configurable-paginator');
|
||||
|
||||
$forTemplate = $this->getTemplateParameters($gridField);
|
||||
if ($forTemplate) {
|
||||
return array(
|
||||
'footer' => $forTemplate->renderWith(
|
||||
$this->itemClass,
|
||||
array('Colspan' => count($gridField->getColumns()))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the arguments for the pagination: total rows, pages, first record etc
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPagerArguments()
|
||||
{
|
||||
return array(
|
||||
'total-rows' => $this->getTotalRecords(),
|
||||
'total-pages' => $this->getTotalPages(),
|
||||
'items-per-page' => $this->getItemsPerPage(),
|
||||
'first-shown' => $this->getFirstShown(),
|
||||
'last-shown' => $this->getLastShown(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns FormActions for each of the pagination actions, in an array
|
||||
*
|
||||
* @param array $controls
|
||||
* @param GridField $gridField
|
||||
* @return GridField_FormAction[]
|
||||
*/
|
||||
public function getPagerActions(array $controls, GridField $gridField)
|
||||
{
|
||||
$actions = array();
|
||||
|
||||
foreach ($controls as $key => $arguments) {
|
||||
$action = GridField_FormAction::create(
|
||||
$gridField,
|
||||
'pagination_' . $key,
|
||||
$arguments['title'],
|
||||
'paginate',
|
||||
$arguments['args']
|
||||
);
|
||||
|
||||
if (isset($arguments['extra-class'])) {
|
||||
$action->addExtraClass($arguments['extra-class']);
|
||||
}
|
||||
|
||||
if (isset($arguments['disable-previous']) && $arguments['disable-previous']) {
|
||||
$action = $action->performDisabledTransformation();
|
||||
} elseif (isset($arguments['disable-next']) && $arguments['disable-next']) {
|
||||
$action = $action->performDisabledTransformation();
|
||||
}
|
||||
|
||||
$actions[$key] = $action;
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
public function getActions($gridField)
|
||||
{
|
||||
return array('paginate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state from the current request's GridField and sets some default values on it
|
||||
*
|
||||
* @param GridField $gridField Not used, but present for parent method compatibility
|
||||
* @return GridState_Data
|
||||
*/
|
||||
protected function getGridPagerState(GridField $gridField = null)
|
||||
{
|
||||
if (!$this->gridFieldState) {
|
||||
$state = $this->getGridField()->State->GridFieldConfigurablePaginator;
|
||||
|
||||
// SS 3.1 compatibility (missing __call)
|
||||
if (is_object($state->firstShown)) {
|
||||
$state->firstShown = 1;
|
||||
}
|
||||
if (is_object($state->pageSize)) {
|
||||
$state->pageSize = $this->getItemsPerPage();
|
||||
}
|
||||
|
||||
// Handle free input in the page number field
|
||||
$parentState = $this->getGridField()->State->GridFieldPaginator;
|
||||
if (is_object($parentState->currentPage)) {
|
||||
$parentState->currentPage = 0;
|
||||
}
|
||||
|
||||
if ($parentState->currentPage >= 1) {
|
||||
$state->firstShown = ($parentState->currentPage - 1) * $this->getItemsPerPage() + 1;
|
||||
$parentState->currentPage = null;
|
||||
}
|
||||
|
||||
$this->gridFieldState = $state;
|
||||
}
|
||||
|
||||
return $this->gridFieldState;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace Symbiote\GridFieldExtensions;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Core\Object;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormField;
|
||||
@ -16,6 +16,7 @@ use SilverStripe\Forms\GridField\GridField_URLHandler;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\ReadonlyField;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\ORM\ManyManyList;
|
||||
use Exception;
|
||||
|
||||
@ -114,8 +115,6 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
|
||||
/** @var GridFieldOrderableRows $sortable */
|
||||
$sortable = $grid->getConfig()->getComponentByType('Symbiote\\GridFieldExtensions\\GridFieldOrderableRows');
|
||||
|
||||
$form = $this->getForm($grid, $record);
|
||||
|
||||
foreach ($value[__CLASS__] as $id => $fields) {
|
||||
if (!is_numeric($id) || !is_array($fields)) {
|
||||
continue;
|
||||
@ -129,6 +128,7 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
|
||||
|
||||
$extra = array();
|
||||
|
||||
$form = $this->getForm($grid, $record);
|
||||
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
|
||||
$form->saveInto($item);
|
||||
|
||||
@ -219,7 +219,7 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
|
||||
$extra = $list->getExtraFields();
|
||||
|
||||
if ($extra && array_key_exists($col, $extra)) {
|
||||
$field = Object::create_from_string($extra[$col], $col)->scaffoldFormField();
|
||||
$field = Injector::inst()->create($extra[$col], $col)->scaffoldFormField();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
use SilverStripe\ORM\ManyManyList;
|
||||
@ -514,7 +515,8 @@ class GridFieldOrderableRows extends RequestHandler implements
|
||||
// match to order the objects.
|
||||
if (!$isVersioned) {
|
||||
$sortTable = $this->getSortTable($list);
|
||||
$additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : '';
|
||||
$now = DBDatetime::now()->Rfc2822();
|
||||
$additionalSQL = (!$list instanceof ManyManyList) ? ", \"LastEdited\" = '$now'" : '';
|
||||
foreach ($sortedIDs as $sortValue => $id) {
|
||||
if ($map[$id] != $sortValue) {
|
||||
DB::query(sprintf(
|
||||
@ -550,7 +552,8 @@ class GridFieldOrderableRows extends RequestHandler implements
|
||||
$field = $this->getSortField();
|
||||
$table = $this->getSortTable($list);
|
||||
$clause = sprintf('"%s"."%s" = 0', $table, $this->getSortField());
|
||||
$additionalSQL = (!$list instanceof ManyManyList) ? ', "LastEdited" = NOW()' : '';
|
||||
$now = DBDatetime::now()->Rfc2822();
|
||||
$additionalSQL = (!$list instanceof ManyManyList) ? ", \"LastEdited\" = '$now'" : '';
|
||||
|
||||
foreach ($list->where($clause)->column('ID') as $id) {
|
||||
$max = DB::query(sprintf('SELECT MAX("%s") + 1 FROM "%s"', $field, $table));
|
||||
|
30
templates/GridFieldConfigurablePaginator.ss
Normal file
30
templates/GridFieldConfigurablePaginator.ss
Normal file
@ -0,0 +1,30 @@
|
||||
<tr>
|
||||
<td class="bottom-all" colspan="$Colspan">
|
||||
<span class="pagination-page-size">
|
||||
<%t GridFieldConfigurablePaginator.SHOW 'Show' %>
|
||||
<select name="$PageSizesName" class="pagination-page-size-select" data-skip-autofocus="true">
|
||||
<% loop $PageSizes %>
|
||||
<option <% if $Selected %>selected="selected"<% end_if %>>$Size</option>
|
||||
<% end_loop %>
|
||||
</select>
|
||||
$PageSizesSubmit
|
||||
</span>
|
||||
<% if not $OnlyOnePage %>
|
||||
<div class="datagrid-pagination">
|
||||
$FirstPage $PreviousPage
|
||||
<span class="pagination-page-number">
|
||||
<%t Pagination.Page 'Page' %>
|
||||
<input class="text" value="$CurrentPageNum" data-skip-autofocus="true" />
|
||||
<%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
|
||||
$NumPages
|
||||
</span>
|
||||
$NextPage $LastPage
|
||||
</div>
|
||||
<% end_if %>
|
||||
<span class="pagination-records-number">
|
||||
{$FirstShownRecord}–{$LastShownRecord}
|
||||
<%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
|
||||
$NumRecords
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
48
tests/GridFieldAddNewMultiClassWithNamespacesTest.php
Normal file
48
tests/GridFieldAddNewMultiClassWithNamespacesTest.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
|
||||
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClassHandler;
|
||||
use Symbiote\GridFieldExtensions\Tests\Stub\NamespacedClass;
|
||||
|
||||
class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest {
|
||||
|
||||
public function testGetClassesWithNamespaces() {
|
||||
$grid = new GridField('TestGridField');
|
||||
$grid->setModelClass(NamespacedClass::class);
|
||||
|
||||
$component = new GridFieldAddNewMultiClass();
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass' => 'NamespacedClass'
|
||||
),
|
||||
$component->getClasses($grid),
|
||||
'Namespaced classes are sanitised'
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandleAddWithNamespaces() {
|
||||
$grid = new GridField('TestGridField');
|
||||
$grid->getConfig()->addComponent(new GridFieldDetailForm());
|
||||
$grid->setModelClass(NamespacedClass::class);
|
||||
$grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create()));
|
||||
|
||||
$request = new HTTPRequest('POST', 'test');
|
||||
$request->setRouteParams(array('ClassName' => 'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass'));
|
||||
|
||||
$component = new GridFieldAddNewMultiClass();
|
||||
$response = $component->handleAdd($grid, $request);
|
||||
|
||||
$record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record');
|
||||
$record->setAccessible(true);
|
||||
$this->assertInstanceOf(NamespacedClass::class, $record->getValue($response));
|
||||
}
|
||||
|
||||
}
|
238
tests/GridFieldConfigurablePaginatorTest.php
Normal file
238
tests/GridFieldConfigurablePaginatorTest.php
Normal file
@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridField_FormAction;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use Symbiote\GridFieldExtensions\GridFieldConfigurablePaginator;
|
||||
|
||||
class GridFieldConfigurablePaginatorTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* @var GridField
|
||||
*/
|
||||
protected $gridField;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Some dummy GridField list data
|
||||
$data = ArrayList::create();
|
||||
for ($i = 1; $i <= 130; $i++) {
|
||||
$data->push(array('ID' => $i));
|
||||
}
|
||||
|
||||
$this->gridField = GridField::create('Mock', null, $data);
|
||||
}
|
||||
|
||||
public function testGetTotalRecords()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator;
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
$this->assertSame(130, $paginator->getTotalRecords());
|
||||
}
|
||||
|
||||
public function testGetFirstShown()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator;
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
// No state
|
||||
$this->assertSame(1, $paginator->getFirstShown());
|
||||
|
||||
// With a state
|
||||
$paginator->setFirstShown(123);
|
||||
$this->assertSame(123, $paginator->getFirstShown());
|
||||
|
||||
// Too high!
|
||||
$paginator->setFirstShown(234);
|
||||
$this->assertSame(1, $paginator->getFirstShown());
|
||||
}
|
||||
|
||||
public function testGetLastShown()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(20, array(10, 20, 30));
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
$this->assertSame(20, $paginator->getLastShown());
|
||||
|
||||
$paginator->setFirstShown(5);
|
||||
$this->assertSame(24, $paginator->getLastShown());
|
||||
}
|
||||
|
||||
public function testGetTotalPages()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60));
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
// Default calculation
|
||||
$this->assertSame(7, $paginator->getTotalPages());
|
||||
|
||||
// With a standard "first shown" record number, e.g. page 2
|
||||
$paginator->setFirstShown(21);
|
||||
$this->assertSame(7, $paginator->getTotalPages());
|
||||
|
||||
// Non-standard "first shown", e.g. when a page size is changed at page 3. In this case the first page is
|
||||
// 20 records, the second page is 7 records, third page 20 records, etc
|
||||
$paginator->setFirstShown(27);
|
||||
$this->assertSame(8, $paginator->getTotalPages());
|
||||
|
||||
// ... and when the page size has also been changed. In this case the first page is 57 records, second page
|
||||
// 60 records and last page is 13 records
|
||||
$paginator->setFirstShown(57);
|
||||
$paginator->setItemsPerPage(60);
|
||||
$this->assertSame(3, $paginator->getTotalPages());
|
||||
}
|
||||
|
||||
public function testItemsPerPageIsSetToFirstInPageSizesListWhenChanged()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60));
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
// Initial state, should be what was provided to the constructor
|
||||
$this->assertSame(20, $paginator->getItemsPerPage());
|
||||
|
||||
$paginator->setPageSizes(array(50, 100, 200));
|
||||
|
||||
// Set via public API, should now be set to 50
|
||||
$this->assertSame(50, $paginator->getItemsPerPage());
|
||||
}
|
||||
|
||||
public function testGetCurrentPreviousAndNextPages()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(20, array(20, 40, 60));
|
||||
$paginator->setGridField($this->gridField);
|
||||
|
||||
// No page selected (first page)
|
||||
$this->assertSame(1, $paginator->getCurrentPage());
|
||||
$this->assertSame(1, $paginator->getPreviousPage());
|
||||
$this->assertSame(2, $paginator->getNextPage());
|
||||
|
||||
// Second page
|
||||
$paginator->setFirstShown(21);
|
||||
$this->assertSame(2, $paginator->getCurrentPage());
|
||||
$this->assertSame(1, $paginator->getPreviousPage());
|
||||
$this->assertSame(3, $paginator->getNextPage());
|
||||
|
||||
// Third page
|
||||
$paginator->setFirstShown(41);
|
||||
$this->assertSame(3, $paginator->getCurrentPage());
|
||||
$this->assertSame(2, $paginator->getPreviousPage());
|
||||
$this->assertSame(4, $paginator->getNextPage());
|
||||
|
||||
// Fourth page, partial record count
|
||||
$paginator->setFirstShown(42);
|
||||
$this->assertSame(4, $paginator->getCurrentPage());
|
||||
$this->assertSame(3, $paginator->getPreviousPage());
|
||||
$this->assertSame(5, $paginator->getNextPage());
|
||||
|
||||
// Last page (default paging)
|
||||
$paginator->setFirstShown(121);
|
||||
$this->assertSame(7, $paginator->getCurrentPage());
|
||||
$this->assertSame(6, $paginator->getPreviousPage());
|
||||
$this->assertSame(7, $paginator->getNextPage());
|
||||
|
||||
// Non-standard page size should recalculate the page numbers to be relative to the page size
|
||||
$paginator->setFirstShown(121);
|
||||
$paginator->setItemsPerPage(60);
|
||||
$this->assertSame(3, $paginator->getCurrentPage());
|
||||
$this->assertSame(2, $paginator->getPreviousPage());
|
||||
$this->assertSame(3, $paginator->getNextPage());
|
||||
}
|
||||
|
||||
public function testPageSizesAreConfigurable()
|
||||
{
|
||||
// Via constructor
|
||||
$paginator = new GridFieldConfigurablePaginator(3, array(2, 4, 6));
|
||||
$this->assertSame(3, $paginator->getItemsPerPage());
|
||||
$this->assertSame(array(2, 4, 6), $paginator->getPageSizes());
|
||||
|
||||
// Via public API
|
||||
$paginator->setPageSizes(array(10, 20, 30));
|
||||
$this->assertSame(array(10, 20, 30), $paginator->getPageSizes());
|
||||
|
||||
// Via default configuration
|
||||
$paginator = new GridFieldConfigurablePaginator;
|
||||
$default = Config::inst()->get(GridFieldConfigurablePaginator::class, 'default_page_sizes');
|
||||
$this->assertSame($default, $paginator->getPageSizes());
|
||||
}
|
||||
|
||||
public function testGetPageSizesAsList()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(10, array(10, 20, 30));
|
||||
$this->assertListEquals(array(
|
||||
array('Size' => '10', 'Selected' => true),
|
||||
array('Size' => '20', 'Selected' => false),
|
||||
array('Size' => '30', 'Selected' => false),
|
||||
), $paginator->getPageSizesAsList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage No GridField available yet for this request!
|
||||
*/
|
||||
public function testGetGridFieldThrowsExceptionWhenNotSet()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator;
|
||||
$paginator->getGridField();
|
||||
}
|
||||
|
||||
public function testGetPagerActions()
|
||||
{
|
||||
$controls = array(
|
||||
'prev' => array(
|
||||
'title' => 'Previous',
|
||||
'args' => array(
|
||||
'next-page' => 123,
|
||||
'first-shown' => 234
|
||||
),
|
||||
'extra-class' => 'ss-gridfield-previouspage',
|
||||
'disable-previous' => false
|
||||
),
|
||||
'next' => array(
|
||||
'title' => 'Next',
|
||||
'args' => array(
|
||||
'next-page' => 234,
|
||||
'first-shown' => 123
|
||||
),
|
||||
'extra-class' => 'ss-gridfield-nextpage',
|
||||
'disable-next' => true
|
||||
)
|
||||
);
|
||||
|
||||
$gridField = $this->getMockBuilder(GridField::class)->disableOriginalConstructor()->getMock();
|
||||
$paginator = new GridFieldConfigurablePaginator;
|
||||
$result = $paginator->getPagerActions($controls, $gridField);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertArrayHasKey('next', $result);
|
||||
$this->assertContainsOnlyInstancesOf(GridField_FormAction::class, $result);
|
||||
|
||||
$this->assertFalse($result['prev']->isDisabled());
|
||||
|
||||
$this->assertTrue((bool) $result['next']->hasClass('ss-gridfield-nextpage'));
|
||||
$this->assertTrue($result['next']->isDisabled());
|
||||
}
|
||||
|
||||
public function testSinglePageWithLotsOfItems()
|
||||
{
|
||||
$paginator = new GridFieldConfigurablePaginator(null, array(100, 200, 300));
|
||||
$this->assertSame(100, $paginator->getItemsPerPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set something to the GridField's paginator state data
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
protected function setState($key, $value)
|
||||
{
|
||||
$this->gridField->State->GridFieldConfigurablePaginator->$key = $value;
|
||||
return $this;
|
||||
}
|
||||
}
|
17
tests/Stub/NamespacedClass.php
Normal file
17
tests/Stub/NamespacedClass.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Symbiote\GridFieldExtensions\Tests\Stub;
|
||||
|
||||
use Silverstripe\Dev\TestOnly;
|
||||
|
||||
class NamespacedClass implements TestOnly
|
||||
{
|
||||
public function i18n_singular_name()
|
||||
{
|
||||
return 'NamespacedClass';
|
||||
}
|
||||
public function canCreate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user