mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
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:
parent
a6d804b05d
commit
aa51d57ada
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user