Merge branch 'master' into pulls/3.0/fix-closure-import

This commit is contained in:
Dylan Wagstaff 2017-10-26 14:34:43 +13:00 committed by GitHub
commit a7c5283bdd
27 changed files with 1080 additions and 227 deletions

View File

@ -1,26 +1,31 @@
# See https://github.com/silverstripe/silverstripe-travis-support for setup details
sudo: false
language: php language: php
php:
- 5.6
- 7.0
- 7.1
env: env:
- DB=MYSQL CORE_RELEASE=4 global:
- DB=PGSQL CORE_RELEASE=4 - COMPOSER_ROOT_VERSION="4.0.x-dev"
matrix:
include:
- php: 5.6
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 7.0
env: DB=PGSQL PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
before_script: before_script:
- composer self-update || true - phpenv rehash
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support - phpenv config-rm xdebug.ini
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
- cd ~/builds/ss - composer validate
- composer install --prefer-dist --no-progress --no-suggest - composer require silverstripe/recipe-core 1.0.x-dev --no-update
- composer require silverstripe/versioned:1.x-dev --prefer-dist --no-progress --no-suggest - if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi
- composer dump-autoload --optimize - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script: script:
- vendor/bin/phpunit gridfieldextensions/tests - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs --standard=vendor/silverstripe/framework/phpcs.xml.dist src/ tests/ ; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi

View File

@ -20,10 +20,11 @@ This module provides a number of useful grid field components:
custom grid field detail views including tabs, breadcrumbs and other CMS custom grid field detail views including tabs, breadcrumbs and other CMS
features. features.
* `GridFieldTitleHeader` - a simple header which displays column titles. * `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. 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). For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/2).
See [docs/en/index.md](docs/en/index.md) for documentation and examples. See [docs/en/index.md](docs/en/index.md) for documentation and examples.

1
codecov.yml Normal file
View File

@ -0,0 +1 @@
comment: false

View File

@ -1,7 +1,7 @@
{ {
"name": "symbiote/silverstripe-gridfieldextensions", "name": "symbiote/silverstripe-gridfieldextensions",
"description": "A collection of useful grid field components", "description": "A collection of useful grid field components",
"type": "silverstripe-module", "type": "silverstripe-vendormodule",
"homepage": "http://github.com/symbiote/silverstripe-gridfieldextensions", "homepage": "http://github.com/symbiote/silverstripe-gridfieldextensions",
"keywords": ["silverstripe", "gridfield"], "keywords": ["silverstripe", "gridfield"],
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
@ -19,8 +19,14 @@
"issues": "http://github.com/symbiote/silverstripe-gridfieldextensions/issues" "issues": "http://github.com/symbiote/silverstripe-gridfieldextensions/issues"
}, },
"require": { "require": {
"silverstripe/vendor-plugin": "^1.0",
"silverstripe/framework": "~4.0" "silverstripe/framework": "~4.0"
}, },
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.0",
"silverstripe/versioned": "^1@dev"
},
"extra": { "extra": {
"installer-name": "gridfieldextensions", "installer-name": "gridfieldextensions",
"branch-alias": { "branch-alias": {
@ -29,6 +35,10 @@
"screenshots": [ "screenshots": [
"docs/en/_images/editable-rows.png", "docs/en/_images/editable-rows.png",
"docs/en/_images/add-existing-search.png" "docs/en/_images/add-existing-search.png"
],
"expose": [
"css",
"javascript"
] ]
}, },
"replace": { "replace": {
@ -37,7 +47,8 @@
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Symbiote\\GridFieldExtensions\\": "src/" "Symbiote\\GridFieldExtensions\\": "src/",
"Symbiote\\GridFieldExtensions\\Tests\\": "tests/"
} }
}, },
"prefer-stable": true, "prefer-stable": true,

View File

@ -73,8 +73,7 @@
*/ */
.ss-gridfield-add-new-multi-class { .ss-gridfield-add-new-multi-class {
margin-bottom: 8px !important; margin-bottom: 8px !important;white-space: nowrap;
white-space: nowrap;
} }
.ss-gridfield-add-new-multi-class .field { .ss-gridfield-add-new-multi-class .field {
@ -166,12 +165,13 @@
} }
.ss-gridfield-orderable .col-reorder .handle { .ss-gridfield-orderable .col-reorder .handle {
cursor: move;
padding: 16px 0 11px; cursor: move;padding: 16px 0 11px;
} }
.ss-gridfield-orderable .col-reorder .handle .icon { .ss-gridfield-orderable .col-reorder .handle .icon {
line-height: 100%; line-
height: 100%;
font-size: 1.5em; font-size: 1.5em;
} }
@ -199,3 +199,25 @@
background-position: -40px 9px !important; background-position: -40px 9px !important;
margin-right: 0; 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;
}

View File

@ -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). **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.

View File

@ -250,7 +250,7 @@
* GridFieldEditableColumns * GridFieldEditableColumns
*/ */
$('.ss-gridfield.ss-gridfield-editable .ss-gridfield-item td').entwine({ $('.grid-field .ss-gridfield-item').entwine({
onclick: function(e) { onclick: function(e) {
// Prevent the default row click action when clicking a cell that contains a field // Prevent the default row click action when clicking a cell that contains a field
if (this.find('.editable-column-field').length) { if (this.find('.editable-column-field').length) {
@ -387,5 +387,14 @@
if(this.hasClass("ui-droppable")) this.droppable("destroy"); 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); })(jQuery);

14
phpunit.xml.dist Normal file
View File

@ -0,0 +1,14 @@
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -144,8 +144,10 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
sprintf('[%s][{%%=o.num%%}]', self::POST_KEY), sprintf('[%s][{%%=o.num%%}]', self::POST_KEY),
$content $content
); );
}
// Cast content as HTML // Cast content
if (! $content instanceof DBField) {
$content = DBField::create_field('HTMLFragment', $content); $content = DBField::create_field('HTMLFragment', $content);
} }
@ -157,7 +159,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
$columns->push(new ArrayData(array( $columns->push(new ArrayData(array(
'Content' => $content, 'Content' => $content,
'Attributes' => $attrs, 'Attributes' => DBField::create_field('HTMLFragment', $attrs),
'IsActions' => $column == 'Actions' 'IsActions' => $column == 'Actions'
))); )));
} }
@ -179,7 +181,6 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
$editable = $grid->getConfig()->getComponentByType(GridFieldEditableColumns::class); $editable = $grid->getConfig()->getComponentByType(GridFieldEditableColumns::class);
/** @var GridFieldOrderableRows $sortable */ /** @var GridFieldOrderableRows $sortable */
$sortable = $grid->getConfig()->getComponentByType(GridFieldOrderableRows::class); $sortable = $grid->getConfig()->getComponentByType(GridFieldOrderableRows::class);
$form = $editable->getForm($grid, $record);
if (!singleton($class)->canCreate()) { if (!singleton($class)->canCreate()) {
return; return;
@ -190,6 +191,7 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
$item = $class::create(); $item = $class::create();
$extra = array(); $extra = array();
$form = $editable->getForm($grid, $record);
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item); $form->saveInto($item);

View File

@ -0,0 +1,453 @@
<?php
namespace Symbiote\GridFieldExtensions;
use Exception;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldPaginator;
use SilverStripe\Forms\GridField\GridField_FormAction;
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
{
use Configurable;
/**
* Specifies default page sizes
*
* @config
* @var int
*/
private static $default_page_sizes = array(15, 30, 60);
/**
* @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 ?: $this->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' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-left ss-gridfield-firstpage',
'disable-previous' => ($this->getCurrentPage() == 1)
),
'prev' => array(
'title' => 'Previous',
'args' => array('first-shown' => $arguments['first-shown'] - $this->getItemsPerPage()),
'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-left ss-gridfield-previouspage',
'disable-previous' => ($this->getCurrentPage() == 1)
),
'next' => array(
'title' => 'Next',
'args' => array('first-shown' => $arguments['first-shown'] + $this->getItemsPerPage()),
'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-right 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' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-right 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(
__CLASS__,
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;
}
}

View File

@ -124,8 +124,6 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
/** @var GridFieldOrderableRows $sortable */ /** @var GridFieldOrderableRows $sortable */
$sortable = $grid->getConfig()->getComponentByType(GridFieldOrderableRows::class); $sortable = $grid->getConfig()->getComponentByType(GridFieldOrderableRows::class);
$form = $this->getForm($grid, $record);
foreach ($value[self::POST_KEY] as $id => $fields) { foreach ($value[self::POST_KEY] as $id => $fields) {
if (!is_numeric($id) || !is_array($fields)) { if (!is_numeric($id) || !is_array($fields)) {
continue; continue;
@ -139,6 +137,7 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
$extra = array(); $extra = array();
$form = $this->getForm($grid, $record);
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING); $form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item); $form->saveInto($item);

View File

@ -9,16 +9,9 @@ use SilverStripe\View\Requirements;
*/ */
class GridFieldExtensions class GridFieldExtensions
{ {
public static function include_requirements() public static function include_requirements()
{ {
$moduleDir = self::get_module_dir(); Requirements::css('symbiote/silverstripe-gridfieldextensions:css/GridFieldExtensions.css');
Requirements::css($moduleDir.'/css/GridFieldExtensions.css'); Requirements::javascript('symbiote/silverstripe-gridfieldextensions:javascript/GridFieldExtensions.js');
Requirements::javascript($moduleDir.'/javascript/GridFieldExtensions.js');
}
public static function get_module_dir()
{
return basename(dirname(__DIR__));
} }
} }

View 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}&ndash;{$LastShownRecord}
<%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
$NumRecords
</span>
</td>
</tr>

View File

@ -1,9 +1,14 @@
<?php <?php
namespace Symbiote\GridFieldExtensions\Tests;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Dev\TestOnly; use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField;
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass; use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
use Symbiote\GridFieldExtensions\Tests\Stub\StubA;
use Symbiote\GridFieldExtensions\Tests\Stub\StubB;
use Symbiote\GridFieldExtensions\Tests\Stub\StubC;
/** /**
* Tests for {@link GridFieldAddNewMultiClass}. * Tests for {@link GridFieldAddNewMultiClass}.
@ -14,59 +19,32 @@ class GridFieldAddNewMultiClassTest extends SapphireTest
public function testGetClasses() public function testGetClasses()
{ {
$grid = new GridField('TestGridField'); $grid = new GridField('TestGridField');
$grid->setModelClass('GridFieldAddNewMultiClassTest_A'); $grid->setModelClass(StubA::class);
$component = new GridFieldAddNewMultiClass(); $component = new GridFieldAddNewMultiClass();
$this->assertEquals( $this->assertEquals(
array( array(
'GridFieldAddNewMultiClassTest_A' => 'A', 'Symbiote-GridFieldExtensions-Tests-Stub-StubA' => 'A',
'GridFieldAddNewMultiClassTest_B' => 'B', 'Symbiote-GridFieldExtensions-Tests-Stub-StubB' => 'B',
'GridFieldAddNewMultiClassTest_C' => 'C' 'Symbiote-GridFieldExtensions-Tests-Stub-StubC' => 'C'
), ),
$component->getClasses($grid), $component->getClasses($grid),
'Subclasses are populated by default and sorted' 'Subclasses are populated by default and sorted'
); );
$component->setClasses(array( $component->setClasses(array(
'GridFieldAddNewMultiClassTest_B' => 'Custom Title', StubB::class => 'Custom Title',
'GridFieldAddNewMultiClassTest_A' StubA::class
)); ));
$this->assertEquals( $this->assertEquals(
array( array(
'GridFieldAddNewMultiClassTest_B' => 'Custom Title', 'Symbiote-GridFieldExtensions-Tests-Stub-StubB' => 'Custom Title',
'GridFieldAddNewMultiClassTest_A' => 'A' 'Symbiote-GridFieldExtensions-Tests-Stub-StubA' => 'A'
), ),
$component->getClasses($grid), $component->getClasses($grid),
'Sorting and custom titles can be specified' 'Sorting and custom titles can be specified'
); );
} }
} }
/**#@+
* @ignore
*/
class GridFieldAddNewMultiClassTest_A implements TestOnly
{
public function i18n_singular_name()
{
$class = get_class($this);
return substr($class, strpos($class, '_') + 1);
}
public function canCreate()
{
return true;
}
}
class GridFieldAddNewMultiClassTest_B extends GridFieldAddNewMultiClassTest_A implements TestOnly
{
}
class GridFieldAddNewMultiClassTest_C extends GridFieldAddNewMultiClassTest_A implements TestOnly
{
}
/**#@-*/

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Symbiote\Test; namespace Symbiote\GridFieldExtensions\Tests;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
@ -12,55 +12,42 @@ use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldDetailForm; use SilverStripe\Forms\GridField\GridFieldDetailForm;
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass; use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClassHandler; use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClassHandler;
use Symbiote\GridFieldExtensions\Tests\Stub\NamespacedClass;
class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest { class GridFieldAddNewMultiClassWithNamespacesTest extends SapphireTest
{
public function testGetClassesWithNamespaces() { public function testGetClassesWithNamespaces()
{
$grid = new GridField('TestGridField'); $grid = new GridField('TestGridField');
$grid->setModelClass('Symbiote\\Test\\NamespacedClass'); $grid->setModelClass(NamespacedClass::class);
$component = new GridFieldAddNewMultiClass(); $component = new GridFieldAddNewMultiClass();
$this->assertEquals( $this->assertEquals(
array( array(
'Symbiote-Test-NamespacedClass' => 'NamespacedClass' 'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass' => 'NamespacedClass'
), ),
$component->getClasses($grid), $component->getClasses($grid),
'Namespaced classes are sanitised' 'Namespaced classes are sanitised'
); );
} }
public function testHandleAddWithNamespaces() { public function testHandleAddWithNamespaces()
{
$grid = new GridField('TestGridField'); $grid = new GridField('TestGridField');
$grid->getConfig()->addComponent(new GridFieldDetailForm()); $grid->getConfig()->addComponent(new GridFieldDetailForm());
$grid->setModelClass('Symbiote\\Test\\NamespacedClass'); $grid->setModelClass(NamespacedClass::class);
$grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create())); $grid->setForm(Form::create(Controller::create(), 'test', FieldList::create(), FieldList::create()));
$request = new HTTPRequest('POST', 'test'); $request = new HTTPRequest('POST', 'test');
$request->setRouteParams(array('ClassName' => 'Symbiote-Test-NamespacedClass')); $request->setRouteParams(array('ClassName' => 'Symbiote-GridFieldExtensions-Tests-Stub-NamespacedClass'));
$component = new GridFieldAddNewMultiClass(); $component = new GridFieldAddNewMultiClass();
$response = $component->handleAdd($grid, $request); $response = $component->handleAdd($grid, $request);
$record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record'); $record = new \ReflectionProperty(GridFieldAddNewMultiClassHandler::class, 'record');
$record->setAccessible(true); $record->setAccessible(true);
$this->assertInstanceOf('Symbiote\\Test\\NamespacedClass', $record->getValue($response)); $this->assertInstanceOf(NamespacedClass::class, $record->getValue($response));
}
}
/**#@+
* @ignore
*/
class NamespacedClass implements TestOnly {
public function i18n_singular_name() {
return 'NamespacedClass';
}
public function canCreate() {
return true;
} }
} }
/**#@-*/

View File

@ -0,0 +1,240 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests;
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->assertDOSEquals(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;
}
}

View File

@ -1,11 +1,15 @@
<?php <?php
namespace Symbiote\GridFieldExtensions\Tests;
use ReflectionMethod;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor; use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use Symbiote\GridFieldExtensions\GridFieldOrderableRows; use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
use SilverStripe\ORM\DataObject; use Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered;
use Symbiote\GridFieldExtensions\Tests\Stub\StubParent;
use Symbiote\GridFieldExtensions\Tests\Stub\StubSubclass;
/** /**
* Tests for the {@link GridFieldOrderableRows} component. * Tests for the {@link GridFieldOrderableRows} component.
@ -15,27 +19,21 @@ class GridFieldOrderableRowsTest extends SapphireTest
protected $usesDatabase = true; protected $usesDatabase = true;
// protected static $fixture_file = 'GridFieldOrderableRowsTest.yml'; protected static $fixture_file = 'GridFieldOrderableRowsTest.yml';
protected $extraDataObjects = array( protected static $extra_dataobjects = array(
'GridFieldOrderableRowsTest_Parent', StubParent::class,
'GridFieldOrderableRowsTest_Ordered', StubOrdered::class,
'GridFieldOrderableRowsTest_Subclass', StubSubclass::class,
); );
public function setUp()
{
parent::setUp();
$this->markTestSkipped('Upgrade to 4.0: Needs to be re-implemented.');
}
public function testReorderItems() public function testReorderItems()
{ {
$orderable = new GridFieldOrderableRows('ManyManySort'); $orderable = new GridFieldOrderableRows('ManyManySort');
$reflection = new ReflectionMethod($orderable, 'executeReorder'); $reflection = new ReflectionMethod($orderable, 'executeReorder');
$reflection->setAccessible(true); $reflection->setAccessible(true);
$parent = $this->objFromFixture('GridFieldOrderableRowsTest_Parent', 'parent'); $parent = $this->objFromFixture(StubParent::class, 'parent');
$config = new GridFieldConfig_RelationEditor(); $config = new GridFieldConfig_RelationEditor();
$config->addComponent($orderable); $config->addComponent($orderable);
@ -71,70 +69,27 @@ class GridFieldOrderableRowsTest extends SapphireTest
{ {
$orderable = new GridFieldOrderableRows(); $orderable = new GridFieldOrderableRows();
$parent = new GridFieldOrderableRowsTest_Parent(); $parent = new StubParent();
$parent->write(); $parent->write();
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'StubOrdered',
$orderable->getSortTable($parent->MyHasMany()) $orderable->getSortTable($parent->MyHasMany())
); );
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'StubOrdered',
$orderable->getSortTable($parent->MyHasManySubclass()) $orderable->getSortTable($parent->MyHasManySubclass())
); );
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Ordered', 'StubOrdered',
$orderable->getSortTable($parent->MyManyMany()) $orderable->getSortTable($parent->MyManyMany())
); );
$this->assertEquals( $this->assertEquals(
'GridFieldOrderableRowsTest_Parent_MyManyMany', 'StubParent_MyManyMany',
$orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany()) $orderable->setSortField('ManyManySort')->getSortTable($parent->MyManyMany())
); );
} }
} }
/**#@+
* @ignore
*/
class GridFieldOrderableRowsTest_Parent extends DataObject implements TestOnly
{
private static $has_many = array(
'MyHasMany' => 'GridFieldOrderableRowsTest_Ordered',
'MyHasManySubclass' => 'GridFieldOrderableRowsTest_Subclass'
);
private static $many_many = array(
'MyManyMany' => 'GridFieldOrderableRowsTest_Ordered'
);
private static $many_many_extraFields = array(
'MyManyMany' => array('ManyManySort' => 'Int')
);
}
class GridFieldOrderableRowsTest_Ordered extends DataObject implements TestOnly
{
private static $db = array(
'Sort' => 'Int'
);
private static $has_one = array(
'Parent' => 'GridFieldOrderableRowsTest_Parent'
);
private static $belongs_many_many =array(
'MyManyMany' => 'GridFieldOrderableRowsTest_Parent',
);
}
class GridFieldOrderableRowsTest_Subclass extends GridFieldOrderableRowsTest_Ordered implements TestOnly
{
}
/**#@-*/

View File

@ -1,22 +1,29 @@
GridFieldOrderableRowsTest_Ordered: Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered:
item1: item1:
Sort: 1
item2: item2:
Sort: 2
item3: item3:
Sort: 3
item4: item4:
Sort: 4
item5: item5:
Sort: 5
item6: item6:
GridFieldOrderableRowsTest_Parent: Sort: 6
Symbiote\GridFieldExtensions\Tests\Stub\StubParent:
parent: parent:
MyManyMany: MyManyMany:
- 0: =>GridFieldOrderableRowsTest_Ordered.item1 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item1:
ManyManySort: 1 ManyManySort: 1
- 1: =>GridFieldOrderableRowsTest_Ordered.item2 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item2:
ManyManySort: 1 ManyManySort: 1
- 2: =>GridFieldOrderableRowsTest_Ordered.item3 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item3:
ManyManySort: 2 ManyManySort: 2
- 3: =>GridFieldOrderableRowsTest_Ordered.item4 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item4:
ManyManySort: 2 ManyManySort: 2
- 4: =>GridFieldOrderableRowsTest_Ordered.item5 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item5:
ManyManySort: 108 ManyManySort: 108
- 5: =>GridFieldOrderableRowsTest_Ordered.item6 - =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item6:
ManyManySort: 108 ManyManySort: 108

View File

@ -0,0 +1,18 @@
<?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;
}
}

19
tests/Stub/StubA.php Normal file
View File

@ -0,0 +1,19 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
class StubA implements TestOnly
{
public function i18n_singular_name()
{
$class = get_class($this);
return substr($class, -1);
}
public function canCreate()
{
return true;
}
}

9
tests/Stub/StubB.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
class StubB extends StubA implements TestOnly
{
}

9
tests/Stub/StubC.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
class StubC extends StubA implements TestOnly
{
}

View File

@ -0,0 +1,23 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class StubOrdered extends DataObject implements TestOnly
{
private static $db = array(
'Sort' => 'Int'
);
private static $has_one = array(
'Parent' => StubParent::class
);
private static $belongs_many_many =array(
'MyManyMany' => StubParent::class,
);
private static $table_name = 'StubOrdered';
}

24
tests/Stub/StubParent.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class StubParent extends DataObject implements TestOnly
{
private static $has_many = array(
'MyHasMany' => StubOrdered::class,
'MyHasManySubclass' => StubSubclass::class
);
private static $many_many = array(
'MyManyMany' => StubOrdered::class
);
private static $many_many_extraFields = array(
'MyManyMany' => array('ManyManySort' => 'Int')
);
private static $table_name = 'StubParent';
}

View File

@ -0,0 +1,11 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class StubSubclass extends StubOrdered implements TestOnly
{
private static $table_name = 'StubSubclass';
}