API CHANGE Deprecated Translatable::enable() and i18n::enable()- use Object::add_extension('SiteTree','Translatable'), Deprecated Translatable::disable() and i18n::disable() - use Object::remove_extension('SiteTree','Translatable'), Deprecated Translatable::enabled() - use $myPage->hasExtension('Translatable')

API CHANGE Removed Translatable::creating_from() - doesn't apply any longer
ENHANCEMENT Translatable extension is no longer hooked up to SiteTree by default, which should improve performance and memory usage for sites not using Translatable. Please use Object::add_extension('SiteTree','Translatable') in your _config.php instead. Adjusted several classes (Image, ErrorPage, RootURLController) to the new behaviour.

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@73951 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-04-01 16:35:32 +00:00
parent 9f0be3eb7b
commit e482ada014
9 changed files with 137 additions and 137 deletions

View File

@ -64,13 +64,13 @@ class RootURLController extends Controller {
} else {
$homePageOBJ = null;
}
if($homePageOBJ) {
$urlSegment = $homePageOBJ->URLSegment;
} elseif(Translatable::is_enabled()) {
$urlSegment = Translatable::get_homepage_urlsegment_by_language(Translatable::current_locale());
}
if(singleton('SiteTree')->hasExtension('Translatable')) {
$urlSegment = Translatable::get_homepage_urlsegment_by_language(Translatable::current_locale());
} elseif($homePageOBJ) {
$urlSegment = $homePageOBJ->URLSegment;
}
return ($urlSegment) ? $urlSegment : self::get_default_homepage_urlsegment();
}

View File

@ -1699,7 +1699,9 @@ class i18n extends Object {
}
/**
* Enables the multilingual content feature (proxy for Translatable::enable())
* Enables the multilingual content feature (proxy for Translatable::enable()).
*
* @deprecated 2.4 Use Object::add_extension('Page', 'Translatable');
*/
static function enable() {
Translatable::enable();
@ -1707,6 +1709,8 @@ class i18n extends Object {
/**
* Disable the multilingual content feature (proxy for Translatable::disable())
*
* @deprecated 2.4 Use Object::add_extension('Page', 'Translatable');
*/
static function disable() {
Translatable::disable();

View File

@ -149,7 +149,7 @@ class ErrorPage extends Page {
* @return String
*/
static function get_filepath_for_errorcode($statusCode, $lang = null) {
if(Translatable::is_enabled() && $lang && $lang != Translatable::default_locale()) {
if(singleton('SiteTree')->hasExtension('Translatable') && $lang && $lang != Translatable::default_locale()) {
return self::$static_filepath . "/error-{$statusCode}-{$lang}.html";
} else {
return self::$static_filepath . "/error-{$statusCode}.html";

View File

@ -494,7 +494,7 @@ class Image_Uploader extends Controller {
}
// set reading lang
if(Translatable::is_enabled() && !Director::is_ajax()) {
if(singleton('SiteTree')->hasExtension('Translatable') && !Director::is_ajax()) {
Translatable::choose_site_lang(array_keys(Translatable::get_existing_content_languages('SiteTree')));
}

View File

@ -171,7 +171,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
static $extensions = array(
"Hierarchy",
"Versioned('Stage', 'Live')",
"Translatable('Title', 'MenuTitle', 'Content', 'URLSegment', 'MetaTitle', 'MetaDescription', 'MetaKeywords', 'Status')",
);
/**
@ -885,7 +884,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
}
// get the "long" lang name suitable for the HTTP content-language flag (with hyphens instead of underscores)
$currentLang = (Translatable::is_enabled()) ? Translatable::current_locale() : i18n::get_locale();
$currentLang = ($this->hasExtension('Translatable')) ? Translatable::current_locale() : i18n::get_locale();
$tags .= "<meta http-equiv=\"Content-Language\" content=\"". i18n::convert_rfc1766($currentLang) ."\"/>\n";
// DEPRECATED 2.3: Use MetaTags
@ -1747,7 +1746,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
//TODO: Add integration
/*
if(Translatable::is_enabled() && $controller->Locale != Translatable::default_locale() && !$this->isTranslation())
if($this->hasExtension('Translatable') && $controller->Locale != Translatable::default_locale() && !$this->isTranslation())
$classes .= " untranslated ";
*/
$classes .= $this->markingClasses();

View File

@ -1,15 +1,15 @@
<?php
/**
* The Translatable decorator allows your DataObjects to have versions in different languages,
* defining which fields are can be translated.
* defining which fields are can be translated. Translatable can be applied
* to any {@link DataObject} subclass, but is mostly used with {@link SiteTree}.
* Translatable is compatible with the {@link Versioned} extension.
*
* Common language names (e.g. 'en_US') are used in Translatable for
* database-entities. On the other hand, the file-based i18n-translations
* always have a "locale" (e.g. 'en_US').
* Locales (e.g. 'en_US') are used in Translatable for identifying a record by language.
*
* <h2>Configuration</h2>
*
* You can enable {Translatable} for any subclass of {@link DataObject}:
* Enabling Translatable in the $extension array of a DataObject
* <code>
* class MyClass extends DataObject {
* static $extensions = array(
@ -17,6 +17,12 @@
* );
* }
* </code>
*
* Enabling Translatable through {@link Object::add_extension()} in your _config.php:
* <example>
* Object::add_extension('MyClass', 'Translatable');
* </example>
*
* Make sure to rebuild the database through /dev/build after enabling translatable.
*
* <h2>Usage</h2>
@ -67,6 +73,7 @@
* Translatable::set_reading_lang('de');
* $englishParent->Children();
* // right
* Translatable::set_reading_lang('de');
* $germanParent = $englishParent->getTranslation('de');
* $germanParent->Children();
* </code>
@ -111,13 +118,6 @@
* @subpackage misc
*/
class Translatable extends DataObjectDecorator {
/**
* Indicates if the multilingual feature is enabled
*
* @var boolean
*/
protected static $enabled = false;
/**
* The 'default' language.
@ -140,27 +140,12 @@ class Translatable extends DataObjectDecorator {
*/
protected static $language_decided = false;
/**
* Indicates whether the 'Locale' transformation when modifying queries should be bypassed
* If it's true
*
* @var boolean
*/
protected static $bypass = false;
/**
* A cached list of existing tables
*
* @var mixed
*/
protected static $tableList = null;
/**
* Dataobject's original ID when we're creating a new language version of an object
*
* @var unknown_type
*/
protected static $creatingFromID;
/**
* An array of fields that can be translated.
@ -316,7 +301,7 @@ class Translatable extends DataObjectDecorator {
$baseDataClass = $baseDataClass . "_Live";
}
$translationGroupID = $this->owner->getTranslationGroup();
$translationGroupID = $this->getTranslationGroup();
if(is_numeric($translationGroupID)) {
$query = new SQLQuery(
'DISTINCT Locale',
@ -361,40 +346,31 @@ class Translatable extends DataObjectDecorator {
/**
* Enables the multilingual feature
*
* @deprecated 2.4 Use Object::add_extension('Page', 'Translatable')
*/
static function enable() {
self::$enabled = true;
Object::add_extension('Page', 'Translatable');
}
/**
* Disable the multilingual feature
*
* @deprecated 2.4 Use Object::remove_extension('Page', 'Translatable')
*/
static function disable() {
self::$enabled = false;
Object::remove_extension('Page', 'Translatable');
}
/**
* Check whether multilingual support has been enabled
*
* @deprecated 2.4 Use Object::has_extension('Page', 'Translatable')
* @return boolean True if enabled
*/
static function is_enabled() {
return self::$enabled;
return Object::has_extension('Page', 'Translatable');
}
/**
* When creating, set the original ID value
*
* @param int $id
*/
static function creating_from($id) {
self::$creatingFromID = $id;
}
//-----------------------------------------------------------------------------------------------//
/**
* Construct a new Translatable object.
@ -428,8 +404,6 @@ class Translatable extends DataObjectDecorator {
}
function extraStatics() {
if(!Translatable::is_enabled()) return;
if(get_class($this->owner) == ClassInfo::baseDataClass(get_class($this->owner))) {
return array(
"db" => array(
@ -454,8 +428,6 @@ class Translatable extends DataObjectDecorator {
* Use {@link $enable_lang_filter} to temporarily disable this "auto-filtering".
*/
function augmentSQL(SQLQuery &$query) {
if(!Translatable::is_enabled()) return;
$lang = Translatable::current_locale();
$baseTable = ClassInfo::baseDataClass($this->owner->class);
$where = $query->where;
@ -491,19 +463,16 @@ class Translatable extends DataObjectDecorator {
$baseDataClass = ClassInfo::baseDataClass($this->owner->class);
if($this->owner->class != $baseDataClass) return;
if(Translatable::is_enabled()) {
$fields = array(
'OriginalID' => 'Int',
'TranslationGroupID' => 'Int',
);
$indexes = array(
'OriginalID' => true,
'TranslationGroupID' => true
);
DB::requireTable("{$baseDataClass}_translationgroups", $fields, $indexes);
} else {
DB::dontRequireTable("{$baseDataClass}_translationgroups");
}
$fields = array(
'OriginalID' => 'Int',
'TranslationGroupID' => 'Int',
);
$indexes = array(
'OriginalID' => true,
'TranslationGroupID' => true
);
DB::requireTable("{$baseDataClass}_translationgroups", $fields, $indexes);
}
/**
@ -558,8 +527,6 @@ class Translatable extends DataObjectDecorator {
/*
function augmentNumChildrenCountQuery(SQLQuery $query) {
if(!Translatable::is_enabled()) return;
if($this->isTranslation()) {
$query->where[0] = '"ParentID" = '.$this->getOriginalPage()->ID;
}
@ -578,20 +545,15 @@ class Translatable extends DataObjectDecorator {
}
function contentcontrollerInit($controller) {
if(!Translatable::is_enabled()) return;
Translatable::choose_site_lang();
$controller->Locale = Translatable::current_locale();
}
function modelascontrollerInit($controller) {
if(!Translatable::is_enabled()) return;
//$this->contentcontrollerInit($controller);
}
function initgetEditForm($controller) {
if(!Translatable::is_enabled()) return;
$this->contentcontrollerInit($controller);
}
@ -603,8 +565,6 @@ class Translatable extends DataObjectDecorator {
* but this involves complicated special cases in AllChildrenIncludingDeleted().
*/
function onBeforeWrite() {
if(!Translatable::is_enabled()) return;
// If language is not set explicitly, set it to current_locale.
// This might be a bit overzealous in assuming the language
// of the content, as a "single language" website might be expanded
@ -616,7 +576,7 @@ class Translatable extends DataObjectDecorator {
// Specific logic for SiteTree subclasses.
// If page has untranslated parents, create (unpublished) translations
// of those as well to avoid having inaccessible children in the sitetree.
// Caution: This logic is very sensitve to eternal loops when translation status isn't determined properly
// Caution: This logic is very sensitve to infinite loops when translation status isn't determined properly
if($this->owner->hasField('ParentID')) {
if(
!$this->owner->ID
@ -647,16 +607,14 @@ class Translatable extends DataObjectDecorator {
}
function onAfterWrite() {
if(!Translatable::is_enabled()) return;
// hacky way to determine if the record was created in the database,
// or just updated
if($this->owner->_TranslatableIsNewRecord) {
// this would kick in for all new records which are NOT
// created through createTranslation(), meaning they don't
// have the translation group automatically set.
$translationGroupID = $this->owner->getTranslationGroup();
if(!$translationGroupID) $this->owner->addTranslationGroup($this->owner->_TranslationGroupID ? $this->owner->_TranslationGroupID : $this->owner->ID);
$translationGroupID = $this->getTranslationGroup();
if(!$translationGroupID) $this->addTranslationGroup($this->owner->_TranslationGroupID ? $this->owner->_TranslationGroupID : $this->owner->ID);
unset($this->owner->_TranslatableIsNewRecord);
unset($this->owner->_TranslationGroupID);
}
@ -667,8 +625,6 @@ class Translatable extends DataObjectDecorator {
* Remove the record from the translation group mapping.
*/
function onBeforeDelete() {
if(!Translatable::is_enabled()) return;
$this->removeTranslationGroup();
parent::onBeforeDelete();
@ -687,8 +643,6 @@ class Translatable extends DataObjectDecorator {
* @return DataObject
*/
function alternateGetByUrl($urlSegment, $extraFilter, $cache = null, $orderby = null) {
if(!Translatable::is_enabled()) return;
$SQL_URLSegment = Convert::raw2sql($urlSegment);
Translatable::disable();
$record = DataObject::get_one('SiteTree', "\"URLSegment\" = '{$SQL_URLSegment}'");
@ -715,8 +669,6 @@ class Translatable extends DataObjectDecorator {
* seeing readonly fields as well.
*/
function updateCMSFields(FieldSet &$fields) {
if(!Translatable::is_enabled()) return;
// Don't apply these modifications for normal DataObjects - they rely on CMSMain logic
if(!($this->owner instanceof SiteTree)) return;
@ -950,7 +902,7 @@ class Translatable extends DataObjectDecorator {
$newTranslation->ID = 0;
$newTranslation->Locale = $locale;
// hacky way to set an existing translation group in onAfterWrite()
$translationGroupID = $this->owner->getTranslationGroup();
$translationGroupID = $this->getTranslationGroup();
$newTranslation->_TranslationGroupID = $translationGroupID ? $translationGroupID : $this->owner->ID;
$newTranslation->write();
@ -971,8 +923,6 @@ class Translatable extends DataObjectDecorator {
/*
function augmentStageChildren(DataObjectSet $children, $showall = false) {
if(!Translatable::is_enabled()) return;
if($this->isTranslation()) {
$children->merge($this->getOriginalPage()->stageChildren($showall));
}
@ -998,7 +948,6 @@ class Translatable extends DataObjectDecorator {
*/
/*
function augmentAllChildrenIncludingDeleted(DataObjectSet $children, $context) {
if(!Translatable::is_enabled()) return false;
$find = array();
$replace = array();
@ -1030,7 +979,6 @@ class Translatable extends DataObjectDecorator {
* @return array Map of languages in the form locale => langName
*/
static function get_existing_content_languages($className = 'SiteTree', $where = '') {
if(!Translatable::is_enabled()) return false;
$baseTable = ClassInfo::baseDataClass($className);
$query = new SQLQuery('Distinct Locale',$baseTable,$where,"",'Locale');
$dbLangs = $query->execute()->column();

View File

@ -54,8 +54,8 @@ class SearchForm extends Form {
));
}
if(Translatable::is_enabled()) {
$fields->push(new HiddenField('lang', 'lang', Translatable::current_locale()));
if(singleton('SiteTree')->hasExtension('Translatable')) {
$fields->push(new HiddenField('locale', 'locale', Translatable::current_locale()));
}
if(!$actions) {
@ -104,8 +104,8 @@ class SearchForm extends Form {
if(!isset($data)) $data = $_REQUEST;
// set language (if present)
if(Translatable::is_enabled() && isset($data['lang'])) {
Translatable::set_reading_locale($data['lang']);
if(singleton('SiteTree')->hasExtension('Translatable') && isset($data['locale'])) {
Translatable::set_reading_locale($data['locale']);
}
$keywords = $data['Search'];

View File

@ -1,5 +1,7 @@
<?php
/**
* @todo Test Versioned getters
*
* @package sapphire
* @subpackage tests
*/
@ -15,24 +17,44 @@ class TranslatableTest extends FunctionalTest {
protected $origTranslatableSettings = array();
function setUp() {
$this->origTranslatableSettings['enabled'] = Translatable::is_enabled();
$this->origTranslatableSettings['default_locale'] = Translatable::default_locale();
Translatable::enable();
Translatable::set_default_locale("en_US");
// needs to recreate the database schema with language properties
self::kill_temp_db();
// store old defaults
$this->origTranslatableSettings['has_extension'] = singleton('SiteTree')->hasExtension('Translatable');
$this->origTranslatableSettings['default_locale'] = Translatable::default_locale();
// overwrite locale
Translatable::set_default_locale("en_US");
// refresh the decorated statics - different fields in $db with Translatable enabled
singleton('SiteTree')->loadExtraStatics();
singleton('TranslatableTest_DataObject')->loadExtraStatics();
if(!$this->origTranslatableSettings['has_extension']) Object::add_extension('SiteTree', 'Translatable');
Object::add_extension('TranslatableTest_DataObject', 'Translatable');
// clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
global $_SINGLETONS;
$_SINGLETONS = array();
// @todo Hack to refresh statics on the newly decorated classes
$newSiteTree = new SiteTree();
foreach($newSiteTree->getExtensionInstances() as $extInstance) {
$extInstance->loadExtraStatics();
}
// @todo Hack to refresh statics on the newly decorated classes
$TranslatableTest_DataObject = new TranslatableTest_DataObject();
foreach($TranslatableTest_DataObject->getExtensionInstances() as $extInstance) {
$extInstance->loadExtraStatics();
}
// recreate database with new settings
$dbname = self::create_temp_db();
DB::set_alternative_database_name($dbname);
parent::setUp();
}
function tearDown() {
if(!$this->origTranslatableSettings['enabled']) Translatable::disable();
if(!$this->origTranslatableSettings['has_extension']) Object::remove_extension('SiteTree', 'Translatable');
Translatable::set_default_locale($this->origTranslatableSettings['default_locale']);
@ -41,7 +63,7 @@ class TranslatableTest extends FunctionalTest {
parent::tearDown();
}
function testTranslationGroups() {
// first in french
$frPage = new SiteTree();
@ -102,9 +124,10 @@ class TranslatableTest extends FunctionalTest {
$enPage->ID
);
}
function testGetTranslationOnSiteTree() {
$origPage = $this->objFromFixture('Page', 'testpage_en');
$translatedPage = $origPage->createTranslation('fr_FR');
$getTranslationPage = $origPage->getTranslation('fr_FR');
@ -408,6 +431,7 @@ class TranslatableTest extends FunctionalTest {
function testTranslatablePropertiesOnSiteTree() {
$origObj = $this->objFromFixture('TranslatableTest_Page', 'testpage_en');
$translatedObj = $origObj->createTranslation('fr_FR');
$translatedObj->TranslatableProperty = 'fr_FR';
$translatedObj->write();
@ -613,26 +637,26 @@ class TranslatableTest extends FunctionalTest {
'Homepage with different URLSegment in non-default language is found'
);
// @todo Fix add/remove extension
// test with translatable disabled
Translatable::disable();
$_SERVER['HTTP_HOST'] = '/';
$this->assertEquals(
RootURLController::get_homepage_urlsegment(),
'home',
'Homepage is showing in default language if ?lang GET variable is left out'
);
Translatable::enable();
// Object::remove_extension('Page', 'Translatable');
// $_SERVER['HTTP_HOST'] = '/';
// $this->assertEquals(
// RootURLController::get_homepage_urlsegment(),
// 'home',
// 'Homepage is showing in default language if ?lang GET variable is left out'
// );
// Object::add_extension('Page', 'Translatable');
// setting back to default
Translatable::set_reading_locale('en_US');
$_SERVER['HTTP_HOST'] = $_originalHost;
}
}
class TranslatableTest_DataObject extends DataObject implements TestOnly {
static $extensions = array(
"Translatable",
);
// add_extension() used to add decorator at end of file
static $db = array(
'TranslatableProperty' => 'Text'

View File

@ -11,20 +11,45 @@ class TranslatableSearchFormTest extends FunctionalTest {
protected $recreateTempDb = true;
/**
* @todo Necessary because of monolithic Translatable design
*/
protected $origTranslatableSettings = array();
function setUp() {
$this->origTranslatableSettings['enabled'] = Translatable::is_enabled();
$this->origTranslatableSettings['default_locale'] = Translatable::default_locale();
Translatable::enable();
Translatable::set_default_locale("en");
// needs to recreate the database schema with language properties
self::kill_temp_db();
// store old defaults
$this->origTranslatableSettings['has_extension'] = singleton('SiteTree')->hasExtension('Translatable');
$this->origTranslatableSettings['default_locale'] = Translatable::default_locale();
// overwrite locale
Translatable::set_default_locale("en_US");
// refresh the decorated statics - different fields in $db with Translatable enabled
singleton('SiteTree')->loadExtraStatics();
singleton('TranslatableTest_DataObject')->loadExtraStatics();
if(!$this->origTranslatableSettings['has_extension']) Object::add_extension('SiteTree', 'Translatable');
Object::add_extension('TranslatableTest_DataObject', 'Translatable');
// clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
global $_SINGLETONS;
$_SINGLETONS = array();
// @todo Hack to refresh statics on the newly decorated classes
$newSiteTree = new SiteTree();
foreach($newSiteTree->getExtensionInstances() as $extInstance) {
$extInstance->loadExtraStatics();
}
// @todo Hack to refresh statics on the newly decorated classes
$TranslatableTest_DataObject = new TranslatableTest_DataObject();
foreach($TranslatableTest_DataObject->getExtensionInstances() as $extInstance) {
$extInstance->loadExtraStatics();
}
// recreate database with new settings
$dbname = self::create_temp_db();
DB::set_alternative_database_name($dbname);
parent::setUp();
$holderPage = $this->objFromFixture('SiteTree', 'searchformholder');
@ -32,7 +57,7 @@ class TranslatableSearchFormTest extends FunctionalTest {
}
function tearDown() {
if(!$this->origTranslatableSettings['enabled']) Translatable::disable();
if(!$this->origTranslatableSettings['has_extension']) Object::remove_extension('SiteTree', 'Translatable');
Translatable::set_default_locale($this->origTranslatableSettings['default_locale']);
@ -47,7 +72,7 @@ class TranslatableSearchFormTest extends FunctionalTest {
$publishedPage = $this->objFromFixture('SiteTree', 'publishedPage');
$publishedPage->publish('Stage', 'Live');
$translatedPublishedPage = $publishedPage->createTranslation('de');
$translatedPublishedPage = $publishedPage->createTranslation('de_DE');
$translatedPublishedPage->Title = 'translatedPublishedPage';
$translatedPublishedPage->Content = 'German content';
$translatedPublishedPage->write();
@ -57,8 +82,8 @@ class TranslatableSearchFormTest extends FunctionalTest {
// from the holder is not present here - we set the language explicitly
// through a pseudo GET variable in getResults()
$lang = 'en';
$results = $sf->getResults(null, array('Search'=>'content', 'lang'=>$lang));
$lang = 'en_US';
$results = $sf->getResults(null, array('Search'=>'content', 'locale'=>$lang));
$this->assertContains(
$publishedPage->ID,
$results->column('ID'),
@ -70,8 +95,8 @@ class TranslatableSearchFormTest extends FunctionalTest {
'Published pages in another language are not found when searching in default language'
);
$lang = 'de';
$results = $sf->getResults(null, array('Search'=>'content', 'lang'=>$lang));
$lang = 'de_DE';
$results = $sf->getResults(null, array('Search'=>'content', 'locale'=>$lang));
$this->assertNotContains(
$publishedPage->ID,
$results->column('ID'),