Skip to content

Commit

Permalink
BUG: Change link type in via a GridField edit form.
Browse files Browse the repository at this point in the history
  • Loading branch information
mfendeksilverstripe committed May 12, 2022
1 parent 1068b78 commit 2eb755e
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/Models/EmailLink.php
Expand Up @@ -3,6 +3,7 @@
namespace SilverStripe\LinkField\Models;

use SilverStripe\Forms\EmailField;
use SilverStripe\Forms\FieldList;

/**
* A link to an Email address.
Expand All @@ -24,7 +25,7 @@ public function generateLinkDescription(array $data): string
return isset($data['Email']) ? $data['Email'] : '';
}

public function getCMSFields()
public function getCMSFields(): FieldList
{
$fields = parent::getCMSFields();

Expand Down
106 changes: 106 additions & 0 deletions src/Models/Link.php
Expand Up @@ -3,8 +3,14 @@
namespace SilverStripe\LinkField\Models;

use InvalidArgumentException;
use ReflectionException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CompositeValidator;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\RequiredFields;
use SilverStripe\LinkField\JsonData;
use SilverStripe\LinkField\Type\Registry;
use SilverStripe\LinkField\Type\Type;
Expand All @@ -28,6 +34,15 @@ class Link extends DataObject implements JsonData, Type
'OpenInNew' => 'Boolean'
];

/**
* In-memory only property used to change link type
* This case is relevant for CMS edit form which doesn't use React driven UI
* This is a workaround as changing the ClassName directly is not fully supported in the GridField admin
*
* @var string
*/
private $linkType;


public function defineLinkTypeRequirements()
{
Expand All @@ -51,11 +66,80 @@ public function LinkTypeTile(): string
return $this->i18n_singular_name();
}

/**
* @param array $data
* @return FieldList
* @throws ReflectionException
*/
public function scaffoldLinkFields(array $data): FieldList
{
return $this->getCMSFields();
}

/**
* @return FieldList
* @throws ReflectionException
*/
public function getCMSFields(): FieldList
{
$fields = parent::getCMSFields();
$linkTypes = $this->getLinkTypes();

if (static::class === self::class) {
// Add a link type selection field for generic links
$fields->addFieldsToTab(
'Root.Main',
[
$linkTypeField = DropdownField::create('LinkType', 'Link Type', $linkTypes),
],
'Title'
);

$linkTypeField->setEmptyString('-- select type --');
}

return $fields;
}

/**
* @return CompositeValidator
*/
public function getCMSCompositeValidator(): CompositeValidator
{
$validator = parent::getCMSCompositeValidator();

if (static::class === self::class) {
// Make Link type mandatory for generic links
$validator->addValidator(RequiredFields::create([
'LinkType',
]));
}

return $validator;
}

/**
* Form hook defined in @see Form::saveInto()
* We use this to work with an in-memory only field
*
* @param $value
*/
public function saveLinkType($value)
{
$this->linkType = $value;
}

public function onBeforeWrite(): void
{
// Detect link type change and update the class accordingly
if ($this->linkType && DataObject::singleton($this->linkType) instanceof Link) {
$this->setClassName($this->linkType);
$this->populateDefaults();
$this->forceChange();
}

parent::onBeforeWrite();
}

function setData($data): JsonData
{
Expand Down Expand Up @@ -142,4 +226,26 @@ public function forTemplate()
{
return $this->renderWith([self::class]);
}

/**
* Get all link types except the generic one
*
* @return array
* @throws ReflectionException
*/
private function getLinkTypes(): array
{
$classes = ClassInfo::subclassesFor(self::class);
$types = [];

foreach ($classes as $class) {
if ($class === self::class) {
continue;
}

$types[$class] = ClassInfo::shortName($class);
}

return $types;
}
}
3 changes: 2 additions & 1 deletion src/Models/SiteTreeLink.php
Expand Up @@ -4,6 +4,7 @@

use SilverStripe\CMS\Forms\AnchorSelectorField;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TreeDropdownField;

/**
Expand Down Expand Up @@ -38,7 +39,7 @@ public function generateLinkDescription(array $data): string
return $page ? $page->URLSegment : '';
}

public function getCMSFields()
public function getCMSFields(): FieldList
{
$fields = parent::getCMSFields();

Expand Down
33 changes: 33 additions & 0 deletions tests/php/Models/LinkTest.php
Expand Up @@ -2,10 +2,15 @@

namespace SilverStripe\LinkField\Tests\Models;

use ReflectionException;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\LinkField\Models\EmailLink;
use SilverStripe\LinkField\Models\ExternalLink;
use SilverStripe\LinkField\Models\FileLink;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\LinkField\Models\SiteTreeLink;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ValidationException;

class LinkTest extends SapphireTest
Expand Down Expand Up @@ -47,4 +52,32 @@ public function testSiteTreeLinkTitleFallback(): void

$this->assertEquals($customTitle, $model->Title, 'We expect to get the custom title not page title');
}

/**
* @param string $class
* @param bool $expected
* @throws ReflectionException
* @dataProvider linkTypeProvider
*/
public function testLinkType(string $class, bool $expected): void
{
/** @var Link $model */
$model = DataObject::singleton($class);
$fields = $model->getCMSFields();
$linkTypeField = $fields->fieldByName('Root.Main.LinkType');
$expected
? $this->assertNotNull($linkTypeField, 'We expect to a find link type field')
: $this->assertNull($linkTypeField, 'We do not expect to a find link type field');
}

public function linkTypeProvider(): array
{
return [
[EmailLink::class, false],
[ExternalLink::class, false],
[FileLink::class, false],
[SiteTreeLink::class, false],
[Link::class, true],
];
}
}

0 comments on commit 2eb755e

Please sign in to comment.