BUG Refactored updateCMSFields() logic (can be called repeatedly)

This builds on 44f8180110, but reverts most of it.
The changeset had a logical flaw where it stored state
on the Translatable extension where it was specific to a FieldList.
This meant side effects when getCMSFields() was called more than once,
such as in the CMSSettingsController template.

Note: We shouldn't need to call getCMSFields() more than once
because its an expensive call, but that's a missing feature
in the template caching layer rather than a problem with the
Translatable extension.
This commit is contained in:
Ingo Schommer 2013-01-17 12:19:59 +01:00
parent 1289b76482
commit cad776c2f8

View File

@ -212,15 +212,7 @@ class Translatable extends DataExtension implements PermissionProvider {
* common locales. * common locales.
*/ */
protected static $allowed_locales = null; 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 * Reset static configuration variables to their default values
*/ */
@ -952,6 +944,9 @@ class Translatable extends DataExtension implements PermissionProvider {
* Translatable also adds a new tab "Translation" which shows existing * Translatable also adds a new tab "Translation" which shows existing
* translations, as well as a formaction to create new translations based * translations, as well as a formaction to create new translations based
* on a dropdown with available languages. * on a dropdown with available languages.
*
* This method can be called multiple times on the same FieldList
* because it checks which fields have already been added or modified.
* *
* @todo This is specific to SiteTree and CMSMain * @todo This is specific to SiteTree and CMSMain
* @todo Implement a special "translation mode" which triggers display of the * @todo Implement a special "translation mode" which triggers display of the
@ -961,82 +956,85 @@ class Translatable extends DataExtension implements PermissionProvider {
function updateCMSFields(FieldList $fields) { function updateCMSFields(FieldList $fields) {
$this->addTranslatableFields($fields); $this->addTranslatableFields($fields);
if ($this->owner->translatableFieldsAdded) return;
// Show a dropdown to create a new translation. // Show a dropdown to create a new translation.
// This action is possible both when showing the "default language" // This action is possible both when showing the "default language"
// and a translation. Include the current locale (record might not be saved yet). // and a translation. Include the current locale (record might not be saved yet).
$alreadyTranslatedLocales = $this->getTranslatedLocales(); $alreadyTranslatedLocales = $this->getTranslatedLocales();
$alreadyTranslatedLocales[$this->owner->Locale] = $this->owner->Locale; $alreadyTranslatedLocales[$this->owner->Locale] = $this->owner->Locale;
$alreadyTranslatedLocales = array_combine($alreadyTranslatedLocales, $alreadyTranslatedLocales); $alreadyTranslatedLocales = array_combine($alreadyTranslatedLocales, $alreadyTranslatedLocales);
$fields->addFieldsToTab( // Check if fields exist already to avoid adding them twice on repeat invocations
'Root', $tab = $fields->findOrMakeTab('Root.Translations', _t('Translatable.TRANSLATIONS', 'Translations'));
new Tab('Translations', _t('Translatable.TRANSLATIONS', 'Translations'), if(!$tab->fieldByName('CreateTransHeader')) {
new HeaderField('CreateTransHeader', _t('Translatable.CREATE', 'Create new translation'), 2), $tab->push(new HeaderField(
$langDropdown = new LanguageDropdownField( 'CreateTransHeader',
"NewTransLang", _t('Translatable.CREATE', 'Create new translation'),
_t('Translatable.NEWLANGUAGE', 'New language'), 2
$alreadyTranslatedLocales, ));
'SiteTree', }
'Locale-English', if(!$tab->fieldByName('NewTransLang') && !$tab->fieldByName('AllTransCreated')) {
$this->owner $langDropdown = LanguageDropdownField::create(
), "NewTransLang",
$createButton = new InlineFormAction( _t('Translatable.NEWLANGUAGE', 'New language'),
'createtranslation', $alreadyTranslatedLocales,
_t('Translatable.CREATEBUTTON', 'Create') 'SiteTree',
) 'Locale-English',
) $this->owner
); )->addExtraClass('languageDropdown no-change-track');
$createButton->includeDefaultJS(false); $tab->push($langDropdown);
$canAddLocale = (count($langDropdown->getSource()) > 0);
if ( count($langDropdown->getSource()) < 1 ) {
$fields->insertAfter( if($canAddLocale) {
new LiteralField( // Only add create button if new languages are available
$tab->push(
$createButton = InlineFormAction::create(
'createtranslation',
_t('Translatable.CREATEBUTTON', 'Create')
)->addExtraClass('createTranslationButton')
);
$createButton->includeDefaultJS(false); // not fluent API...
} else {
$tab->removeByName('NewTransLang');
$tab->push(new LiteralField(
'AllTransCreated', 'AllTransCreated',
_t('Translatable.ALLCREATED', 'All allowed translations have been created.') _t('Translatable.ALLCREATED', 'All allowed translations have been created.')
), ));
'CreateTransHeader' }
);
$fields->removeByName('NewTransLang');
$fields->removeByName('createtranslation');
} }
if($alreadyTranslatedLocales) { if($alreadyTranslatedLocales) {
$fields->addFieldToTab( if(!$tab->fieldByName('ExistingTransHeader')) {
'Root.Translations', $tab->push(new HeaderField(
new HeaderField('ExistingTransHeader', _t('Translatable.EXISTING', 'Existing translations'), 3) 'ExistingTransHeader',
); _t('Translatable.EXISTING', 'Existing translations'),
$existingTransHTML = '<ul>'; 3
foreach($alreadyTranslatedLocales as $langCode) { ));
$existingTranslation = $this->owner->getTranslation($langCode); if(!$tab->fieldByName('existingtrans')) {
if($existingTranslation && $existingTranslation->hasMethod('CMSEditLink')) { $existingTransHTML = '<ul>';
$existingTransHTML .= sprintf('<li><a href="%s">%s</a></li>', foreach($alreadyTranslatedLocales as $langCode) {
sprintf('%s/?locale=%s', $existingTranslation->CMSEditLink(), $langCode), $existingTranslation = $this->owner->getTranslation($langCode);
i18n::get_locale_name($langCode) if($existingTranslation && $existingTranslation->hasMethod('CMSEditLink')) {
); $existingTransHTML .= sprintf('<li><a href="%s">%s</a></li>',
sprintf('%s/?locale=%s', $existingTranslation->CMSEditLink(), $langCode),
i18n::get_locale_name($langCode)
);
}
}
$existingTransHTML .= '</ul>';
$tab->push(new LiteralField('existingtrans',$existingTransHTML));
} }
} }
$existingTransHTML .= '</ul>'; }
$fields->addFieldToTab(
'Root.Translations',
new LiteralField('existingtrans',$existingTransHTML)
);
}
$langDropdown->addExtraClass('languageDropdown no-change-track');
$createButton->addExtraClass('createTranslationButton');
$this->owner->translatableFieldsAdded = true;
} }
function updateSettingsFields(&$fields) { function updateSettingsFields(&$fields) {
$this->addTranslatableFields($fields); $this->addTranslatableFields($fields);
} }
/**
* This method can be called multiple times on the same FieldList
* because it checks which fields have already been added or modified.
*/
protected function addTranslatableFields(&$fields) { protected function addTranslatableFields(&$fields) {
// used in LeftAndMain->init() to set language state when reading/writing record // used in LeftAndMain->init() to set language state when reading/writing record
$fields->push(new HiddenField("Locale", "Locale", $this->owner->Locale)); $fields->push(new HiddenField("Locale", "Locale", $this->owner->Locale));
@ -1061,17 +1059,10 @@ class Translatable extends DataExtension implements PermissionProvider {
'NewTransLang', 'NewTransLang',
'createtranslation' '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", // if a language other than default language is used, we're in "translation mode",
// hence have to modify the original fields // hence have to modify the original fields
$creating = false;
$baseClass = $this->owner->class; $baseClass = $this->owner->class;
$allFields = $fields->toArray();
while( ($p = get_parent_class($baseClass)) != "DataObject") $baseClass = $p; while( ($p = get_parent_class($baseClass)) != "DataObject") $baseClass = $p;
// try to get the record in "default language" // try to get the record in "default language"
@ -1085,8 +1076,6 @@ class Translatable extends DataExtension implements PermissionProvider {
$isTranslationMode = $this->owner->Locale != Translatable::default_locale(); $isTranslationMode = $this->owner->Locale != Translatable::default_locale();
if($originalRecord && $isTranslationMode) { if($originalRecord && $isTranslationMode) {
$originalLangID = Session::get($this->owner->ID . '_originalLangID');
// Remove parent page dropdown // Remove parent page dropdown
$fields->removeByName("ParentType"); $fields->removeByName("ParentType");
$fields->removeByName("ParentID"); $fields->removeByName("ParentID");
@ -1099,18 +1088,22 @@ class Translatable extends DataExtension implements PermissionProvider {
// iterate through sequential list of all datafields in fieldset // iterate through sequential list of all datafields in fieldset
// (fields are object references, so we can replace them with the translatable CompositeField) // (fields are object references, so we can replace them with the translatable CompositeField)
foreach($allDataFields as $dataField) { foreach($allDataFields as $dataField) {
// Transformation is a visual helper for CMS authors, so ignore hidden fields
if($dataField instanceof HiddenField) continue; if($dataField instanceof HiddenField) continue;
if(in_array($dataField->getName(), $this->translatableExcludes)) continue; // Some fields are explicitly excluded from transformation
if(in_array($dataField->getName(), $excludeFields)) continue;
// Readonly field which has been added previously
if(preg_match('/_original$/', $dataField->getName())) continue;
// Field already has been transformed
if(isset($allDataFields[$dataField->getName() . '_original'])) continue;
if(in_array($dataField->getName(), $translatableFieldNames)) { if(in_array($dataField->getName(), $translatableFieldNames)) {
// if the field is translatable, perform transformation // if the field is translatable, perform transformation
$fields->replaceField($dataField->getName(), $transformation->transformFormField($dataField)); $fields->replaceField($dataField->getName(), $transformation->transformFormField($dataField));
} else { } elseif(!$dataField->isReadonly()) {
// else field shouldn't be editable in translation-mode, make readonly // else field shouldn't be editable in translation-mode, make readonly
$fields->replaceField($dataField->getName(), $dataField->performReadonlyTransformation()); $fields->replaceField($dataField->getName(), $dataField->performReadonlyTransformation());
} }
$this->translatableExcludes[] = $dataField->getName();
$this->translatableExcludes[] = $dataField->getName() . '_original';
} }
} elseif($this->owner->isNew()) { } elseif($this->owner->isNew()) {
@ -1128,8 +1121,7 @@ class Translatable extends DataExtension implements PermissionProvider {
} }
/** /**
* Get the names of all translatable fields on this class * Get the names of all translatable fields on this class as a numeric array.
* as a numeric array.
* @todo Integrate with blacklist once branches/translatable is merged back. * @todo Integrate with blacklist once branches/translatable is merged back.
* *
* @return array * @return array