Skip to content

Commit

Permalink
Merge branch '4' into 5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Aug 9, 2023
2 parents 4ccfc37 + 4dd6b2f commit 15e4cbe
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 34 deletions.
9 changes: 8 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ Requires PHPUnit ^9
<testsuite name="Default">
<directory>tests/php</directory>
</testsuite>
<testsuite name="framework">
<!-- Framework ORM tests are split up to run in parallel -->
<testsuite name="framework-core">
<directory>tests/php</directory>
<exclude>
<directory>tests/php/ORM</directory>
</exclude>
</testsuite>
<testsuite name="framework-orm">
<directory>tests/php/ORM</directory>
</testsuite>
<testsuite name="cms">
<directory>vendor/silverstripe/cms/tests</directory>
Expand Down
26 changes: 26 additions & 0 deletions src/Forms/FieldsValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace SilverStripe\Forms;

/**
* Validates the internal state of all fields in the form.
*/
class FieldsValidator extends Validator
{
public function php($data): bool
{
$valid = true;
$fields = $this->form->Fields();

foreach ($fields as $field) {
$valid = ($field->validate($this) && $valid);
}

return $valid;
}

public function canBeCached(): bool
{
return true;
}
}
2 changes: 1 addition & 1 deletion src/Forms/GridField/GridFieldFilterHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public function canFilterAnyColumns($gridField)
sort($searchableFields);
sort($summaryFields);
// searchable_fields has been explictily defined i.e. searchableFields() is not falling back to summary_fields
if ($searchableFields !== $summaryFields) {
if (!empty($searchableFields) && ($searchableFields !== $summaryFields)) {
return true;
}
// we have fallen back to summary_fields, check they are filterable
Expand Down
10 changes: 10 additions & 0 deletions src/Forms/HTMLEditor/HTMLEditorField.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,14 @@ public function getSchemaStateDefaults()
$stateDefaults['data'] = $config->getConfigSchemaData();
return $stateDefaults;
}

/**
* Return value with all values encoded in html entities
*
* @return string Raw HTML
*/
public function ValueEntities()
{
return htmlentities($this->Value() ?? '', ENT_COMPAT, 'UTF-8', false);
}
}
66 changes: 51 additions & 15 deletions src/ORM/DataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use SilverStripe\Forms\FormField;
use SilverStripe\Forms\FormScaffolder;
use SilverStripe\Forms\CompositeValidator;
use SilverStripe\Forms\FieldsValidator;
use SilverStripe\Forms\HiddenField;
use SilverStripe\i18n\i18n;
use SilverStripe\i18n\i18nEntityProvider;
Expand Down Expand Up @@ -2543,7 +2544,7 @@ public function getCMSActions()
*/
public function getCMSCompositeValidator(): CompositeValidator
{
$compositeValidator = CompositeValidator::create();
$compositeValidator = CompositeValidator::create([FieldsValidator::create()]);

// Support for the old method during the deprecation period
if ($this->hasMethod('getCMSValidator')) {
Expand Down Expand Up @@ -3689,6 +3690,52 @@ public function onAfterBuild()
$this->extend('onAfterBuild');
}

private function getDatabaseBackedField(string $fieldPath): ?string
{
$component = $this;
$fieldParts = [];
$parts = explode('.', $fieldPath ?? '');

foreach ($parts as $nextPart) {
if (!$component) {
return null;
}
$fieldParts[] = $nextPart;

if ($component instanceof Relation || $component instanceof DataList) {
if ($component->hasMethod($nextPart)) {
// If the next part is a method, we don't have a database-backed field.
return null;
}
// The next part could either be a field, or another relation
$singleton = DataObject::singleton($component->dataClass());
if ($singleton->dbObject($nextPart) instanceof DBField) {
// If the next part is a DBField, we've found the database-backed field.
break;
}
$component = $component->relation($nextPart);
array_shift($parts);
} elseif ($component instanceof DataObject && ($component->dbObject($nextPart) instanceof DBField)) {
// If the next part is a DBField, we've found the database-backed field.
break;
} elseif ($component instanceof DataObject && $component->getRelationType($nextPart) !== null) {
// If it's a last part or only one elemnt of a relation, we don't have a database-backed field.
if (count($parts) === 1) {
return null;
}
$component = $component->$nextPart();
array_shift($parts);
} elseif (ClassInfo::hasMethod($component, $nextPart)) {
// If the next part is a method, we don't have a database-backed field.
return null;
} else {
return null;
}
}

return implode('.', $fieldParts) ?: null;
}

/**
* Get the default searchable fields for this object, as defined in the
* $searchable_fields list. If searchable fields are not defined on the
Expand All @@ -3707,21 +3754,10 @@ public function searchableFields()
$summaryFields = array_keys($this->summaryFields() ?? []);
$fields = [];

// remove the custom getters as the search should not include them
$schema = static::getSchema();
if ($summaryFields) {
foreach ($summaryFields as $key => $name) {
$spec = $name;

// Extract field name in case this is a method called on a field (e.g. "Date.Nice")
if (($fieldPos = strpos($name ?? '', '.')) !== false) {
$name = substr($name ?? '', 0, $fieldPos);
}

if ($schema->fieldSpec($this, $name)) {
$fields[] = $name;
} elseif ($this->relObject($spec)) {
$fields[] = $spec;
foreach ($summaryFields as $name) {
if ($field = $this->getDatabaseBackedField($name)) {
$fields[] = $field;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use SilverStripe\Control\Controller;
use SilverStripe\Control\Cookie;
use SilverStripe\ORM\DataObject;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Session;
Expand Down Expand Up @@ -62,7 +63,7 @@ public function authenticateRequest(HTTPRequest $request)
return null;
}
/** @var Member $member */
$member = Member::get()->byID($id);
$member = DataObject::get_by_id(Member::class, $id);
return $member;
}

Expand Down
36 changes: 24 additions & 12 deletions tests/behat/src/CmsUiContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Behat\Behat\Hook\Scope\AfterStepScope;
use Behat\Mink\Element\Element;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Exception\ElementNotFoundException;
use Behat\Mink\Selector\Xpath\Escaper;
use Behat\Mink\Session;
use PHPUnit\Framework\Assert;
Expand Down Expand Up @@ -92,26 +93,37 @@ public function iDismissAllToasts()
}

/**
* @Then /^I should see a "(.+)" (\w+) toast$/
* @Then /^I should (not |)see a "(.+)" (\w+) toast$/
*/
public function iShouldSeeAToast($notice, $type)
public function iShouldSeeAToast($not, $notice, $type)
{
$this->getMainContext()->assertElementContains('.toast--' . $type, $notice);
if ($not) {
try {
// If there is a toast of that type, ensure it doesn't contain the notice.
$this->getMainContext()->assertElementNotContains('.toast--' . $type, $notice);
} catch (ElementNotFoundException $e) {
// no-op - if the element doesn't exist at all, then that passes the test.
}
} else {
$this->getMainContext()->assertElementContains('.toast--' . $type, $notice);
}
}

/**
* @Then /^I should see a "(.+)" (\w+) toast with these actions: (.+)$/
* @Then /^I should (not |)see a "(.+)" (\w+) toast with these actions: (.+)$/
*/
public function iShouldSeeAToastWithAction($notice, $type, $actions)
public function iShouldSeeAToastWithAction($not, $notice, $type, $actions)
{
$this->iShouldSeeAToast($notice, $type);
$this->iShouldSeeAToast($not, $notice, $type);

$actions = explode(',', $actions ?? '');
foreach ($actions as $order => $action) {
$this->getMainContext()->assertElementContains(
sprintf('.toast--%s .toast__action:nth-child(%s)', $type, $order+1),
trim($action ?? '')
);
if (!$not) {
$actions = explode(',', $actions ?? '');
foreach ($actions as $order => $action) {
$this->getMainContext()->assertElementContains(
sprintf('.toast--%s .toast__action:nth-child(%s)', $type, $order+1),
trim($action ?? '')
);
}
}
}

Expand Down
79 changes: 79 additions & 0 deletions tests/php/Forms/FieldsValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace SilverStripe\Forms\Tests;

use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\EmailField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FieldsValidator;
use SilverStripe\Forms\Form;

class FieldsValidatorTest extends SapphireTest
{
protected $usesDatabase = false;

public function provideValidation()
{
return [
'missing values arent invalid' => [
'values' => [],
'isValid' => true,
],
'empty values arent invalid' => [
'values' => [
'EmailField1' => '',
'EmailField2' => null,
],
'isValid' => true,
],
'any invalid is invalid' => [
'values' => [
'EmailField1' => '[email protected]',
'EmailField2' => 'not email',
],
'isValid' => false,
],
'all invalid is invalid' => [
'values' => [
'EmailField1' => 'not email',
'EmailField2' => 'not email',
],
'isValid' => false,
],
'all valid is valid' => [
'values' => [
'EmailField1' => '[email protected]',
'EmailField2' => '[email protected]',
],
'isValid' => true,
],
];
}

/**
* @dataProvider provideValidation
*/
public function testValidation(array $values, bool $isValid)
{
$fieldList = new FieldList([
$field1 = new EmailField('EmailField1'),
$field2 = new EmailField('EmailField2'),
]);
if (array_key_exists('EmailField1', $values)) {
$field1->setValue($values['EmailField1']);
}
if (array_key_exists('EmailField2', $values)) {
$field2->setValue($values['EmailField2']);
}
$form = new Form(null, 'testForm', $fieldList, new FieldList([/* no actions */]), new FieldsValidator());

$result = $form->validationResult();
$this->assertSame($isValid, $result->isValid());
$messages = $result->getMessages();
if ($isValid) {
$this->assertEmpty($messages);
} else {
$this->assertNotEmpty($messages);
}
}
}
21 changes: 21 additions & 0 deletions tests/php/Forms/HTMLEditor/HTMLEditorFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,25 @@ public function testReadonlyField()
$readonlyContent->getValue()
);
}

public function testValueEntities()
{
$inputText = "The company &amp; partners";
$field = new HTMLEditorField("Content");
$field->setValue($inputText);

$this->assertEquals(
"The company &amp; partners",
$field->obj('ValueEntities')->forTemplate()
);

$inputText = "The company &amp;&amp; partners";
$field = new HTMLEditorField("Content");
$field->setValue($inputText);

$this->assertEquals(
"The company &amp;&amp; partners",
$field->obj('ValueEntities')->forTemplate()
);
}
}
Loading

0 comments on commit 15e4cbe

Please sign in to comment.