Merge branch '3.2' into 3

This commit is contained in:
Steve Boyd 2021-03-20 14:14:24 +13:00
commit 74126ed6a7
15 changed files with 303 additions and 57 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
vendor/
resources/
composer.lock
assets/

View File

@ -1,43 +1,4 @@
language: php
version: ~> 1.0
dist: xenial
services:
- mysql
- postgresql
env:
global:
- COMPOSER_ROOT_VERSION="3.x-dev"
matrix:
include:
- php: 5.6
env: DB=MYSQL PHPUNIT_TEST=1 RECIPE_VERSION="^1 --prefer-lowest"
- php: 7.0
env: DB=MYSQL PHPUNIT_TEST=1 RECIPE_VERSION="^1"
- php: 7.1
env: DB=PGSQL PHPUNIT_TEST=1 RECIPE_VERSION="^4 --prefer-lowest"
- php: 7.2
env: DB=MYSQL PHPUNIT_TEST=1 PHPCS_TEST=1 RECIPE_VERSION="^4"
- php: 7.3
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 RECIPE_VERSION="4.x-dev"
- php: 7.4
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 RECIPE_VERSION="4.x-dev"
before_script:
- phpenv rehash
- phpenv config-rm xdebug.ini
- composer validate
- composer require silverstripe/recipe-cms:$RECIPE_VERSION --no-update
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:^2 --no-update; fi
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script:
- 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 src/ tests/ *.php; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
import:
- silverstripe/silverstripe-travis-shared:config/provision/standard-jobs-range.yml

View File

@ -23,7 +23,7 @@
"silverstripe/framework": "~4.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"sminnee/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.0",
"silverstripe/versioned": "^1"
},

View File

@ -2,6 +2,9 @@
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<rule ref="PSR2" >
<!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" />

View File

@ -13,6 +13,7 @@ use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\ManyManyList;
use SilverStripe\ORM\ManyManyThroughList;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
use Exception;
@ -189,9 +190,15 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
foreach ($value[self::POST_KEY] as $fields) {
/** @var DataObject $item */
$item = $class::create();
// Add the item before the form is loaded so that the join-object is available
if ($list instanceof ManyManyThroughList) {
$list->add($item);
}
$extra = array();
$form = $editable->getForm($grid, $record);
$form = $editable->getForm($grid, $item);
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item);
@ -205,8 +212,12 @@ class GridFieldAddNewInlineButton implements GridField_HTMLProvider, GridField_S
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
}
$item->write();
$list->add($item, $extra);
$item->write(false, false, false, true);
// Add non-through lists after the write. many_many_extraFields are added there too
if (!($list instanceof ManyManyThroughList)) {
$list->add($item, $extra);
}
}
}
}

View File

@ -56,10 +56,6 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
public function getColumnContent($grid, $record, $col)
{
if (!$record->canEdit()) {
return parent::getColumnContent($grid, $record, $col);
}
$fields = $this->getForm($grid, $record)->Fields();
if (!$this->displayFields) {
@ -82,8 +78,13 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
$field = clone $field;
} else {
$value = $grid->getDataFieldValue($record, $col);
$rel = (strpos($col, '.') === false); // field references a relation value
$field = ($rel) ? clone $fields->fieldByName($col) : new ReadonlyField($col);
$field = $fields->dataFieldByName($col);
// Fall back to previous logic
if (!$field) {
$rel = (strpos($col, '.') === false); // field references a relation value
$field = ($rel) ? clone $fields->fieldByName($col) : new ReadonlyField($col);
}
if (!$field) {
throw new Exception("Could not find the field '$col'");
@ -99,6 +100,10 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
$field->setName($this->getFieldName($field->getName(), $grid, $record));
$field->setValue($value);
if ($grid->isReadonly() || !$record->canEdit()) {
$field = $field->performReadonlyTransformation();
}
if ($field instanceof HtmlEditorField) {
return $field->FieldHolder();
}
@ -138,10 +143,11 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
$extra = array();
$form = $this->getForm($grid, $record);
$form = $this->getForm($grid, $item);
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
$form->saveInto($item);
// Check if we are also sorting these records
if ($sortable) {
$sortField = $sortable->getSortField();
@ -154,7 +160,7 @@ class GridFieldEditableColumns extends GridFieldDataColumns implements
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
}
$item->write();
$item->write(false, false, false, true);
$list->add($item, $extra);
}
}

View File

@ -715,7 +715,7 @@ class GridFieldOrderableRows extends RequestHandler implements
$introspector->getExtraFields() :
DataObjectSchema::create()->fieldSpecs($introspector->getJoinClass(), DataObjectSchema::DB_ONLY);
$key = $introspector->getLocalKey();
$foreignKey = $introspector->getForeignKey();
$foreignKey = $this->getManyManyInspectorForeignKey($introspector);
$foreignID = (int) $list->getForeignID();
if ($extra && array_key_exists($this->getSortField(), $extra)) {
@ -755,6 +755,24 @@ class GridFieldOrderableRows extends RequestHandler implements
return $inspector;
}
/**
* Depending on the list inspector and the list itself (ManyMany vs ManyManyThrough), the method to obtain
* the foreign key may be different.
*
* @param $inspector
* @return string
*/
private function getManyManyInspectorForeignKey($inspector)
{
if (($inspector instanceof ManyManyThroughQueryManipulator) && (method_exists($inspector, 'getForeignIDKey'))) {
// This method has been introduced in framework 4.1
return $inspector->getForeignIDKey();
}
return $inspector->getForeignKey();
}
/**
* Used to get sort orders from a many many through list relationship record, rather than the current
* record itself.
@ -768,7 +786,7 @@ class GridFieldOrderableRows extends RequestHandler implements
// Find the foreign key name, ID and class to look up
$joinClass = $manipulator->getJoinClass();
$fromRelationName = $manipulator->getForeignKey();
$fromRelationName = $this->getManyManyInspectorForeignKey($manipulator);
$toRelationName = $manipulator->getLocalKey();
// Create a list of the MMTL relations

View File

@ -0,0 +1,94 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests;
use Symbiote\GridFieldExtensions\Tests\Stub\TestController;
use Symbiote\GridFieldExtensions\Tests\Stub\StubUnorderable;
use Symbiote\GridFieldExtensions\GridFieldEditableColumns;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FieldList;
use SilverStripe\Dev\SapphireTest;
class GridFieldEditableColumnsTest extends SapphireTest
{
private function getMockGrid()
{
$controller = new TestController('Test');
$form = new Form($controller, 'TestForm', new FieldList(
$grid = new GridField('TestGridField')
), new FieldList());
$grid->setModelClass(StubUnorderable::class);
$grid->setList(StubUnorderable::get());
return $grid;
}
private function getMockRecord($id, $title)
{
$record = new StubUnorderable();
$record->ID = $id;
$record->Title = $title;
return $record;
}
public function testProvidesEditableFieldsInColumns()
{
$grid = $this->getMockGrid();
$component = new GridFieldEditableColumns();
$record = $this->getMockRecord(100, "foo");
$this->assertEquals(
[ 'Title' ],
$component->getColumnsHandled($grid)
);
$record->setCanEdit(true);
$column = $component->getColumnContent($grid, $record, 'Title');
$this->assertInstanceOf(DBHTMLText::class, $column);
$this->assertRegExp(
'/<input type="text" name="TestGridField\[GridFieldEditableColumns\]\[100\]\[Title\]" value="foo"[^>]*>/',
$column->getValue()
);
}
public function testProvidesReadonlyColumnsForNoneditableRecords()
{
$grid = $this->getMockGrid();
$component = new GridFieldEditableColumns();
$record = $this->getMockRecord(100, "testval");
$record->setCanEdit(false);
$column = $component->getColumnContent($grid, $record, 'Title');
$this->assertInstanceOf(DBHTMLText::class, $column);
$this->assertRegExp(
'/<span[^>]*>\s*testval\s*<\/span>/',
$column->getValue()
);
}
public function testProvidesReadonlyColumnsForReadonlyGrids()
{
$grid = $this->getMockGrid();
$component = new GridFieldEditableColumns();
$record = $this->getMockRecord(100, "testval");
$record->setCanEdit(true);
$grid = $grid->performReadonlyTransformation();
if (!$grid instanceof GridField) {
$this->markTestSkipped('silverstripe/framework <4.2.2 doesn\'t support readonly GridFields');
}
$column = $component->getColumnContent($grid, $record, 'Title');
$this->assertInstanceOf(DBHTMLText::class, $column);
$this->assertRegExp(
'/<span[^>]*>\s*testval\s*<\/span>/',
$column->getValue()
);
}
}

View File

@ -7,6 +7,9 @@ use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
use Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild;
use Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MMapper;
use Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrderableChild;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrdered;
use Symbiote\GridFieldExtensions\Tests\Stub\StubOrderedVersioned;
@ -25,10 +28,14 @@ class GridFieldOrderableRowsTest extends SapphireTest
{
protected static $fixture_file = [
'GridFieldOrderableRowsTest.yml',
'OrderableRowsThroughTest.yml'
'OrderableRowsThroughTest.yml',
// 'OrderablePolymorphicManyToMany.yml' // TODO: introduce this tests in the next minor release
];
protected static $extra_dataobjects = [
// PolymorphM2MChild::class,
// PolymorphM2MMapper::class,
// PolymorphM2MParent::class,
StubParent::class,
StubOrdered::class,
StubSubclass::class,
@ -46,6 +53,7 @@ class GridFieldOrderableRowsTest extends SapphireTest
return [
[StubParent::class . '.parent', 'MyManyMany', 'ManyManySort'],
[ThroughDefiner::class . '.DefinerOne', 'Belongings', 'Sort'],
// [PolymorphM2MParent::class . '.ParentOne', 'Children', 'Sort']
];
}
@ -122,6 +130,40 @@ class GridFieldOrderableRowsTest extends SapphireTest
);
}
public function testPolymorphicManyManyListSortOrdersAreUsedForInitialRender()
{
$this->markTestSkipped('TODO: Introduce this test in the next minor release (3.3)');
$record = $this->objFromFixture(PolymorphM2MParent::class, 'ParentOne');
$orderable = new GridFieldOrderableRows('Sort');
$config = new GridFieldConfig_RelationEditor();
$config->addComponent($orderable);
$grid = new GridField(
'Children',
'Testing Polymorphic Many Many',
$record->Children()->sort('Sort'),
$config
);
// Get the first record, which would be the first one to have column contents generated
$intermediary = $this->objFromFixture(PolymorphM2MMapper::class, 'MapP1ToC1');
$result = $orderable->getColumnContent($grid, $record, 'irrelevant');
$this->assertContains(
'Children[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

@ -0,0 +1,28 @@
Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent:
ParentOne:
ParentTwo:
Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild:
ChildOne:
ChildTwo:
Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MMapper:
MapP1ToC1:
Parent: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent.ParentOne'
Child: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild.ChildOne'
Sort: 1
MapP1ToC2:
Parent: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent.ParentOne'
Child: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild.ChildTwo'
Sort: 2
MapP2ToC1:
Parent: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent.ParentTwo'
Child: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild.ChildOne'
Sort: 2
MapP2ToC2:
Parent: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MParent.ParentTwo'
Child: '=>Symbiote\GridFieldExtensions\Tests\Stub\PolymorphM2MChild.ChildTwo'
Sort: 1

View File

@ -0,0 +1,15 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class PolymorphM2MChild extends DataObject implements TestOnly
{
private static $table_name = 'TestOnly_PolymorphM2MChild';
private static $has_many = [
'Parents' => PolymorphM2MMapper::class
];
}

View File

@ -0,0 +1,22 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class PolymorphM2MMapper extends DataObject implements TestOnly
{
private static $table_name = 'TestOnly_PolymorphM2MMapper';
private static $db = [
'Sort' => 'Int'
];
private static $has_one = [
'Parent' => DataObject::class, // PolymorphM2MParent
'Child' => PolymorphM2MChild::class,
];
private static $default_sort = '"Sort" ASC';
}

View File

@ -0,0 +1,19 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class PolymorphM2MParent extends DataObject implements TestOnly
{
private static $table_name = 'TableOnly_PolymorphM2MParent';
private static $many_many = [
'Children' => [
'through' => PolymorphM2MMapper::class,
'from' => 'Parent',
'to' => 'Child',
]
];
}

View File

@ -12,4 +12,16 @@ class StubUnorderable extends DataObject implements TestOnly
];
private static $table_name = 'StubUnorderable';
private $canEdit = false;
public function setCanEdit($canEdit)
{
$this->canEdit = $canEdit;
}
public function canEdit($member = null)
{
return $this->canEdit;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Symbiote\GridFieldExtensions\Tests\Stub;
use SilverStripe\Control\Controller;
class TestController extends Controller
{
private static $url_segment = 'test';
}