From fce085fbf01dfe19a6fc93bf7add282f5861532f Mon Sep 17 00:00:00 2001 From: Mojmir Fendek Date: Fri, 5 Apr 2024 13:28:03 +1300 Subject: [PATCH 1/2] FIX: Source locale indicator correction. --- src/Extension/FluentExtension.php | 36 +++- src/Extension/FluentVersionedExtension.php | 14 +- src/Extension/Traits/FluentBadgeTrait.php | 14 +- src/Model/RecordLocale.php | 25 ++- tests/php/Extension/LocaleInheritanceTest.php | 189 ++++++++++++++++++ tests/php/Extension/LocaleInheritanceTest.yml | 13 ++ 6 files changed, 275 insertions(+), 16 deletions(-) create mode 100644 tests/php/Extension/LocaleInheritanceTest.php create mode 100644 tests/php/Extension/LocaleInheritanceTest.yml diff --git a/src/Extension/FluentExtension.php b/src/Extension/FluentExtension.php index ff4f6820..b0b7e4c8 100644 --- a/src/Extension/FluentExtension.php +++ b/src/Extension/FluentExtension.php @@ -24,7 +24,6 @@ use SilverStripe\ORM\Queries\SQLConditionGroup; use SilverStripe\ORM\Queries\SQLSelect; use SilverStripe\ORM\ValidationException; -use SilverStripe\Security\Permission; use SilverStripe\Versioned\Versioned; use SilverStripe\View\HTML; use TractorCow\Fluent\Extension\Traits\FluentObjectTrait; @@ -582,11 +581,11 @@ public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) // Apply substitutions $localisedPredicate = str_replace($conditionSearch, $conditionReplace, $predicate); - + if (empty($localisedPredicate)) { continue; } - + $where[$index] = [ $localisedPredicate => $parameters ]; @@ -965,19 +964,24 @@ protected function getRecordLocale() return Locale::getCurrentLocale(); } - /** * Returns the source locale that will display the content for this record * * @return Locale|null */ - public function getSourceLocale() + public function getSourceLocale(): ?Locale { - $sourceLocale = $this->owner->getField('SourceLocale'); - if ($sourceLocale) { - return Locale::getByLocale($sourceLocale); + $currentLocale = FluentState::singleton()->getLocale(); + + // We do not have any locales set up yet, so there is no source locale to find + if (!$currentLocale) { + return null; } - return Locale::getDefault(); + + $owner = $this->owner; + $localeInformation = $owner->LocaleInformation($currentLocale); + + return $localeInformation->getSourceLocale(); } /** @@ -1265,11 +1269,21 @@ public function updateLocalisationTabColumns(&$summaryColumns) $summaryColumns['Source'] = [ 'title' => 'Source', 'callback' => function (Locale $object) { - if (!$object->RecordLocale()) { + $localeInformation = $object->RecordLocale(); + + if (!$localeInformation) { return ''; } - $sourceLocale = $object->RecordLocale()->getSourceLocale(); + $sourceLocale = FluentState::singleton()->withState( + static function (FluentState $state) use ($localeInformation): ?Locale { + // We are currently in the CMS context, but we want to show to the content author + // what the data state is in the frontend context + $state->setIsFrontend(true); + + return $localeInformation->getSourceLocale(); + } + ); if ($sourceLocale) { return $sourceLocale->getLongTitle(); diff --git a/src/Extension/FluentVersionedExtension.php b/src/Extension/FluentVersionedExtension.php index 83ddc7df..718e26f8 100644 --- a/src/Extension/FluentVersionedExtension.php +++ b/src/Extension/FluentVersionedExtension.php @@ -616,11 +616,21 @@ public function updateLocalisationTabColumns(&$summaryColumns) $summaryColumns['Source'] = [ 'title' => 'Source', 'callback' => function (Locale $object) { - if (!$object->RecordLocale()) { + $localeInformation = $object->RecordLocale(); + + if (!$localeInformation) { return ''; } - $sourceLocale = $object->RecordLocale()->getSourceLocale(); + $sourceLocale = FluentState::singleton()->withState( + static function (FluentState $state) use ($localeInformation): ?Locale { + // We are currently in the CMS context, but we want to show to the content author + // what the data state is in the frontend context + $state->setIsFrontend(true); + + return $localeInformation->getSourceLocale(); + } + ); if ($sourceLocale) { return $sourceLocale->getLongTitle(); diff --git a/src/Extension/Traits/FluentBadgeTrait.php b/src/Extension/Traits/FluentBadgeTrait.php index f47d7802..0dd49c58 100644 --- a/src/Extension/Traits/FluentBadgeTrait.php +++ b/src/Extension/Traits/FluentBadgeTrait.php @@ -9,6 +9,7 @@ use TractorCow\Fluent\Extension\FluentFilteredExtension; use TractorCow\Fluent\Model\Locale; use TractorCow\Fluent\Model\RecordLocale; +use TractorCow\Fluent\State\FluentState; trait FluentBadgeTrait { @@ -68,6 +69,15 @@ protected function generateBadgeHTML( $extraProperties = [] ) { $info = RecordLocale::create($record, $locale); + $sourceLocale = FluentState::singleton()->withState( + static function (FluentState $state) use ($info): ?Locale { + // We are currently in the CMS context, but we want to show to the content author + // what the data state is in the frontend context + $state->setIsFrontend(true); + + return $info->getSourceLocale(); + } + ); // Build new badge $badgeClasses = ['badge', 'fluent-badge']; @@ -81,14 +91,14 @@ protected function generateBadgeHTML( 'locale' => $locale->getTitle() ] ); - } elseif ($info->getSourceLocale()) { + } elseif ($sourceLocale) { // If object is inheriting content from another locale show the source $badgeClasses[] = 'fluent-badge--localised'; $tooltip = _t( __TRAIT__ . '.BadgeInherited', 'Inherited from {locale}', [ - 'locale' => $info->getSourceLocale()->getTitle() + 'locale' => $sourceLocale->getTitle() ] ); } else { diff --git a/src/Model/RecordLocale.php b/src/Model/RecordLocale.php index 4374727c..318ff52b 100644 --- a/src/Model/RecordLocale.php +++ b/src/Model/RecordLocale.php @@ -276,7 +276,15 @@ public function IsPublished($inLocale = false): bool // If frontend publishing is not required for localisation, // we need to check if record is published in the source locale if (!$inLocale && $record->config()->get('frontend_publish_required') !== FluentExtension::INHERITANCE_MODE_EXACT) { - $sourceLocale = $this->getSourceLocale(); + $sourceLocale = FluentState::singleton()->withState( + function (FluentState $state): ?Locale { + // We are currently in the CMS context, but we want to show to the content author + // what the data state is in the frontend context + $state->setIsFrontend(true); + + return $this->getSourceLocale(); + } + ); if (!$sourceLocale) { // No source locale available @@ -367,16 +375,31 @@ public function getSourceLocale(): ?Locale { /** @var DataObject|FluentExtension $record */ $record = $this->getOriginalRecord(); + $config = $record->config(); + + $isFrontend = FluentState::singleton()->getIsFrontend(); + $inheritanceMode = $isFrontend + ? $config->get('frontend_publish_required') + : $config->get('cms_localisation_required'); + // This model has localised data in the current locale so the current locale is also the source locale if ($record->existsInLocale($this->getLocale())) { return $this->getLocaleObject(); } + // This model requires localisation so fallback of any kind is not allowed + // hence the content can't come from another locale + // We don't have a source locale for such case + if ($inheritanceMode === FluentExtension::INHERITANCE_MODE_EXACT) { + return null; + } + foreach ($this->getLocaleObject()->Fallbacks() as $fallback) { if (!$record->existsInLocale($fallback->Locale)) { continue; } + // We found a locale to fall back to, so this will be our source locale return $fallback; } diff --git a/tests/php/Extension/LocaleInheritanceTest.php b/tests/php/Extension/LocaleInheritanceTest.php new file mode 100644 index 00000000..a567e5c6 --- /dev/null +++ b/tests/php/Extension/LocaleInheritanceTest.php @@ -0,0 +1,189 @@ + [ + FluentSiteTreeExtension::class, + ], + ]; + + /** + * @param string $cmsInheritanceMode + * @param string $frontendInheritanceMode + * @param bool $frontendContext + * @param string $locale + * @param string|null $expected + * @return void + * @throws ValidationException + * @dataProvider sourceLocaleCasesProvider + */ + public function testGetSourceLocale( + string $cmsInheritanceMode, + string $frontendInheritanceMode, + bool $frontendContext, + string $locale, + ?string $expected + ): void { + Page::config() + ->set('cms_localisation_required', $cmsInheritanceMode) + ->set('frontend_publish_required', $frontendInheritanceMode); + + Versioned::withVersionedMode(function () use ($frontendContext, $locale, $expected): void { + // Make sure we have the correct stage set + Versioned::set_stage(Versioned::DRAFT); + + // Create the page in the default locale + FluentState::singleton()->withState( + function (FluentState $state) use ($frontendContext, $locale, $expected): void { + $state + ->setLocale('en_US') + ->setIsFrontend($frontendContext); + + /** @var Page|FluentExtension $page */ + $page = Page::create(); + $page->Title = 'Page title'; + $page->URLSegment = 'test-page'; + $page->write(); + + $localeInformation = $page->LocaleInformation($locale); + $sourceLocaleObject = $localeInformation->getSourceLocale(); + $sourceLocale = $sourceLocaleObject?->Locale; + $this->assertEquals( + $expected, + $sourceLocale, + 'We expect a specific source locale (locale information)' + ); + + if (!$sourceLocale) { + return; + } + + // Re-fetch the page in the target locale + $state->setLocale($locale); + + /** @var Page|FluentExtension $page */ + $page = Page::get()->byID($page->ID); + + $this->assertNotNull($page, 'We expect the page to be available in this locale'); + $sourceLocaleObject = $page->getSourceLocale(); + $this->assertEquals( + $expected, + $sourceLocaleObject->Locale, + 'We expect a specific source locale (page shorthand method)' + ); + } + ); + }); + } + + public function sourceLocaleCasesProvider(): array + { + return [ + 'default locale, cms with any mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'en_US', + 'en_US', + ], + 'default locale, cms with exact mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_EXACT, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'en_US', + 'en_US', + ], + 'default locale, cms with any mode, frontend with exact mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_EXACT, + true, + 'en_US', + 'en_US', + ], + 'fallback locale, cms with any mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'de_DE', + 'en_US', + ], + 'fallback locale, cms with exact mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_EXACT, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'de_DE', + 'en_US', + ], + 'fallback locale, cms with any mode, frontend with exact mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_EXACT, + true, + 'de_DE', + null, + ], + 'fallback locale, cms with any mode, frontend with fallback mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_FALLBACK, + true, + 'de_DE', + 'en_US', + ], + 'fallback locale, cms with any mode, frontend with exact mode, cms context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_EXACT, + false, + 'de_DE', + 'en_US', + ], + 'no fallback locale, cms with any mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'es_ES', + null, + ], + 'no fallback locale, cms with exact mode, frontend with any mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_EXACT, + FluentExtension::INHERITANCE_MODE_ANY, + true, + 'es_ES', + null, + ], + 'no fallback locale, cms with any mode, frontend with exact mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_EXACT, + true, + 'es_ES', + null, + ], + 'no fallback locale, cms with any mode, frontend with fallback mode, frontend context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_FALLBACK, + true, + 'es_ES', + null, + ], + 'no fallback locale, cms with any mode, frontend with exact mode, cms context' => [ + FluentExtension::INHERITANCE_MODE_ANY, + FluentExtension::INHERITANCE_MODE_EXACT, + false, + 'es_ES', + null, + ], + ]; + } +} diff --git a/tests/php/Extension/LocaleInheritanceTest.yml b/tests/php/Extension/LocaleInheritanceTest.yml new file mode 100644 index 00000000..4f9fca82 --- /dev/null +++ b/tests/php/Extension/LocaleInheritanceTest.yml @@ -0,0 +1,13 @@ +TractorCow\Fluent\Model\Locale: + default: + Title: US English + Locale: en_US + IsGlobalDefault: true + german: + Title: German + Locale: de_DE + Fallbacks: + - =>TractorCow\Fluent\Model\Locale.default + spanish: + Title: Spanish + Locale: es_ES From 6f35e2d9d7da22bdbe14755f3b3035c25377f453 Mon Sep 17 00:00:00 2001 From: Mojmir Fendek Date: Tue, 9 Apr 2024 13:09:14 +1200 Subject: [PATCH 2/2] PR fixes. --- src/Extension/FluentExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extension/FluentExtension.php b/src/Extension/FluentExtension.php index b0b7e4c8..1df2f824 100644 --- a/src/Extension/FluentExtension.php +++ b/src/Extension/FluentExtension.php @@ -969,7 +969,7 @@ protected function getRecordLocale() * * @return Locale|null */ - public function getSourceLocale(): ?Locale + public function getSourceLocale() { $currentLocale = FluentState::singleton()->getLocale();