diff --git a/README.md b/README.md index d737a86f..7182a1da 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,29 @@ class Page extends SiteTree Note that you also need to add a `has_one` relation on the `Link` model to match your `has_many` here. See [official docs about `has_many`](https://docs.silverstripe.org/en/developer_guides/model/relations/#has-many) +## Default title for each link type + +By default, if the title for the link has not been set, then the default title will be used instead according to the type of link that is used. Default link is not stored in the database as link title. This value is used only when rendering page content. + +The developer also can set his own default title value using an extension by using `updateDefaultLinkTitle` method for each link type class. + +```php +owner->ExternalUrl); + } +} + +``` + ## Migrating from Shae Dawson's Linkable module https://github.com/sheadawson/silverstripe-linkable diff --git a/lang/en.yml b/lang/en.yml index 619f15c8..b860a7f1 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -32,3 +32,10 @@ en: UNAUTHORIZED: 'Unauthorized' UPDATE_LINK: 'Update link' VERSIONED_STATUS_MISMATCH: 'Linkable and LinkField do not have matching Versioned applications. Make sure that both are either un-Versioned or Versioned' + SilverStripe\LinkField\Models\Link: + LINK_FIELD_TITLE_DESCRIPTION: 'If left blank, an appropriate default title will be used on the front-end' + MISSING_DEFAULT_TITLE: 'No link provided' + SilverStripe\LinkField\Models\SiteTreeLink: + MISSING_DEFAULT_TITLE: 'Page missing' + SilverStripe\LinkField\Models\FileLink: + MISSING_DEFAULT_TITLE: 'File missing' diff --git a/src/Models/FileLink.php b/src/Models/FileLink.php index 15c7779e..483dd6ee 100644 --- a/src/Models/FileLink.php +++ b/src/Models/FileLink.php @@ -32,4 +32,17 @@ public function getURL(): string $file = $this->File(); return $file->exists() ? (string) $file->getURL() : ''; } + + public function getDefaultTitle(): string + { + $file = $this->File(); + if (!$file->exists()) { + return _t( + static::class . '.MISSING_DEFAULT_TITLE', + 'File missing', + ); + } + + return (string) $this->getDescription(); + } } diff --git a/src/Models/Link.php b/src/Models/Link.php index 78e9cb55..fd407973 100644 --- a/src/Models/Link.php +++ b/src/Models/Link.php @@ -5,6 +5,7 @@ use InvalidArgumentException; use ReflectionException; use SilverStripe\Core\ClassInfo; +use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\CompositeValidator; use SilverStripe\Forms\DropdownField; @@ -68,6 +69,10 @@ public function getCMSFields(): FieldList $titleField = $fields->dataFieldByName('Title'); $titleField->setTitle(_t('LinkField.LINK_FIELD_TITLE', 'Title')); + $titleField->setDescription(_t( + self::class . '.LINK_FIELD_TITLE_DESCRIPTION', + 'If left blank, an appropriate default title will be used on the front-end', + )); $openInNewField = $fields->dataFieldByName('OpenInNew'); $openInNewField->setTitle(_t('LinkField.OPEN_IN_NEW_TITLE', 'Open in new window?')); @@ -290,4 +295,27 @@ private function getLinkTypes(): array return $types; } + + public function getDisplayTitle(): string + { + // If we have a title, we can just bail out without any changes + if ($this->Title) { + return $this->Title; + } + + $defaultLinkTitle = $this->getDefaultTitle(); + + $this->extend('updateDefaultLinkTitle', $defaultLinkTitle); + + return $defaultLinkTitle; + } + + public function getDefaultTitle(): string + { + $default = $this->getDescription() ?: $this->getURL(); + if (!$default) { + $default = _t(static::class . '.MISSING_DEFAULT_TITLE', 'No link provided'); + } + return $default; + } } diff --git a/src/Models/SiteTreeLink.php b/src/Models/SiteTreeLink.php index 87c1f590..9dcf9a90 100644 --- a/src/Models/SiteTreeLink.php +++ b/src/Models/SiteTreeLink.php @@ -110,28 +110,18 @@ public function getURL(): string return Controller::join_links($url, $anchorSegment, $queryStringSegment); } - /** - * Try to populate link title from page title in case we don't have a title yet - * - * @return string|null - */ - public function getTitle(): ?string + public function getDefaultTitle(): string { - $title = $this->getField('Title'); - - if ($title) { - // If we already have a title, we can just bail out without any changes - return $title; - } - $page = $this->Page(); + $pageExist = $this->Page()->exists(); - if (!$page?->exists()) { - // We don't have a page to fall back to - return null; + if (!$pageExist) { + return _t( + static::class . '.MISSING_DEFAULT_TITLE', + 'Page missing', + ); } - // Use page title as a default value in case CMS user didn't provide the title return $page->Title; } } diff --git a/templates/SilverStripe/LinkField/Models/Link.ss b/templates/SilverStripe/LinkField/Models/Link.ss index 7b95b36b..ebfb3eed 100644 --- a/templates/SilverStripe/LinkField/Models/Link.ss +++ b/templates/SilverStripe/LinkField/Models/Link.ss @@ -1 +1 @@ -target="_blank" rel="noopener noreferrer"<% end_if %>>$Title +target="_blank" rel="noopener noreferrer"<% end_if %>>$DisplayTitle diff --git a/tests/php/Extensions/ExternalLinkExtension.php b/tests/php/Extensions/ExternalLinkExtension.php new file mode 100644 index 00000000..3b50f773 --- /dev/null +++ b/tests/php/Extensions/ExternalLinkExtension.php @@ -0,0 +1,14 @@ +owner->getURL()); + } +} diff --git a/tests/php/Models/LinkTest.php b/tests/php/Models/LinkTest.php index 37fd43d0..ad02ef3d 100644 --- a/tests/php/Models/LinkTest.php +++ b/tests/php/Models/LinkTest.php @@ -20,6 +20,7 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\ValidationException; use SilverStripe\Versioned\Versioned; +use SilverStripe\LinkField\Tests\Extensions\ExternalLinkExtension; class LinkTest extends SapphireTest { @@ -28,6 +29,12 @@ class LinkTest extends SapphireTest */ protected static $fixture_file = 'LinkTest.yml'; + protected static $required_extensions = [ + ExternalLink::class => [ + ExternalLinkExtension::class, + ], + ]; + protected function setUp(): void { parent::setUp(); @@ -78,8 +85,7 @@ public function testSiteTreeLinkTitleFallback(): void // The actual Database Title field should still be null $this->assertNull($model->getField('Title')); - // But when we fetch the field (ViewableData) it should return the value from getTitle() - $this->assertEquals($page->Title, $model->Title, 'We expect to get the linked Page title'); + $this->assertEquals(null, $model->Title, 'We expect that link does not have a title'); $customTitle = 'My custom title'; $model->Title = $customTitle; @@ -329,4 +335,76 @@ public function linkUrlCasesDataProvider(): array ], ]; } + + function linkDefaultTitleDataProvider(): array + { + return [ + 'page link' => [ + 'identifier' => 'page-link-1', + 'class' => SiteTreeLink::class, + 'expected' => 'PageLink1' + ], + 'email link' => [ + 'identifier' => 'email-link-with-email', + 'class' => EmailLink::class, + 'expected' => 'EmailLinkWithEmail' + ], + 'external link' => [ + 'identifier' => 'external-link-with-url', + 'class' => ExternalLink::class, + 'expected' => 'ExternalLinkWithUrl' + ], + 'phone link' => [ + 'identifier' => 'phone-link-with-phone', + 'class' => PhoneLink::class, + 'expected' => 'PhoneLinkWithPhone' + ], + 'file link' => [ + 'identifier' => 'file-link-no-image', + 'class' => FileLink::class, + 'expected' => 'File missing' + ], + 'page link with default title' => [ + 'identifier' => 'page-link-with-default-title', + 'class' => SiteTreeLink::class, + 'expected' => 'Page1' + ], + 'page link no page default title' => [ + 'identifier' => 'page-link-no-page-default-title', + 'class' => SiteTreeLink::class, + 'expected' => 'Page missing' + ], + 'email link with default title' => [ + 'identifier' => 'email-link-with-default-title', + 'class' => EmailLink::class, + 'expected' => 'maxime@silverstripe.com' + ], + 'external link with default title' => [ + 'identifier' => 'external-link-with-default-title', + 'class' => ExternalLink::class, + 'expected' => 'External Link: https://google.com' + ], + 'phone link with default title' => [ + 'identifier' => 'phone-link-with-default-title', + 'class' => PhoneLink::class, + 'expected' => '+64 4 978 7330' + ], + 'file link with default title' => [ + 'identifier' => 'file-link-with-default-title', + 'class' => FileLink::class, + 'expected' => '600x400.png' + ], + ]; + } + + /** + * @dataProvider linkDefaultTitleDataProvider + */ + public function testDefaultLinkTitle(string $identifier, string $class, string $expected): void + { + /** @var Link $link */ + $link = $this->objFromFixture($class, $identifier); + + $this->assertEquals($expected, $link->getDisplayTitle()); + } } diff --git a/tests/php/Models/LinkTest.yml b/tests/php/Models/LinkTest.yml index 7faac9d9..89229344 100644 --- a/tests/php/Models/LinkTest.yml +++ b/tests/php/Models/LinkTest.yml @@ -6,6 +6,7 @@ SilverStripe\CMS\Model\SiteTree: SilverStripe\Assets\Image: image-1: Title: 'Image1' + image-2: null SilverStripe\LinkField\Models\Link: link-1: @@ -36,6 +37,10 @@ SilverStripe\LinkField\Models\SiteTreeLink: QueryString: 'param1=value1¶m2=option2' Anchor: 'my-anchor' Page: =>SilverStripe\CMS\Model\SiteTree.page-1 + page-link-with-default-title: + Page: =>SilverStripe\CMS\Model\SiteTree.page-1 + page-link-no-page-default-title: + Page: null SilverStripe\LinkField\Models\EmailLink: email-link-with-email: @@ -43,6 +48,8 @@ SilverStripe\LinkField\Models\EmailLink: Email: 'maxime@silverstripe.com' email-link-no-email: Title: 'EmailLinkNoEmail' + email-link-with-default-title: + Email: 'maxime@silverstripe.com' SilverStripe\LinkField\Models\ExternalLink: external-link-with-url: @@ -50,6 +57,8 @@ SilverStripe\LinkField\Models\ExternalLink: ExternalUrl: 'https://google.com' external-link-no-url: Title: 'ExternalLinkNoUrl' + external-link-with-default-title: + ExternalUrl: 'https://google.com' SilverStripe\LinkField\Models\PhoneLink: phone-link-with-phone: @@ -57,10 +66,16 @@ SilverStripe\LinkField\Models\PhoneLink: Phone: '+64 4 978 7330' phone-link-no-phone: Title: 'PhoneLinkNoPhone' + phone-link-with-default-title: + Phone: '+64 4 978 7330' SilverStripe\LinkField\Models\FileLink: file-link-with-image: Title: 'FileLinkWithImage' File: =>SilverStripe\Assets\Image.image-1 file-link-no-image: - Title: 'FileLinkNoImage' + Title: null + File: =>SilverStripe\Assets\Image.image-2 + OpenInNew: true + file-link-with-default-title: + File: =>SilverStripe\Assets\Image.image-1