Merge branch '3.2' into 3

This commit is contained in:
Serge Latyntcev 2019-11-25 16:02:34 +13:00
commit 0dc0c5614d
15 changed files with 221 additions and 67 deletions

View File

@ -1,18 +1,16 @@
language: php
env:
global:
- COMPOSER_ROOT_VERSION="3.1.x-dev"
matrix:
include:
- php: 5.6
env: DB=MYSQL RECIPE_VERSION=1.0.x-dev PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 7.0
env: DB=PGSQL RECIPE_VERSION=1.1.x-dev PHPUNIT_TEST=1
env: DB=MYSQL RECIPE_VERSION=1.1.x-dev PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL RECIPE_VERSION=4.2.x-dev PHPUNIT_COVERAGE_TEST=1
env: DB=PGSQL RECIPE_VERSION=4.2.x-dev PHPUNIT_COVERAGE_TEST=1
- php: 7.2
env: DB=MYSQL RECIPE_VERSION=4.3.x-dev PHPUNIT_TEST=1
- php: 7.3
env: DB=MYSQL RECIPE_VERSION=4.x-dev PHPUNIT_TEST=1
before_script:
@ -20,8 +18,8 @@ before_script:
- phpenv config-rm xdebug.ini
- composer validate
- composer require silverstripe/recipe-core "$RECIPE_VERSION" --no-update
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi
- composer require silverstripe/recipe-cms:"$RECIPE_VERSION" --no-update
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.1.x-dev --no-update; fi
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script:

View File

@ -1,6 +1,7 @@
# SilverStripe Grid Field Extensions Module
[![Build Status](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions.svg?branch=master)](https://travis-ci.org/symbiote/silverstripe-gridfieldextensions)
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
[![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)
@ -24,6 +25,11 @@ This module provides a number of useful grid field components:
This branch will aim for compatibility with SilverStripe 4.x.
## Installation
```bash
composer require symbiote/silverstripe-gridfieldextensions "^3"
```
For SilverStripe 3.x, please see the [compatible branch](https://github.com/symbiote/silverstripe-gridfieldextensions/tree/2).

1
code-of-conduct.md Normal file
View File

@ -0,0 +1 @@
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).

View File

@ -25,7 +25,7 @@
"require-dev": {
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.0",
"silverstripe/versioned": "^1@dev"
"silverstripe/versioned": "^1"
},
"extra": {
"screenshots": [
@ -35,10 +35,7 @@
"expose": [
"css",
"javascript"
],
"branch-alias": {
"3.x-dev": "3.3.x-dev"
}
]
},
"replace": {
"ajshort/silverstripe-gridfieldextensions": "self.version",

View File

@ -42,6 +42,11 @@
background: #DFD;
}
.grid-field__table .form-check-input.editable-column-field {
margin-top: .9rem;
position: relative;
}
/**
* GridFieldAddNewMultiClass
*/

View File

@ -208,7 +208,7 @@
}
});
$(".ss-gridfield-delete-inline").entwine({
$(".grid-field .action.ss-gridfield-delete-inline").entwine({
onclick: function() {
var msg = ss.i18n._t("GridFieldExtensions.CONFIRMDEL", "Are you sure you want to delete this?");
@ -325,6 +325,12 @@
self.find('.sortable-header th:last').html(content);
}
// update CMS preview
var preview = $('.cms-preview');
if (preview.length) {
preview.entwine('.ss.preview')._initialiseFromContent();
}
form.removeClass('loading');
if (successCallback) {
successCallback.apply(this, arguments);

View File

@ -290,33 +290,34 @@ class GridFieldConfigurablePaginator extends GridFieldPaginator
'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',
. 'ss-gridfield-pagination-action 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',
. 'ss-gridfield-pagination-action 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',
'extra-class' => 'btn btn-secondary btn--hide-text btn-sm font-icon-angle-right '
.'ss-gridfield-pagination-action 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',
. 'ss-gridfield-pagination-action 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'
'extra-class' => 'ss-gridfield-pagination-action ss-gridfield-pagesize-submit'
),
);

View File

@ -4,6 +4,8 @@ namespace Symbiote\GridFieldExtensions;
use Exception;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Forms\GridField\GridField;
@ -20,11 +22,11 @@ use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\ManyManyList;
use SilverStripe\ORM\ManyManyThroughList;
use SilverStripe\ORM\ManyManyThroughQueryManipulator;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\ViewableData;
@ -233,20 +235,16 @@ class GridFieldOrderableRows extends RequestHandler implements
public function getSortTable(SS_List $list)
{
$field = $this->getSortField();
if ($list instanceof ManyManyList) {
$extra = $list->getExtraFields();
$table = $list->getJoinTable();
if ($extra && array_key_exists($field, $extra)) {
return $table;
}
} elseif ($list instanceof ManyManyThroughList) {
return $this->getManyManyInspector($list)->getJoinAlias();
}
$classes = ClassInfo::dataClassesFor($list->dataClass());
foreach ($classes as $class) {
if (singleton($class)->hasDataBaseField($field)) {
return DataObject::getSchema()->tableName($class);
@ -299,7 +297,22 @@ class GridFieldOrderableRows extends RequestHandler implements
$record->ID,
$this->getSortField()
);
$sortField = new HiddenField($sortFieldName, false, $record->getField($this->getSortField()));
// Default: Get the sort field directly from the current record
$currentSortValue = $record->getField($this->getSortField());
$list = $grid->getList();
if ($list instanceof ManyManyThroughList) {
// In a many many through list we should get the current sort order from the relationship
// if it exists, not directly from the record
$throughListSorts = $this->getSortValuesFromManyManyThroughList($list, $this->getSortField());
if (array_key_exists($record->ID, $throughListSorts)) {
$currentSortValue = $throughListSorts[$record->ID];
}
}
$sortField = HiddenField::create($sortFieldName, false, $currentSortValue);
$sortField->addExtraClass('ss-orderable-hidden-sort');
$sortField->setForm($grid->getForm());
@ -349,17 +362,18 @@ class GridFieldOrderableRows extends RequestHandler implements
$sortterm .= '"'.$this->getSortTable($list).'"."'.$this->getSortField().'"';
}
return $list->sort($sortterm);
} else {
return $list;
}
return $list;
}
/**
* Handles requests to reorder a set of IDs in a specific order.
*
* @param GridField $grid
* @param SS_HTTPRequest $request
* @return SS_HTTPResponse
* @param HTTPRequest $request
* @return string
* @throws HTTPResponse_Exception
*/
public function handleReorder($grid, $request)
{
@ -540,16 +554,7 @@ class GridFieldOrderableRows extends RequestHandler implements
}
}
} elseif ($items instanceof ManyManyThroughList) {
$manipulator = $this->getManyManyInspector($list);
$joinClass = $manipulator->getJoinClass();
$fromRelationName = $manipulator->getForeignKey();
$toRelationName = $manipulator->getLocalKey();
$sortlist = DataList::create($joinClass)->filter([
$toRelationName => $items->column('ID'),
// first() is safe as there are earlier checks to ensure our list to sort is valid
$fromRelationName => $items->first()->getJoin()->$fromRelationName,
]);
$current = $sortlist->map($toRelationName, $sortField)->toArray();
$current = $this->getSortValuesFromManyManyThroughList($list, $sortField);
} else {
$current = $items->map('ID', $sortField)->toArray();
}
@ -583,25 +588,11 @@ class GridFieldOrderableRows extends RequestHandler implements
// Model has sort column and is versioned - handle as versioned
// Model has sort column and is NOT versioned - handle as NOT versioned
// Model doesn't have sort column because sort column is on ManyManyList - handle as NOT versioned
// Model doesn't have sort column because sort column is on ManyManyThroughList...
// - Related item is not versioned:
// - Through object is versioned: THROW an error.
// - Through object is NOT versioned: handle as NOT versioned
// - Related item is versioned...
// - Through object is versioned: handle as versioned
// - Through object is NOT versioned: THROW an error.
// Model doesn't have sort column because sort column is on ManyManyThroughList - inspect through object
if ($list instanceof ManyManyThroughList) {
$listClassVersioned = $class::create()->hasExtension(Versioned::class);
// We'll be updating the join class, not the relation class.
$class = $this->getManyManyInspector($list)->getJoinClass();
$isVersioned = $class::create()->hasExtension(Versioned::class);
// If one side of the relationship is versioned and the other is not, throw an error.
if ($listClassVersioned xor $isVersioned) {
throw new Exception(
'ManyManyThrough cannot mismatch Versioning between join class and related class'
);
}
} elseif (!$this->isManyMany($list)) {
$isVersioned = $class::create()->hasExtension(Versioned::class);
}
@ -656,7 +647,7 @@ class GridFieldOrderableRows extends RequestHandler implements
}
}
$this->extend('onAfterReorderItems', $list);
$this->extend('onAfterReorderItems', $list, $values, $sortedIDs);
}
protected function populateSortValues(DataList $list)
@ -763,4 +754,30 @@ class GridFieldOrderableRows extends RequestHandler implements
}
return $inspector;
}
/**
* Used to get sort orders from a many many through list relationship record, rather than the current
* record itself.
*
* @param ManyManyList|ManyManyThroughList $list
* @return int[] Sort orders for the
*/
protected function getSortValuesFromManyManyThroughList($list, $sortField)
{
$manipulator = $this->getManyManyInspector($list);
// Find the foreign key name, ID and class to look up
$joinClass = $manipulator->getJoinClass();
$fromRelationName = $manipulator->getForeignKey();
$toRelationName = $manipulator->getLocalKey();
// Create a list of the MMTL relations
$sortlist = DataList::create($joinClass)->filter([
$toRelationName => $list->column('ID'),
// first() is safe as there are earlier checks to ensure our list to sort is valid
$fromRelationName => $list->first()->getJoin()->$fromRelationName,
]);
return $sortlist->map($toRelationName, $sortField)->toArray();
}
}

View File

@ -2,7 +2,7 @@
<td class="grid-field__paginator bottom-all" colspan="$Colspan">
<span class="pagination-page-size">
<%t Symbiote\\GridFieldExtensions\\GridFieldConfigurablePaginator.SHOW 'Show' is 'Verb. Example: Show 1 of 2' %>
<select name="$PageSizesName" class="pagination-page-size-select" data-skip-autofocus="true">
<select name="$PageSizesName" class="pagination-page-size-select no-change-track" data-skip-autofocus="true">
<% loop $PageSizes %>
<option <% if $Selected %>selected="selected"<% end_if %>>$Size</option>
<% end_loop %>
@ -14,7 +14,7 @@
$FirstPage $PreviousPage
<span class="pagination-page-number">
<%t SilverStripe\\Forms\\GridField\\GridFieldPaginator.Page 'Page' %>
<input class="text" value="$CurrentPageNum" data-skip-autofocus="true" />
<input class="text no-change-track" value="$CurrentPageNum" data-skip-autofocus="true" />
<%t SilverStripe\\Forms\\GridField\\GridFieldPaginator.OF 'of' is 'Example: View 1 of 2' %>
$NumPages
</span>

View File

@ -9,8 +9,10 @@ use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrderedVersioned;
use Symbiote\GridFieldExtensions\Tests\Stub\StubParent;
use Symbiote\GridFieldExtensions\Tests\Stub\StubSubclass;
use Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned;
use Symbiote\GridFieldExtensions\Tests\Stub\StubUnorderable;
use Symbiote\GridFieldExtensions\Tests\Stub\ThroughDefiner;
use Symbiote\GridFieldExtensions\Tests\Stub\ThroughIntermediary;
@ -21,9 +23,6 @@ use Symbiote\GridFieldExtensions\Tests\Stub\ThroughBelongs;
*/
class GridFieldOrderableRowsTest extends SapphireTest
{
/**
* @var string
*/
protected static $fixture_file = [
'GridFieldOrderableRowsTest.yml',
'OrderableRowsThroughTest.yml'
@ -89,6 +88,40 @@ class GridFieldOrderableRowsTest extends SapphireTest
$this->assertEquals($desiredOrder, $newOrder);
}
public function testManyManyThroughListSortOrdersAreUsedForInitialRender()
{
/** @var ThroughDefiner $record */
$record = $this->objFromFixture(ThroughDefiner::class, 'DefinerOne');
$orderable = new GridFieldOrderableRows('Sort');
$config = new GridFieldConfig_RelationEditor();
$config->addComponent($orderable);
$grid = new GridField(
'Belongings',
'Testing Many Many',
$record->Belongings()->sort('Sort'),
$config
);
// Get the first record, which would be the first one to have column contents generated
/** @var ThroughIntermediary $expected */
$intermediary = $this->objFromFixture(ThroughIntermediary::class, 'One');
$result = $orderable->getColumnContent($grid, $record, 'irrelevant');
$this->assertContains(
'Belongings[GridFieldEditableColumns][' . $record->ID . '][Sort]',
$result,
'The field name is indexed under the record\'s ID'
);
$this->assertContains(
'value="' . $intermediary->Sort . '"',
$result,
'The value comes from the MMTL intermediary Sort value'
);
}
public function testSortableChildClass()
{
$orderable = new GridFieldOrderableRows('Sort');

View File

@ -21,7 +21,26 @@ Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered:
item6:
Sort: 6
nestedtest:
Children: =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item1,=>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item2,=>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item3,=>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item4
Children:
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item1
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item2
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item3
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild.item4
Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned:
item1:
ExtraField: 1
Sort: 1
item2:
ExtraField: 2
Sort: 2
item3:
ExtraField: 3
Sort: 3
item4:
ExtraField: 4
Sort: 4
Symbiote\GridFieldExtensions\Tests\Stub\StubParent:
parent:
MyManyMany:
@ -37,3 +56,9 @@ Symbiote\GridFieldExtensions\Tests\Stub\StubParent:
ManyManySort: 108
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered.item6:
ManyManySort: 108
parent-subclass-ordered-versioned:
MyHasManySubclassOrderedVersioned:
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned.item1
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned.item2
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned.item3
- =>Symbiote\GridFieldExtensions\Tests\Stub\StubSubclassOrderedVersioned.item4

View File

@ -12,7 +12,7 @@ use Symbiote\GridFieldExtensions\Tests\Stub\ThroughIntermediary;
use Symbiote\GridFieldExtensions\Tests\Stub\ThroughBelongs;
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
class OrderableRowsThroughTest extends SapphireTest
class OrderableRowsThroughVersionedTest extends SapphireTest
{
protected static $fixture_file = 'OrderableRowsThroughTest.yml';
@ -105,7 +105,7 @@ class OrderableRowsThroughTest extends SapphireTest
}
}
$this->assertTrue($differenceFound, 'All records should have changes in draft');
// Verify live stage has NOT reordered
Versioned::set_stage(Versioned::LIVE);
$sameOrder = $parent->$relationName()->sort($sortName)->column('ID');

View File

@ -0,0 +1,33 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
/**
* Class StubOrderedVersioned
* @package Symbiote\GridFieldExtensions\Tests\Stub
*/
class StubOrderedVersioned extends DataObject implements TestOnly
{
/**
* @var string
*/
private static $table_name = 'StubOrderedVersioned';
/**
* @var array
*/
private static $extensions = [
Versioned::class,
];
/**
* @var array
*/
private static $db = [
'Sort' => 'Int',
];
}

View File

@ -0,0 +1,33 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
/**
* Class StubOrderedVersioned
* @package Symbiote\GridFieldExtensions\Tests\Stub
*/
class StubSubclassOrderedVersioned extends StubOrderedVersioned
{
/**
* @var string
*/
private static $table_name = 'StubSubclassOrderedVersioned';
/**
* @var array
*/
private static $db = [
'ExtraField' => 'Int',
];
/**
* @var array
*/
private static $has_one = [
'Parent' => StubParent::class,
];
}

View File

@ -2,18 +2,17 @@
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBInt;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class ThroughIntermediary extends DataObject implements TestOnly
{
private static $table_name = 'IntermediaryThrough';
private static $db = [
'Sort' => DBInt::class,
'Sort' => 'Int',
];
private static $has_one = [
'Defining' => ThroughDefiner::class,
'Belonging' => ThroughBelongs::class,