diff --git a/code/model/Translatable.php b/code/model/Translatable.php index 9174d5e..83f3481 100755 --- a/code/model/Translatable.php +++ b/code/model/Translatable.php @@ -213,6 +213,14 @@ class Translatable extends DataExtension implements PermissionProvider { */ protected static $allowed_locales = null; + /** + * @var array An array of fields which should be excluded from + * being transformed in the CMS for translated dataobjects. This + * includes some default excludes and any field which has already + * been transformed. + */ + protected $translatableExcludes = null; + /** * Reset static configuration variables to their default values */ @@ -927,6 +935,14 @@ class Translatable extends DataExtension implements PermissionProvider { //-----------------------------------------------------------------------------------------------// + function applyTranslatableFieldsUpdate($fields, $type) { + if (method_exists($this, $type)) { + $this->$type($fields); + } else { + throw new InvalidArgumentException("Method $type does not exist on object of type ". get_class($this)); + } + } + /** * If the record is not shown in the default language, this method * will try to autoselect a master language which is shown alongside @@ -944,6 +960,8 @@ class Translatable extends DataExtension implements PermissionProvider { */ function updateCMSFields(FieldList $fields) { $this->addTranslatableFields($fields); + + if ($this->owner->translatableFieldsAdded) return; // Show a dropdown to create a new translation. // This action is possible both when showing the "default language" @@ -996,6 +1014,9 @@ class Translatable extends DataExtension implements PermissionProvider { $langDropdown->addExtraClass('languageDropdown no-change-track'); $createButton->addExtraClass('createTranslationButton'); + + $this->owner->translatableFieldsAdded = true; + } function updateSettingsFields(&$fields) { @@ -1003,6 +1024,7 @@ class Translatable extends DataExtension implements PermissionProvider { } protected function addTranslatableFields(&$fields) { + // used in LeftAndMain->init() to set language state when reading/writing record $fields->push(new HiddenField("Locale", "Locale", $this->owner->Locale)); @@ -1023,8 +1045,15 @@ class Translatable extends DataExtension implements PermissionProvider { 'ViewerGroups', 'EditorGroups', 'CanViewType', - 'CanEditType' + 'CanEditType', + 'NewTransLang', + 'createtranslation' ); + + $this->translatableExcludes = ( isset($this->translatableExcludes) && is_array($this->translatableExcludes) ) + ? array_merge($this->translatableExcludes, $excludeFields) + : $excludeFields; + // if a language other than default language is used, we're in "translation mode", // hence have to modify the original fields @@ -1059,7 +1088,7 @@ class Translatable extends DataExtension implements PermissionProvider { // (fields are object references, so we can replace them with the translatable CompositeField) foreach($allDataFields as $dataField) { if($dataField instanceof HiddenField) continue; - if(in_array($dataField->getName(), $excludeFields)) continue; + if(in_array($dataField->getName(), $this->translatableExcludes)) continue; if(in_array($dataField->getName(), $translatableFieldNames)) { // if the field is translatable, perform transformation @@ -1068,6 +1097,8 @@ class Translatable extends DataExtension implements PermissionProvider { // else field shouldn't be editable in translation-mode, make readonly $fields->replaceField($dataField->getName(), $dataField->performReadonlyTransformation()); } + $this->translatableExcludes[] = $dataField->getName(); + $this->translatableExcludes[] = $dataField->getName() . '_original'; } } elseif($this->owner->isNew()) { @@ -1081,7 +1112,7 @@ class Translatable extends DataExtension implements PermissionProvider { ) ) ); - } + } } /** diff --git a/docs/en/index.md b/docs/en/index.md index 1a9e1fb..a907dd4 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -197,10 +197,10 @@ Keep in mind that the `[api:Translatable]` extension currently doesn't support t translated - all custom properties will automatically be fetched from their translated record on the database. This means you don't have to explicitly mark any custom properties as being translatable. -The `[api:Translatable]` decorator applies only to the getCMSFields() method on DataObject or SiteTree, not to any fields -added in overloaded getCMSFields() implementations. See Translatable->updateCMSFields() for details. By default, custom -fields in the CMS won't show an original readonly value on a translated record, although they will save correctly. You can -attach this behaviour to custom fields by using Translatable_Transformation as shown below. +The `[api:Translatable]` decorator applies only to the getCMSFields() method on DataObject or SiteTree and the getSettingsFields() +on SiteTree, not to any fields added in overloaded getCMSFields() implementations. See Translatable->updateCMSFields() for details. +By default, custom fields in the CMS won't show an original readonly value on a translated record, although they will save correctly. You can +attach this behaviour to custom fields by calling a helper function from your getCMSFields() and getSettingsFields() functions. :::php class Page extends SiteTree { @@ -214,24 +214,27 @@ attach this behaviour to custom fields by using Translatable_Transformation as s // Add fields as usual $additionalField = new TextField('AdditionalProperty'); - $fields->addFieldToTab('Root.Content.Main', $additionalField); + $fields->addFieldToTab('Root.Main', $additionalField); - // If a translation exists, exchange them with - // original/translation field pairs - $translation = $this->getTranslation(Translatable::default_locale()); - if($translation && $this->Locale != Translatable::default_locale()) { - $transformation = new Translatable_Transformation($translation); - $fields->replaceField( - 'AdditionalProperty', - $transformation->transformFormField($additionalField) - ); - } + // Apply Translatable modifications + $this->applyTranslatableFieldsUpdate($fields, 'updateCMSFields'); return $fields; } - - } + function getSettingsFields() { + $fields = parent::getSettingsFields(); + + // Add fields as usual + $additionalField = new TextField('AdditionalProperty'); + $fields->addFieldToTab('Root.Main', $additionalField); + + // Apply Translatable modifications + $this->applyTranslatableFieldsUpdate($fields, 'updateSettingsFields'); + + return $fields; + } + } ### Translating the Homepage diff --git a/tests/unit/TranslatableTest.php b/tests/unit/TranslatableTest.php index 378c79e..eed5404 100755 --- a/tests/unit/TranslatableTest.php +++ b/tests/unit/TranslatableTest.php @@ -220,7 +220,8 @@ class TranslatableTest extends FunctionalTest { } function testUpdateCMSFieldsOnSiteTree() { - $pageOrigLang = $this->objFromFixture('Page', 'testpage_en'); + $pageOrigLang = new TranslatableTest_Page(); + $pageOrigLang->write(); // first test with default language $fields = $pageOrigLang->getCMSFields(); @@ -233,6 +234,11 @@ class TranslatableTest extends FunctionalTest { $fields->dataFieldByName('Title_original'), 'Translatable doesnt modify fields if called in default language (e.g. "non-translation mode")' ); + $this->assertInstanceOf( + 'TextField', + $fields->dataFieldByName('TranslatableProperty'), + 'Has custom field' + ); // then in "translation mode" $pageTranslated = $pageOrigLang->createTranslation('fr_FR'); @@ -248,6 +254,16 @@ class TranslatableTest extends FunctionalTest { $fields->dataFieldByName('Title_original'), 'Translatable adds the original value as a ReadonlyField in "translation mode"' ); + $this->assertInstanceOf( + 'ReadonlyField', + $fields->dataFieldByName('TranslatableProperty_original'), + 'Retains original custom field' + ); + $this->assertInstanceOf( + 'TextField', + $fields->dataFieldByName('TranslatableProperty'), + 'Adds custom fields as ReadonlyField' + ); } @@ -990,6 +1006,18 @@ class TranslatableTest_Page extends Page implements TestOnly { static $db = array( 'TranslatableProperty' => 'Text' ); + + function getCMSFields() { + $fields = parent::getCMSFields(); + $fields->addFieldToTab( + 'Root.Main', + new TextField('TranslatableProperty') + ); + + $this->applyTranslatableFieldsUpdate($fields, 'updateCMSFields'); + + return $fields; + } } DataObject::add_extension('TranslatableTest_DataObject', 'TranslatableTest_Extension');