MINOR Merged from trunk

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.3@76842 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-05-14 05:19:41 +00:00 committed by Sam Minnee
parent a6d804b05d
commit aa51d57ada
4 changed files with 179 additions and 60 deletions

View File

@ -193,6 +193,15 @@ class Translatable extends DataObjectDecorator {
*/
protected static $enable_lang_filter = true;
/**
* @var array All locales in which a translation can be created.
* This limits the choice in the CMS language dropdown in the
* "Translation" tab, as well as the language dropdown above
* the CMS tree. If not set, it will default to showing all
* common locales.
*/
protected static $allowed_locales = null;
/**
* Choose the language the site is currently on.
* If $_GET['locale'] is set, then it will use that language, and store it in the session.
@ -311,9 +320,9 @@ class Translatable extends DataObjectDecorator {
* Gets all translations for this specific page.
* Doesn't include the language of the current record.
*
* @return array Numeric array of all language codes, sorted alphabetically.
* @return array Numeric array of all locales, sorted alphabetically.
*/
function getTranslatedLangs() {
function getTranslatedLocales() {
$langs = array();
$baseDataClass = ClassInfo::baseDataClass($this->owner->class); //Base Class
@ -363,7 +372,7 @@ class Translatable extends DataObjectDecorator {
*/
static function get_langs_by_id($class, $id) {
$do = DataObject::get_by_id($class, $id);
return ($do ? $do->getTranslatedLangs() : array());
return ($do ? $do->getTranslatedLocales() : array());
}
/**
@ -773,6 +782,12 @@ class Translatable extends DataObjectDecorator {
}
$isTranslationMode = $this->owner->Locale != Translatable::default_locale();
// Show a dropdown to create a new translation.
// This action is possible both when showing the "default language"
// and a translation. Include the current locale (record might not be saved yet).
$alreadyTranslatedLocales = $this->getTranslatedLocales();
$alreadyTranslatedLocales[$this->owner->Locale] = $this->owner->Locale;
if($originalRecord && $isTranslationMode) {
$originalLangID = Session::get($this->owner->ID . '_originalLangID');
@ -812,12 +827,6 @@ class Translatable extends DataObjectDecorator {
);
}
// Show a dropdown to create a new translation.
// This action is possible both when showing the "default language"
// and a translation. Include the current locale (record might not be saved yet).
$alreadyTranslatedLangs = $this->getTranslatedLangs();
$alreadyTranslatedLangs[$this->owner->Locale] = $this->owner->Locale;
$fields->addFieldsToTab(
'Root',
new Tab('Translations', _t('Translatable.TRANSLATIONS', 'Translations'),
@ -825,22 +834,22 @@ class Translatable extends DataObjectDecorator {
$langDropdown = new LanguageDropdownField(
"NewTransLang",
_t('Translatable.NEWLANGUAGE', 'New language'),
$alreadyTranslatedLangs,
'SiteTree',
'Locale-English'
$alreadyTranslatedLocales,
'SiteTree',
$this->owner
),
$createButton = new InlineFormAction('createtranslation',_t('Translatable.CREATEBUTTON', 'Create'))
)
);
$createButton->includeDefaultJS(false);
if($alreadyTranslatedLangs) {
if($alreadyTranslatedLocales) {
$fields->addFieldToTab(
'Root.Translations',
new HeaderField('ExistingTransHeader', _t('Translatable.EXISTING', 'Existing translations:'), 3)
);
$existingTransHTML = '<ul>';
foreach($alreadyTranslatedLangs as $i => $langCode) {
foreach($alreadyTranslatedLocales as $i => $langCode) {
$existingTranslation = $this->owner->getTranslation($langCode);
if($existingTranslation) {
$existingTransHTML .= sprintf('<li><a href="%s">%s</a></li>',
@ -855,8 +864,7 @@ class Translatable extends DataObjectDecorator {
new LiteralField('existingtrans',$existingTransHTML)
);
}
$langDropdown->addExtraClass('languageDropdown');
$createButton->addExtraClass('createTranslationButton');
}
@ -965,6 +973,15 @@ class Translatable extends DataObjectDecorator {
user_error('Translatable::createTranslation(): Please save your record before creating a translation', E_USER_ERROR);
}
// permission check
if(!$this->owner->canTranslate(null, $locale)) {
throw new Exception(sprintf(
'Creating a new translation in locale "%s" is not allowed for this user',
$locale
));
return;
}
$existingTranslation = $this->getTranslation($locale);
if($existingTranslation) return $existingTranslation;
@ -995,6 +1012,31 @@ class Translatable extends DataObjectDecorator {
return $newTranslation;
}
/**
* Caution: Does not consider the {@link canEdit()} permissions.
*
* @param DataObject|int $member
* @param string $locale
* @return boolean
*/
function canTranslate($member = null, $locale) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
return (
!is_array(self::get_allowed_locales())
|| in_array($locale, self::get_allowed_locales())
);
}
/**
* @return boolean
*/
function canEdit($member) {
if(!$this->owner->Locale) return true;
return $this->owner->canTranslate($member, $this->owner->Locale);
}
/**
* Returns TRUE if the current record has a translation in this language.
* Use {@link getTranslation()} to get the actual translated record from
@ -1004,7 +1046,7 @@ class Translatable extends DataObjectDecorator {
* @return boolean
*/
function hasTranslation($locale) {
return (array_search($locale, $this->getTranslatedLangs()) !== false);
return (array_search($locale, $this->getTranslatedLocales()) !== false);
}
function AllChildrenIncludingDeleted($context = null) {
@ -1081,6 +1123,28 @@ class Translatable extends DataObjectDecorator {
return null;
}
/**
* Define all locales which in which a new translation is allowed.
* Checked in {@link canTranslate()}.
*
* @param array List of allowed locale codes (see {@link i18n::$all_locales}).
* Example: array('de_DE','ja_JP')
*/
static function set_allowed_locales($locales) {
self::$allowed_locales = $locales;
}
/**
* Get all locales which are generally permitted to be translated.
* Use {@link canTranslate()} to check if a specific member has permission
* to translate a record.
*
* @return array
*/
static function get_allowed_locales() {
return self::$allowed_locales;
}
/**
* @deprecated 2.4 Use get_homepage_urlsegment_by_locale()
*/
@ -1171,6 +1235,13 @@ class Translatable extends DataObjectDecorator {
static function choose_site_lang($langsAvail=null) {
return self::choose_site_locale($langAvail);
}
/**
* @deprecated 2.4 Use getTranslatedLocales()
*/
function getTranslatedLangs() {
return $this->getTranslatedLocales();
}
}

View File

@ -11,55 +11,50 @@ class LanguageDropdownField extends GroupedDropdownField {
* Create a new LanguageDropdownField
* @param string $name
* @param string $title
* @param array $dontInclude list of languages that won't be included
* @param array $excludeLocales List of locales that won't be included
* @param string $translatingClass Name of the class with translated instances where to look for used languages
* @param string $list Indicates the source language list. Can be either Common-English, Common-Native, Locale-English, Locale-Native
*/
function __construct($name, $title, $dontInclude = array(), $translatingClass = 'SiteTree', $list = 'Common-English' ) {
$usedlangs = array_diff(
Translatable::get_existing_content_languages($translatingClass),
$dontInclude
);
// we accept in dontInclude both language codes and names, so another diff is required
$usedlangs = array_diff(
$usedlangs,
array_flip($dontInclude)
);
function __construct($name, $title, $excludeLocales = array(), $translatingClass = 'SiteTree', $list = 'Common-English', $instance = null) {
$usedLocalesWithTitle = Translatable::get_existing_content_languages($translatingClass);
$usedLocalesWithTitle = array_diff_key($usedLocalesWithTitle, $excludeLocales);
//if (isset($usedlangs[Translatable::default_locale()])) unset($usedlangs[Translatable::default_locale()]);
if ('Common-English' == $list) $languageList = i18n::get_common_languages();
else if ('Common-Native' == $list) $languageList = i18n::get_common_languages(true);
else if ('Locale-English' == $list) $languageList = i18n::get_common_locales();
else if ('Locale-Native' == $list) $languageList = i18n::get_common_locales(true);
else $languageList = i18n::get_locale_list();
if('Common-English' == $list) $allLocalesWithTitle = i18n::get_common_languages();
else if('Common-Native' == $list) $allLocalesWithTitle = i18n::get_common_languages(true);
else if('Locale-English' == $list) $allLocalesWithTitle = i18n::get_common_locales();
else if('Locale-Native' == $list) $allLocalesWithTitle = i18n::get_common_locales(true);
else $allLocalesWithTitle = i18n::get_locale_list();
$alllangs = array_diff(
$languageList,
(array)$usedlangs,
$dontInclude
);
$alllangs = array_flip(array_diff(
array_flip($alllangs),
$dontInclude
));
if (isset($alllangs[Translatable::default_locale()])) unset($alllangs[Translatable::default_locale()]);
if(isset($allLocales[Translatable::default_locale()])) unset($allLocales[Translatable::default_locale()]);
// Limit to allowed locales if defined
// Check for canTranslate() if an $instance is given
$allowedLocales = Translatable::get_allowed_locales();
foreach($allLocalesWithTitle as $locale => $localeTitle) {
if(
($allowedLocales && !in_array($locale, $allowedLocales))
|| ($excludeLocales && in_array($locale, $excludeLocales))
|| ($usedLocalesWithTitle && array_key_exists($locale, $usedLocalesWithTitle))
|| ($instance && !$instance->canTranslate(null, $locale))
) {
unset($allLocalesWithTitle[$locale]);
}
}
asort($alllangs);
if (count($usedlangs)) {
asort($usedlangs);
$labelAvail = _t('Form.LANGAVAIL', "Available languages");
$labelOther = _t('Form.LANGAOTHER', "Other languages");
parent::__construct($name, $title, array(
$labelAvail => $usedlangs,
$labelOther => $alllangs
),
reset($usedlangs)
// Sort by title (array value)
asort($allLocalesWithTitle);
if(count($usedLocalesWithTitle)) {
asort($usedLocalesWithTitle);
$source = array(
_t('Form.LANGAVAIL', "Available languages") => $usedLocalesWithTitle,
_t('Form.LANGAOTHER', "Other languages") => $allLocalesWithTitle
);
} else {
$source = $allLocalesWithTitle;
}
else {
parent::__construct($name, $title, $alllangs);
}
parent::__construct($name, $title, $source);
}
}

View File

@ -62,6 +62,14 @@ class TranslatableTest extends FunctionalTest {
parent::tear_down_once();
}
function setUp() {
parent::setUp();
// whenever a translation is created, canTranslate() is checked
$cmseditor = $this->objFromFixture('Member', 'cmseditor');
$cmseditor->logIn();
}
function testTranslationGroups() {
// first in french
@ -462,7 +470,7 @@ class TranslatableTest extends FunctionalTest {
'Subsequent calls to createTranslation() dont cause new records in database'
);
}
function testTranslatablePropertiesOnDataObject() {
$origObj = $this->objFromFixture('TranslatableTest_DataObject', 'testobject_en');
$translatedObj = $origObj->createTranslation('fr_FR');
@ -711,6 +719,38 @@ class TranslatableTest extends FunctionalTest {
$this->assertEquals($compareLive->Title, 'Publiziert');
}
function testCanTranslate() {
$origAllowedLocales = Translatable::get_allowed_locales();
$cmseditor = $this->objFromFixture('Member', 'cmseditor');
$testPage = $this->objFromFixture('Page', 'testpage_en');
$this->assertTrue(
$testPage->canTranslate($cmseditor, 'de_DE'),
"Users with canEdit() permission can create a new translation if locales are not limited"
);
Translatable::set_allowed_locales(array('ja_JP'));
$this->assertTrue(
$testPage->canTranslate($cmseditor, 'ja_JP'),
"Users with canEdit() permission can create a new translation if locale is in Translatable::get_allowed_locales()"
);
$this->assertFalse(
$testPage->canTranslate($cmseditor, 'de_DE'),
"Users with canEdit() permission can't create a new translation if locale is not in Translatable::get_allowed_locales()"
);
$this->assertType(
'Page',
$testPage->createTranslation('ja_JP')
);
try {
$testPage->createTranslation('de_DE');
$this->setExpectedException("Exception");
} catch(Exception $e) {}
Translatable::set_allowed_locales($origAllowedLocales);
}
}
class TranslatableTest_DataObject extends DataObject implements TestOnly {

View File

@ -44,4 +44,17 @@ TranslatableTest_Page:
testpage_en:
Title: En
TranslatableProperty: en_US
URLSegment: testpage-en
URLSegment: testpage-en
Group:
cmseditorgroup:
Code: cmseditorgroup
Member:
cmseditor:
FirstName: Editor
Groups: =>Group.cmseditorgroup
websiteuser:
FirstName: Website User
Permission:
cmsmaincode:
Code: CMS_ACCESS_CMSMain
Group: =>Group.cmseditorgroup