Skip to content

Commit

Permalink
Merge branch '5' into 6
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Aug 12, 2024
2 parents 370762a + 410ef74 commit 051dadd
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 23 deletions.
3 changes: 3 additions & 0 deletions src/Forms/FieldList.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ public function findTab(string $tabName): Tab|TabSet|null
$currentPointer = $this;

foreach ($parts as $k => $part) {
if ($currentPointer === null) {
return null;
}
$currentPointer = $currentPointer->fieldByName($part);
}
$this->checkIsTabOrTabSetOrNull($tabName, $currentPointer);
Expand Down
54 changes: 52 additions & 2 deletions src/Forms/FormScaffolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ class FormScaffolder
*/
public $tabbed = false;

/**
* Only set up the "Root.Main" tab, but skip scaffolding actual FormFields.
* If $tabbed is false, an empty FieldList will be returned.
*/
public bool $mainTabOnly = false;

/**
* @var boolean $ajaxSafe
* @deprecated 5.3.0 Will be removed without equivalent functionality.
*/
public $ajaxSafe = false;

Expand All @@ -39,17 +46,33 @@ class FormScaffolder
*/
public $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}.
* By default the scaffolder will determine the field instance by {@link DBField::scaffoldFormField()}.
*/
public $fieldClasses;

/**
* @var boolean $includeRelations Include has_one, has_many and many_many relations
* @var boolean $includeRelations Include has_many and many_many relations
*/
public $includeRelations = false;

/**
* Array of relation names to use as an allow list.
* If left blank, all has_many and many_many relations will be scaffolded unless explicitly ignored.
*/
public array $restrictRelations = [];

/**
* Numeric array of has_many and many_many relations to explicitly not scaffold.
*/
public array $ignoreRelations = [];

/**
* @param DataObject $obj
*/
Expand All @@ -76,12 +99,20 @@ public function getFieldList()
$mainTab->setTitle(_t(__CLASS__ . '.TABMAIN', 'Main'));
}

if ($this->mainTabOnly) {
return $fields;
}

// Add logical fields directly specified in db config
foreach ($this->obj->config()->get('db') as $fieldName => $fieldType) {
// Skip restricted fields
// Skip fields that aren't in the allow list
if ($this->restrictFields && !in_array($fieldName, $this->restrictFields ?? [])) {
continue;
}
// Skip ignored fields
if (in_array($fieldName, $this->ignoreFields)) {
continue;
}

if ($this->fieldClasses && isset($this->fieldClasses[$fieldName])) {
$fieldClass = $this->fieldClasses[$fieldName];
Expand Down Expand Up @@ -110,6 +141,9 @@ public function getFieldList()
if ($this->restrictFields && !in_array($relationship, $this->restrictFields ?? [])) {
continue;
}
if (in_array($relationship, $this->ignoreFields)) {
continue;
}
$fieldName = $component === 'SilverStripe\\ORM\\DataObject'
? $relationship // Polymorphic has_one field is composite, so don't refer to ID subfield
: "{$relationship}ID";
Expand Down Expand Up @@ -138,6 +172,12 @@ public function getFieldList()
&& ($this->includeRelations === true || isset($this->includeRelations['has_many']))
) {
foreach ($this->obj->hasMany() as $relationship => $component) {
if (!empty($this->restrictRelations) && !in_array($relationship, $this->restrictRelations)) {
continue;
}
if (in_array($relationship, $this->ignoreRelations)) {
continue;
}
$includeInOwnTab = true;
$fieldLabel = $this->obj->fieldLabel($relationship);
$fieldClass = (isset($this->fieldClasses[$relationship]))
Expand Down Expand Up @@ -177,6 +217,12 @@ public function getFieldList()
&& ($this->includeRelations === true || isset($this->includeRelations['many_many']))
) {
foreach ($this->obj->manyMany() as $relationship => $component) {
if (!empty($this->restrictRelations) && !in_array($relationship, $this->restrictRelations)) {
continue;
}
if (in_array($relationship, $this->ignoreRelations)) {
continue;
}
static::addManyManyRelationshipFields(
$fields,
$relationship,
Expand Down Expand Up @@ -252,8 +298,12 @@ protected function getParamsArray()
{
return [
'tabbed' => $this->tabbed,
'mainTabOnly' => $this->mainTabOnly,
'includeRelations' => $this->includeRelations,
'restrictRelations' => $this->restrictRelations,
'ignoreRelations' => $this->ignoreRelations,
'restrictFields' => $this->restrictFields,
'ignoreFields' => $this->ignoreFields,
'fieldClasses' => $this->fieldClasses,
'ajaxSafe' => $this->ajaxSafe
];
Expand Down
33 changes: 33 additions & 0 deletions src/Forms/TabSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,39 @@ public function insertAfter($insertAfter, $field, $appendIfMissing = true)
return parent::insertAfter($insertAfter, $field, $appendIfMissing);
}

/**
* Change the order of tabs which are direct children of this TabSet by specifying an ordered list of
* tab names.
*
* This works well in conjunction with SilverStripe's scaffolding functions: take the scaffold, and
* shuffle the tabs around to the order that you want.
*
* Tab names should exclude prefixes. For example if this TabSet is "Root", include "Main" not "Root.Main"
*/
public function changeTabOrder(array $tabNames): static
{
// Build a map of tabs indexed by their name. This will make the 2nd step much easier.
$existingTabs = [];
foreach ($this->children as $tab) {
$existingTabs[$tab->getName()] = $tab;
}

// Iterate through the ordered list of names, building a new array.
// While we're doing this, empty out $existingTabs so that we can keep track of leftovers.
// Unrecognised field names are okay; just ignore them.
$orderedTabs = [];
foreach ($tabNames as $tabName) {
if (isset($existingTabs[$tabName])) {
$orderedTabs[] = $existingTabs[$tabName];
unset($existingTabs[$tabName]);
}
}

// Add the leftover fields to the end of the ordered list.
$this->setTabs(FieldList::create([...$orderedTabs, ...$existingTabs]));
return $this;
}

/**
* Sets an additional default for $schemaData.
* The existing keys are immutable. HideNav is added in this overriding method to ensure it is not ignored by
Expand Down
29 changes: 23 additions & 6 deletions src/ORM/DataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,15 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/
private static $table_name = null;

/**
* Settings used by the FormScaffolder that scaffolds fields for getCMSFields()
*/
private static array $scaffold_cms_fields_settings = [
'includeRelations' => true,
'tabbed' => true,
'ajaxSafe' => true,
];

/**
* Non-static relationship cache, indexed by component name.
*
Expand Down Expand Up @@ -2474,8 +2483,12 @@ public function scaffoldFormFields($_params = null)
$params = array_merge(
[
'tabbed' => false,
'mainTabOnly' => false,
'includeRelations' => false,
'restrictRelations' => [],
'ignoreRelations' => [],
'restrictFields' => false,
'ignoreFields' => [],
'fieldClasses' => false,
'ajaxSafe' => false
],
Expand All @@ -2484,8 +2497,12 @@ public function scaffoldFormFields($_params = null)

$fs = FormScaffolder::create($this);
$fs->tabbed = $params['tabbed'];
$fs->mainTabOnly = $params['mainTabOnly'];
$fs->includeRelations = $params['includeRelations'];
$fs->restrictRelations = $params['restrictRelations'];
$fs->ignoreRelations = $params['ignoreRelations'];
$fs->restrictFields = $params['restrictFields'];
$fs->ignoreFields = $params['ignoreFields'];
$fs->fieldClasses = $params['fieldClasses'];
$fs->ajaxSafe = $params['ajaxSafe'];

Expand Down Expand Up @@ -2605,12 +2622,12 @@ protected function afterUpdateCMSFields(callable $callback)
*/
public function getCMSFields()
{
$tabbedFields = $this->scaffoldFormFields([
// Don't allow has_many/many_many relationship editing before the record is first saved
'includeRelations' => ($this->ID > 0),
'tabbed' => true,
'ajaxSafe' => true
]);
$scaffoldOptions = static::config()->get('scaffold_cms_fields_settings');
// Don't allow has_many/many_many relationship editing before the record is first saved
if (!$this->isInDB()) {
$scaffoldOptions['includeRelations'] = false;
}
$tabbedFields = $this->scaffoldFormFields($scaffoldOptions);

$this->extend('updateCMSFields', $tabbedFields);

Expand Down
3 changes: 3 additions & 0 deletions src/View/SSViewer_Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ public function resetLocalScope()
public function getObj($name, $arguments = [], $cache = false, $cacheName = null)
{
$on = $this->getItem();
if ($on === null) {
return null;
}
return $on->obj($name, $arguments, $cache, $cacheName);
}

Expand Down
1 change: 1 addition & 0 deletions tests/php/Forms/FieldListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ public function testFindOrMakeTab()
$message = "Root.Tab1.Field1 is an instance of 'SilverStripe\Forms\TextField', not Tab or TabSet";
$this->expectExceptionMessage($message);
$fields->findOrMakeTab('Root.Tab1.Field1');
$this->assertNull($fields->findTab('This.Doesnt.Exist'));
}

/**
Expand Down
Loading

0 comments on commit 051dadd

Please sign in to comment.