FEATURE: Implemented Translatable::get_homepage_link_by_locale() to enable translated home pages to function.

FEATURE: Updated SiteTree::get_by_link() to integrate with translatable, and allow it to work across languages by implementing Translatable->alternateGetByLink().
API CHANGE: Moved lang_filter enabling & disabling into static methods on Translatable, and renamed to locale_filter.
BUGFIX: Fixed viewing a translatable page by URL without explicitly setting a Locale in ContentController->handleRequest().

From: Andrew Short <andrewjshort@gmail.com>

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@88503 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Andrew Short 2009-10-11 00:07:22 +00:00 committed by Sam Minnee
parent 80c8780b6f
commit ea906a50a8
5 changed files with 153 additions and 49 deletions

View File

@ -169,25 +169,29 @@ class ContentController extends Controller {
*/ */
public function handleRequest(HTTPRequest $request) { public function handleRequest(HTTPRequest $request) {
Director::set_current_page($this->data()); Director::set_current_page($this->data());
$response = parent::handleRequest($request); $response = parent::handleRequest($request);
// If the default handler returns an error, due to the action not existing, attempt to fall over to a child // If the default handler returns an error, due to the action not existing, attempt to fall over to a child
// SiteTree object, then use its corresponding ContentController to handle the request. This allows for the // SiteTree object, then use its corresponding ContentController to handle the request. This allows for the
// building of nested chains of controllers corresponding to a nested URL. // building of nested chains of controllers corresponding to a nested URL.
if(SiteTree::nested_urls() && $response instanceof HTTPResponse && $response->isError()) { if(SiteTree::nested_urls() && $response instanceof HTTPResponse && $response->isError()) {
$SQL_URLParam = Convert::raw2sql($request->param('Action')); Translatable::disable_locale_filter();
if($SQL_URLParam && $nextPage = DataObject::get_one('SiteTree', "\"ParentID\" = $this->ID AND \"URLSegment\" = '$SQL_URLParam'")) { $child = DataObject::get_one('SiteTree', sprintf (
if($nextPage->canView()) { "\"ParentID\" = %s AND \"URLSegment\" = '%s'",
$request->shiftAllParams(); $this->ID,
return ModelAsController::controller_for($nextPage)->handleRequest($request); Convert::raw2sql($request->param('Action'))
} ));
Translatable::enable_locale_filter();
if($child && $child->canView()) {
$request->shiftAllParams();
return ModelAsController::controller_for($child)->handleRequest($request);
} }
} }
Director::set_current_page(null); Director::set_current_page(null);
return $response; return $response;
} }

View File

@ -69,10 +69,14 @@ class ModelAsController extends Controller implements NestedController {
throw new Exception('ModelAsController->getNestedController(): was not passed a URLSegment value.'); throw new Exception('ModelAsController->getNestedController(): was not passed a URLSegment value.');
} }
Translatable::disable_locale_filter();
$sitetree = DataObject::get_one('SiteTree', sprintf ( $sitetree = DataObject::get_one('SiteTree', sprintf (
'"URLSegment" = \'%s\' %s', Convert::raw2sql($URLSegment), (SiteTree::nested_urls() ? 'AND "ParentID" = 0' : null) '"URLSegment" = \'%s\' %s', Convert::raw2sql($URLSegment), (SiteTree::nested_urls() ? 'AND "ParentID" = 0' : null)
)); ));
Translatable::enable_locale_filter();
if(!$sitetree) { if(!$sitetree) {
// If a root page has been renamed, redirect to the new location. // If a root page has been renamed, redirect to the new location.
if($redirect = $this->findOldPage($URLSegment)) { if($redirect = $this->findOldPage($URLSegment)) {

View File

@ -262,11 +262,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
// Attempt to grab an alternative page from decorators. // Attempt to grab an alternative page from decorators.
if(!$sitetree) { if(!$sitetree) {
if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $link, $filter, $cache, $order)) { $parentID = self::nested_urls() ? 0 : null;
foreach($alternatives as $alternative) if($alternative) return $alternative;
if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $URLSegment, $parentID)) {
foreach($alternatives as $alternative) if($alternative) $sitetree = $alternative;
} }
return false; if(!$sitetree) return false;
} }
// Check if we have any more URL parts to parse. // Check if we have any more URL parts to parse.
@ -279,11 +281,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
); );
if(!$next) { if(!$next) {
if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $link, $filter, $cache, $order)) { $parentID = (int) $sitetree->ID;
foreach($alternatives as $alternative) if($alternative) return $alternative;
if($alternatives = singleton('SiteTree')->extend('alternateGetByLink', $segment, $parentID)) {
foreach($alternatives as $alternative) if($alternative) $next = $alternative;
} }
return false; if(!$next) return false;
} }
$sitetree->destroy(); $sitetree->destroy();

View File

@ -189,11 +189,13 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
protected $original_values = null; protected $original_values = null;
/** /**
* @var boolean Temporarily override the "auto-filter" for {@link get_current_locale()} * If this is set to TRUE then {@link augmentSQL()} will automatically add a filter
* in {@link augmentSQL()}. IMPORTANT: You must set this value back to TRUE * clause to limit queries to the current {@link get_current_locale()}. This camn be
* after the temporary usage. * disabled using {@link disable_locale_filter()}
*
* @var bool
*/ */
protected static $enable_lang_filter = true; protected static $locale_filter_enabled = true;
/** /**
* @var array All locales in which a translation can be created. * @var array All locales in which a translation can be created.
@ -208,9 +210,9 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
* Reset static configuration variables to their default values * Reset static configuration variables to their default values
*/ */
static function reset() { static function reset() {
self::enable_locale_filter();
self::$default_locale = 'en_US'; self::$default_locale = 'en_US';
self::$current_locale = null; self::$current_locale = null;
self::$enable_lang_filter = true;
self::$allowed_locales = null; self::$allowed_locales = null;
} }
@ -281,7 +283,7 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
* Set the reading language, either namespaced to 'site' (website content) * Set the reading language, either namespaced to 'site' (website content)
* or 'cms' (management backend). This value is used in {@link augmentSQL()} * or 'cms' (management backend). This value is used in {@link augmentSQL()}
* to "auto-filter" all SELECT queries by this language. * to "auto-filter" all SELECT queries by this language.
* See {@link $enable_lang_filter} on how to override this behaviour temporarily. * See {@link disable_locale_filter()} on how to override this behaviour temporarily.
* *
* @param string $lang New reading language. * @param string $lang New reading language.
*/ */
@ -327,6 +329,29 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
return $result; return $result;
} }
/**
* @return bool
*/
public static function locale_filter_enabled() {
return self::$locale_filter_enabled;
}
/**
* Enables automatic filtering by locale. This is normally called after is has been
* disabled using {@link disable_locale_filter()}.
*/
public static function enable_locale_filter() {
self::$locale_filter_enabled = true;
}
/**
* Disables automatic locale filtering in {@link augmentSQL()}. This can be re-enabled
* using {@link enable_locale_filter()}.
*/
public static function disable_locale_filter() {
self::$locale_filter_enabled = false;
}
/** /**
* Gets all translations for this specific page. * Gets all translations for this specific page.
* Doesn't include the language of the current record. * Doesn't include the language of the current record.
@ -491,7 +516,7 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
* It falls back to "Locale='' OR Lang IS NULL" and assumes that * It falls back to "Locale='' OR Lang IS NULL" and assumes that
* this implies querying for the default language. * this implies querying for the default language.
* *
* Use {@link $enable_lang_filter} to temporarily disable this "auto-filtering". * Use {@link disable_locale_filter()} to temporarily disable this "auto-filtering".
*/ */
function augmentSQL(SQLQuery &$query) { function augmentSQL(SQLQuery &$query) {
// If the record is saved (and not a singleton), and has a locale, // If the record is saved (and not a singleton), and has a locale,
@ -503,7 +528,7 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
if( if(
$locale $locale
// unless the filter has been temporarily disabled // unless the filter has been temporarily disabled
&& self::$enable_lang_filter && self::locale_filter_enabled()
// DataObject::get_by_id() should work independently of language // DataObject::get_by_id() should work independently of language
&& !$query->filtersOnID() && !$query->filtersOnID()
// the query contains this table // the query contains this table
@ -786,27 +811,40 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
} }
/** /**
* Getter specifically for {@link SiteTree} subclasses * Attempt to get the page for a link in the default language that has been translated.
* which is hooked in to {@link SiteTree::get_by_url()}. *
* Disables translatable to get the page independently * @param string $URLSegment
* of the current language setting. * @param int|null $parentID
* * @return SiteTree
* @param string $urlSegment
* @param string $extraFilter
* @param boolean $cache
* @param string|array $orderby
* @return DataObject
*/ */
function alternateGetByUrl($urlSegment, $extraFilter, $cache = null, $orderby = null) { public function alternateGetByLink($URLSegment, $parentID) {
$filter = sprintf("\"SiteTree\".\"URLSegment\" = '%s'", Convert::raw2sql($urlSegment)); // If the parentID value has come from a translated page, then we need to find the corresponding parentID value
if($extraFilter) $filter .= " AND $extraFilter"; // in the default Locale.
self::$enable_lang_filter = false; if (
$record = DataObject::get_one('SiteTree', $filter); is_int($parentID)
self::$enable_lang_filter = true; && $parentID > 0
&& ($parent = DataObject::get_by_id('SiteTree', $parentID))
return $record; && ($parent->isTranslation())
) {
$parentID = $parent->getTranslationGroup();
}
// Find the locale language-independent of the page
self::disable_locale_filter();
$default = DataObject::get_one (
'SiteTree',
sprintf (
'"URLSegment" = \'%s\'%s',
Convert::raw2sql($URLSegment),
(is_int($parentID) ? " AND \"ParentID\" = $parentID" : null)
),
false
);
self::enable_locale_filter();
return $default;
} }
//-----------------------------------------------------------------------------------------------// //-----------------------------------------------------------------------------------------------//
/** /**
@ -985,7 +1023,7 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
if($this->owner->exists()) { if($this->owner->exists()) {
// HACK need to disable language filtering in augmentSQL(), // HACK need to disable language filtering in augmentSQL(),
// as we purposely want to get different language // as we purposely want to get different language
self::$enable_lang_filter = false; self::disable_locale_filter();
$translationGroupID = $this->getTranslationGroup(); $translationGroupID = $this->getTranslationGroup();
@ -1017,7 +1055,7 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
$translations = DataObject::get($this->owner->class, $filter, null, $join); $translations = DataObject::get($this->owner->class, $filter, null, $join);
} }
self::$enable_lang_filter = true; self::enable_locale_filter();
return $translations; return $translations;
} }
@ -1219,9 +1257,21 @@ class Translatable extends DataObjectDecorator implements PermissionProvider {
} }
/** /**
* @todo * Get the RelativeLink value for a home page in another locale. This is found by searching for the default home
* page in the default language, then returning the link to the translated version (if one exists).
*
* @return string
*/ */
public static function get_homepage_link_by_locale($locale) { public static function get_homepage_link_by_locale($locale) {
$originalLocale = self::get_current_locale();
self::set_current_locale(self::default_locale());
$original = SiteTree::get_by_link(RootURLController::get_default_homepage_link());
self::set_current_locale($originalLocale);
if($original) {
if($translation = $original->getTranslation($locale)) return trim($translation->RelativeLink(true), '/');
}
} }
/** /**

View File

@ -641,7 +641,7 @@ class TranslatableTest extends FunctionalTest {
Translatable::set_current_locale('en_US'); Translatable::set_current_locale('en_US');
} }
function testRootUrlDefaultsToTranslatedUrlSegment() { function testRootUrlDefaultsToTranslatedLink() {
$origPage = $this->objFromFixture('Page', 'homepage_en'); $origPage = $this->objFromFixture('Page', 'homepage_en');
$origPage->publish('Stage', 'Live'); $origPage->publish('Stage', 'Live');
$translationDe = $origPage->createTranslation('de_DE'); $translationDe = $origPage->createTranslation('de_DE');
@ -844,22 +844,64 @@ class TranslatableTest extends FunctionalTest {
Translatable::set_current_locale($origLocale); Translatable::set_current_locale($origLocale);
} }
public function testSiteTreeGetByUrlFindsTranslationWithoutLocale() { public function testAlternateGetByLink() {
$parent = $this->objFromFixture('Page', 'parent');
$child = $this->objFromFixture('Page', 'child1');
$grandchild = $this->objFromFixture('Page', 'grandchild1');
$parentTranslation = $parent->createTranslation('en_AU');
$parentTranslation->write();
$childTranslation = $child->createTranslation('en_AU');
$childTranslation->write();
$grandchildTranslation = $grandchild->createTranslation('en_AU');
$grandchildTranslation->write();
SiteTree::enable_nested_urls();
Translatable::set_current_locale('en_AU');
$this->assertEquals (
$parentTranslation->ID,
Sitetree::get_by_link($parentTranslation->Link())->ID,
'Top level pages can be found.'
);
$this->assertEquals (
$childTranslation->ID,
SiteTree::get_by_link($childTranslation->Link())->ID,
'Child pages can be found.'
);
$this->assertEquals (
$grandchildTranslation->ID,
SiteTree::get_by_link($grandchildTranslation->Link())->ID,
'Grandchild pages can be found.'
);
$this->assertEquals (
$childTranslation->ID,
SiteTree::get_by_link($parentTranslation->Link($child->URLSegment))->ID,
'Links can be made up of multiple languages'
);
}
public function testSiteTreeGetByLinkFindsTranslationWithoutLocale() {
$parent = $this->objFromFixture('Page', 'parent'); $parent = $this->objFromFixture('Page', 'parent');
$parentTranslation = $parent->createTranslation('en_AU'); $parentTranslation = $parent->createTranslation('en_AU');
$parentTranslation->URLSegment = 'parent-en-AU'; $parentTranslation->URLSegment = 'parent-en-AU';
$parentTranslation->write(); $parentTranslation->write();
$match = Sitetree::get_by_url($parentTranslation->URLSegment); $match = Sitetree::get_by_link($parentTranslation->URLSegment);
$this->assertNotNull( $this->assertNotNull(
$match, $match,
'SiteTree::get_by_url() doesnt need a locale setting to find translated pages' 'SiteTree::get_by_link() doesnt need a locale setting to find translated pages'
); );
$this->assertEquals( $this->assertEquals(
$parentTranslation->ID, $parentTranslation->ID,
$match->ID, $match->ID,
'SiteTree::get_by_url() doesnt need a locale setting to find translated pages' 'SiteTree::get_by_link() doesnt need a locale setting to find translated pages'
); );
} }
} }