diff --git a/core/control/RootURLController.php b/core/control/RootURLController.php index ce9a581bc..b141b98b7 100755 --- a/core/control/RootURLController.php +++ b/core/control/RootURLController.php @@ -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(); } diff --git a/core/i18n.php b/core/i18n.php index 52cc0f604..7097d2fb4 100755 --- a/core/i18n.php +++ b/core/i18n.php @@ -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(); diff --git a/core/model/ErrorPage.php b/core/model/ErrorPage.php index 8002c8b25..6b3255bb1 100755 --- a/core/model/ErrorPage.php +++ b/core/model/ErrorPage.php @@ -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"; diff --git a/core/model/Image.php b/core/model/Image.php index 40f7cbe05..a220131ba 100755 --- a/core/model/Image.php +++ b/core/model/Image.php @@ -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'))); } diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index 457645bca..592b36304 100644 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -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 .= "\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(); diff --git a/core/model/Translatable.php b/core/model/Translatable.php index 9bc4bb520..67b52d2f0 100755 --- a/core/model/Translatable.php +++ b/core/model/Translatable.php @@ -1,15 +1,15 @@ Configuration * - * You can enable {Translatable} for any subclass of {@link DataObject}: + * Enabling Translatable in the $extension array of a DataObject * * class MyClass extends DataObject { * static $extensions = array( @@ -17,6 +17,12 @@ * ); * } * + * + * Enabling Translatable through {@link Object::add_extension()} in your _config.php: + * + * Object::add_extension('MyClass', 'Translatable'); + * + * * Make sure to rebuild the database through /dev/build after enabling translatable. * *

Usage

@@ -67,6 +73,7 @@ * Translatable::set_reading_lang('de'); * $englishParent->Children(); * // right + * Translatable::set_reading_lang('de'); * $germanParent = $englishParent->getTranslation('de'); * $germanParent->Children(); * @@ -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(); diff --git a/search/SearchForm.php b/search/SearchForm.php index 55d3d85ec..4c6b9f6f6 100755 --- a/search/SearchForm.php +++ b/search/SearchForm.php @@ -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']; diff --git a/tests/model/TranslatableTest.php b/tests/model/TranslatableTest.php index b11648f85..4812b053c 100644 --- a/tests/model/TranslatableTest.php +++ b/tests/model/TranslatableTest.php @@ -1,5 +1,7 @@ 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' diff --git a/tests/search/TranslatableSearchFormTest.php b/tests/search/TranslatableSearchFormTest.php index 3f6364de7..85f497e59 100644 --- a/tests/search/TranslatableSearchFormTest.php +++ b/tests/search/TranslatableSearchFormTest.php @@ -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'),