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

NEW Globally disallow link types #219

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
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,39 @@ class ExternalLinkExtension extends Extension

## Controlling what type of links can be created in a LinkField

By default, all `Link` subclasses can be created by a LinkField. This includes any custom `Link` subclasses defined in your projects or via third party module.
Developers can control the link types allowed for individual `LinkField`. The `setAllowedTypes` method only allow link types that have been provided as parameters.
By default, all `Link` subclasses can be created by a `LinkField`. This includes any custom `Link` subclasses defined in your project or via a third party module.

If you wish to globally disable one of the default `Link` subclasses for all `LinkField` instances, then this can be done using the following YAML configuration with the FQCN (Fully-Qualified Class Name) of the relevant default `Link` subclass you wish to disable:

```yml
SilverStripe\LinkField\Models\SiteTreeLink:
allowed_by_default: false
```

You can also apply this configuration to any of your own custom `Link` subclasses:

```php
namespace App\Links;

use SilverStripe\LinkField\Models\Link;

class MyCustomLink extends Link
{
// ...
private static bool $allowed_by_default = false;
}
```

Developers can control the link types allowed for individual `LinkField`. The `setAllowedTypes()` method only allow link types that have been provided as parameters. This method will override the `allowed_by_default` configuration.

```php
$fields->addFieldsToTab(
'Root.Main',
[
LinkField::create('EmailLink')
->setAllowedTypes([EmailLink::class]),
MultiLinkField::create('PageLinkList')
->setAllowedTypes([ SiteTreeLink::class ]),
Link::create('EmailLink')
->setAllowedTypes([ EmailLink::class ]),
->setAllowedTypes([SiteTreeLink::class, EmailLink::class]),
],
);
```
Expand Down
5 changes: 4 additions & 1 deletion src/Form/AbstractLinkField.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use DNADesign\Elemental\Models\BaseElement;
use InvalidArgumentException;
use LogicException;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormField;
Expand Down Expand Up @@ -183,7 +184,9 @@ private function generateAllowedTypes(): array
$typeDefinitions = $this->getAllowedTypes() ?? [];

if (empty($typeDefinitions)) {
return LinkTypeService::create()->generateAllLinkTypes();
$allLinkTypes = LinkTypeService::create()->generateAllLinkTypes();
$fn = fn ($className) => Config::inst()->get($className, 'allowed_by_default');
return array_filter($allLinkTypes, $fn);
}

$result = array();
Expand Down
28 changes: 7 additions & 21 deletions src/Models/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ class Link extends DataObject
*/
private static int $menu_priority = 100;

/**
* Whether this link type is allowed by default
* If this is set to `false` then this type of Link can still be manually allowed
* on a per field basis with AbstractLinkField::setAllowedTypes();
*/
private static bool $allowed_by_default = true;

/**
* The css class for the icon to display for this link type
*/
Expand Down Expand Up @@ -373,27 +380,6 @@ private function canPerformAction(string $canMethod, $member, $context = [])
return parent::$canMethod($member, $context);
}

/**
* Get all link types except the generic one
*
* @throws ReflectionException
*/
private function getLinkTypes(): array
Copy link
Member Author

Choose a reason for hiding this comment

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

This is method is unused

{
$classes = ClassInfo::subclassesFor(self::class);
$types = [];

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

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

return $types;
}

public function getTitle(): string
{
// If we have link text, we can just bail out without any changes
Expand Down
76 changes: 76 additions & 0 deletions tests/php/Form/AbstractLinkFieldTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace SilverStripe\LinkField\Tests\Form;

use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\LinkField\Form\LinkField;
use SilverStripe\LinkField\Tests\Form\AbstractLinkFieldTest\TestBlock;
use SilverStripe\LinkField\Tests\Controllers\LinkFieldControllerTest\TestPhoneLink;
use SilverStripe\Forms\Form;
use ReflectionObject;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\LinkField\Models\Link;
use SilverStripe\LinkField\Models\PhoneLink;
use SilverStripe\LinkField\Models\EmailLink;

class AbstractLinkFieldTest extends SapphireTest
Copy link
Member Author

Choose a reason for hiding this comment

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

I renamed this file and associated fixture files from LinkFieldTest

{
protected static $fixture_file = 'AbstractLinkFieldTest.yml';

protected static $extra_dataobjects = [
TestBlock::class,
TestPhoneLink::class,
];

public function testElementalNamespaceRemoved(): void
{
$form = new Form();
$field = new LinkField('PageElements_1_MyLink');
$form->setFields(new FieldList([$field]));
$block = $this->objFromFixture(TestBlock::class, 'TestBlock01');
$form->loadDataFrom($block);
$reflector = new ReflectionObject($field);
$method = $reflector->getMethod('getOwnerFields');
$method->setAccessible(true);
$res = $method->invoke($field);
$this->assertEquals([
'ID' => $block->ID,
'Class' => TestBlock::class,
'Relation' => 'MyLink',
], $res);
}

Copy link
Member Author

@emteknetnz emteknetnz Feb 13, 2024

Choose a reason for hiding this comment

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

Below this line is new test code, above this line is existing code

public function testAllowedLinks(): void
{
// Ensure only default link subclasses are included this test
foreach (ClassInfo::subclassesFor(Link::class) as $className) {
if (strpos($className, 'SilverStripe\\LinkField\\Models\\') !== 0) {
Config::modify()->set($className, 'allowed_by_default', false);
}
}
// Test default allowed types
$field = new LinkField('MyLink');
$keys = $this->getKeysForAllowedTypes($field);
$this->assertSame(['email', 'external', 'file', 'phone', 'sitetree'], $keys);
// Test can disallow globally
Config::modify()->set(PhoneLink::class, 'allowed_by_default', false);
$keys = $this->getKeysForAllowedTypes($field);
$this->assertSame(['email', 'external', 'file', 'sitetree'], $keys);
// Test can override with setAllowedTypes()
$field->setAllowedTypes([EmailLink::class, PhoneLink::class]);
$keys = $this->getKeysForAllowedTypes($field);
$this->assertSame(['email', 'phone'], $keys);
}

private function getKeysForAllowedTypes(LinkField $field): array
{
$rawJson = $field->getTypesProp();
$types = json_decode($rawJson, true);
$allowedTypes = array_filter($types, fn($type) => $type['allowed']);
$keys = array_column($allowedTypes, 'key');
sort($keys);
return $keys;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ SilverStripe\LinkField\Tests\Controllers\LinkFieldControllerTest\TestPhoneLink:
TestPhoneLink01:
Title: My phone link 01
Phone: 0123456790
SilverStripe\LinkField\Tests\Form\LinkFieldTest\TestBlock:
SilverStripe\LinkField\Tests\Form\AbstractLinkFieldTest\TestBlock:
TestBlock01:
MyLink: =>SilverStripe\LinkField\Tests\Controllers\LinkFieldControllerTest\TestPhoneLink.TestPhoneLink01
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?php

namespace SilverStripe\LinkField\Tests\Form\LinkFieldTest;
namespace SilverStripe\LinkField\Tests\Form\AbstractLinkFieldTest;

use SilverStripe\LinkField\Models\Link;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\Dev\TestOnly;

class TestBlock extends BaseElement implements TestOnly
{
private static $table_name = 'LinkField_TestBlock';
private static $table_name = 'AbstractLinkFieldTest_TestBlock';

private static $has_one = [
'MyLink' => Link::class,
Expand Down
39 changes: 0 additions & 39 deletions tests/php/Form/LinkFieldTest.php

This file was deleted.

Loading