Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH Various changes to support SiteTree form field scaffolding #11327

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 15 additions & 22 deletions src/Forms/FormScaffolder.php
Copy link
Member Author

@GuySartorelli GuySartorelli Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added strict typing for the properties as a matter of course - it's unrelated refactoring.

Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ class FormScaffolder
use Injectable;

/**
* @var DataObject $obj The object defining the fields to be scaffolded
* The object defining the fields to be scaffolded
* through its metadata like $db, $searchable_fields, etc.
*/
protected $obj;
protected DataObject $obj;

/**
* @var boolean $tabbed Return fields in a tabset, with all main fields in the path "Root.Main",
* Return fields in a tabset, with all main fields in the path "Root.Main",
* relation fields in "Root.<relationname>" (if {@link $includeRelations} is enabled).
*/
public $tabbed = false;
public bool $tabbed = false;

/**
* Only set up the "Root.Main" tab, but skip scaffolding actual FormFields.
Expand All @@ -35,32 +35,26 @@ class FormScaffolder
public bool $mainTabOnly = false;

/**
* @var boolean $ajaxSafe
* @deprecated 5.3.0 Will be removed without equivalent functionality.
* Array of field names to use as an allow list.
* If left blank, all fields from {@link DataObject->db()} will be included unless explicitly ignored.
*/
public $ajaxSafe = false;

/**
* @var array $restrictFields Numeric array of a field name whitelist.
* If left blank, all fields from {@link DataObject->db()} will be included.
*/
public $restrictFields;
public array $restrictFields = [];

/**
* Numeric array of field names and has_one relations to explicitly not scaffold.
*/
public array $ignoreFields = [];

/**
* @var array $fieldClasses Optional mapping of fieldnames to subclasses of {@link FormField}.
* Optional mapping of fieldnames to subclasses of {@link FormField}.
* By default the scaffolder will determine the field instance by {@link DBField::scaffoldFormField()}.
*/
public $fieldClasses;
public array $fieldClasses = [];

/**
* @var boolean $includeRelations Include has_many and many_many relations
* Include has_many and many_many relations
*/
public $includeRelations = false;
public bool|array $includeRelations = false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code that uses this, you could use it to explicitly only allow has_many relations but not many_many relations (or vice versa) - in which case it needs to be an associative array. Hence bool|array.


/**
* Array of relation names to use as an allow list.
Expand Down Expand Up @@ -106,15 +100,15 @@ public function getFieldList()
// Add logical fields directly specified in db config
foreach ($this->obj->config()->get('db') as $fieldName => $fieldType) {
// Skip fields that aren't in the allow list
if ($this->restrictFields && !in_array($fieldName, $this->restrictFields ?? [])) {
if (!empty($this->restrictFields) && !in_array($fieldName, $this->restrictFields)) {
Comment on lines -109 to +103
Copy link
Member Author

@GuySartorelli GuySartorelli Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be an array, so we can tidy this a little now. Applies to similar changes below as well.

continue;
}
// Skip ignored fields
if (in_array($fieldName, $this->ignoreFields)) {
continue;
}

if ($this->fieldClasses && isset($this->fieldClasses[$fieldName])) {
if (isset($this->fieldClasses[$fieldName])) {
$fieldClass = $this->fieldClasses[$fieldName];
$fieldObject = new $fieldClass($fieldName);
} else {
Expand All @@ -138,7 +132,7 @@ public function getFieldList()
// add has_one relation fields
if ($this->obj->hasOne()) {
foreach ($this->obj->hasOne() as $relationship => $component) {
if ($this->restrictFields && !in_array($relationship, $this->restrictFields ?? [])) {
if (!empty($this->restrictFields) && !in_array($relationship, $this->restrictFields)) {
continue;
}
if (in_array($relationship, $this->ignoreFields)) {
Expand All @@ -147,7 +141,7 @@ public function getFieldList()
$fieldName = $component === 'SilverStripe\\ORM\\DataObject'
? $relationship // Polymorphic has_one field is composite, so don't refer to ID subfield
: "{$relationship}ID";
if ($this->fieldClasses && isset($this->fieldClasses[$fieldName])) {
if (isset($this->fieldClasses[$fieldName])) {
$fieldClass = $this->fieldClasses[$fieldName];
$hasOneField = new $fieldClass($fieldName);
} else {
Expand Down Expand Up @@ -305,7 +299,6 @@ protected function getParamsArray()
'restrictFields' => $this->restrictFields,
'ignoreFields' => $this->ignoreFields,
'fieldClasses' => $this->fieldClasses,
'ajaxSafe' => $this->ajaxSafe
];
}
}
11 changes: 10 additions & 1 deletion src/Forms/Tab.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ public function __construct($name, $titleOrField = null, $fields = null)
// Assign name and title (not assigned by parent constructor)
$this->setName($name);
$this->setTitle($title);
$this->setID(Convert::raw2htmlid($name));
}

public function ID()
Expand All @@ -99,6 +98,16 @@ public function setID($id)
return $this;
}

public function setName($name)
{
// Use raw properties instead of ID() and getName() here because we
// only want to check the actual raw values of those properties.
if (($this->id ?? '') === Convert::raw2htmlid($this->name)) {
$this->setID(Convert::raw2htmlid($name));
}
return parent::setName($name);
}

/**
* Get child fields
*
Expand Down
11 changes: 4 additions & 7 deletions src/ORM/DataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
private static array $scaffold_cms_fields_settings = [
'includeRelations' => true,
'tabbed' => true,
'ajaxSafe' => true,
];

/**
Expand Down Expand Up @@ -2396,8 +2395,8 @@ public function scaffoldSearchFields($_params = null)
{
$params = array_merge(
[
'fieldClasses' => false,
'restrictFields' => false
'fieldClasses' => [],
'restrictFields' => []
],
(array)$_params
);
Expand Down Expand Up @@ -2487,10 +2486,9 @@ public function scaffoldFormFields($_params = null)
'includeRelations' => false,
'restrictRelations' => [],
'ignoreRelations' => [],
'restrictFields' => false,
'restrictFields' => [],
'ignoreFields' => [],
'fieldClasses' => false,
'ajaxSafe' => false
'fieldClasses' => [],
],
(array)$_params
);
Expand All @@ -2504,7 +2502,6 @@ public function scaffoldFormFields($_params = null)
$fs->restrictFields = $params['restrictFields'];
$fs->ignoreFields = $params['ignoreFields'];
$fs->fieldClasses = $params['fieldClasses'];
$fs->ajaxSafe = $params['ajaxSafe'];

$this->extend('updateFormScaffolder', $fs, $this);

Expand Down
17 changes: 17 additions & 0 deletions src/Security/InheritedPermissionsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,21 @@ class InheritedPermissionsExtension extends DataExtension
'ViewerMembers',
'EditorMembers',
];

/**
* These fields will need to be added manually, since SiteTree wants it in the special settings tab
* and nothing else in code that uses these fields is scaffolded.
*/
private static array $scaffold_cms_fields_settings = [
'ignoreFields' => [
'CanViewType',
'CanEditType',
],
'ignoreRelations' => [
'ViewerGroups',
'EditorGroups',
'ViewerMembers',
'EditorMembers',
],
];
}
30 changes: 30 additions & 0 deletions tests/php/Forms/TabTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace SilverStripe\Forms\Tests;

use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\Tab;

class TabTest extends SapphireTest
{
protected $usesDatabase = false;

public function testNameAndID(): void
{
// ID is set on instantiation based on the name
$tab = new Tab('MyName _-()!@#$');
$this->assertSame('MyName _-()!@#$', $tab->getName());
$this->assertSame('MyName_-', $tab->ID());

// Changing the name changes the ID
$tab->setName('NewName');
$this->assertSame('NewName', $tab->getName());
$this->assertSame('NewName', $tab->ID());

// If ID is explicitly set, changing the name doesn't override it
$tab->setID('Custom-ID');
$tab->setName('AnotherName');
$this->assertSame('AnotherName', $tab->getName());
$this->assertSame('Custom-ID', $tab->ID());
}
Comment on lines +12 to +29
Copy link
Member Author

@GuySartorelli GuySartorelli Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't use a data provider for this - the scenarios need to be run sequentially.

}
Loading