From 0faf03ee4eb8f79d565452350e03475b0d6051a8 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 5 Oct 2009 10:33:36 +0000 Subject: [PATCH] API CHANGE Requiring TRANSLATE_ALL or TRANSLATE_ permission for authors without administrative access to edit translations ENHANCEMENT Added Translatable->providePermissions() git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@88123 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- core/model/Translatable.php | 49 +++++++++++++++++++- tests/model/TranslatableTest.php | 79 ++++++++++++++++++++++---------- tests/model/TranslatableTest.yml | 20 ++++++-- 3 files changed, 118 insertions(+), 30 deletions(-) diff --git a/core/model/Translatable.php b/core/model/Translatable.php index 055181ec3..4deee51d4 100755 --- a/core/model/Translatable.php +++ b/core/model/Translatable.php @@ -127,6 +127,16 @@ * is stored and represented in UTF-8 (Unicode). Please make sure your database and * HTML-templates adjust to this. * + *

Permissions

+ * + * Authors without administrative access need special permissions. + * + * - TRANSLATE_ALL: Translate into all locales + * - Translate_: Translate a specific locale. Only available for all locales set in + * `Translatable::set_allowed_locales()`. + * + * Note: If user-specific view permissions are required, please overload `SiteTree->canView()`. + * *

Uninstalling/Disabling

* * Disabling Translatable after creating translations will lead to all @@ -144,7 +154,7 @@ * @package sapphire * @subpackage i18n */ -class Translatable extends DataObjectDecorator { +class Translatable extends DataObjectDecorator implements PermissionProvider { /** * The 'default' language. @@ -1071,10 +1081,20 @@ class Translatable extends DataObjectDecorator { function canTranslate($member = null, $locale) { if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); - return ( + // check for locale + $allowedLocale = ( !is_array(self::get_allowed_locales()) || in_array($locale, self::get_allowed_locales()) ); + if(!$allowedLocale) return false; + + // check for generic translation permission + if(Permission::checkMember($member, 'TRANSLATE_ALL')) return true; + + // check for locale specific translate permission + if(!Permission::checkMember($member, 'TRANSLATE_' . $locale)) return false; + + return true; } /** @@ -1126,6 +1146,31 @@ class Translatable extends DataObjectDecorator { } } + function providePermissions() { + $locales = self::get_allowed_locales(); + + $permissions = array(); + if($locales) foreach($locales as $locale) { + $localeName = i18n::get_locale_name($locale); + $permissions['TRANSLATE_' . $locale] = sprintf( + _t( + 'Translatable.TRANSLATEPERMISSION', + 'Translate %s', + PR_MEDIUM, + 'Translate pages into a language' + ), + $localeName + ); + } + + $permissions['TRANSLATE_ALL'] = _t( + 'Translatable.TRANSLATEALLPERMISSION', + 'Translate into all available languages' + ); + + return $permissions; + } + /** * Get a list of languages with at least one element translated in (including the default language) * diff --git a/tests/model/TranslatableTest.php b/tests/model/TranslatableTest.php index 24593794e..3dc7f3df2 100644 --- a/tests/model/TranslatableTest.php +++ b/tests/model/TranslatableTest.php @@ -59,7 +59,7 @@ class TranslatableTest extends FunctionalTest { $cmseditor = $this->objFromFixture('Member', 'cmseditor'); $cmseditor->logIn(); } - + function testTranslationGroups() { // first in french $frPage = new SiteTree(); @@ -139,13 +139,13 @@ class TranslatableTest extends FunctionalTest { $frPage->doUnpublish(); $this->assertEquals($frPage->getTranslationGroup(), $frTranslationGroup); } - + function testGetTranslationOnSiteTree() { $origPage = $this->objFromFixture('Page', 'testpage_en'); $translatedPage = $origPage->createTranslation('fr_FR'); $getTranslationPage = $origPage->getTranslation('fr_FR'); - + $this->assertNotNull($getTranslationPage); $this->assertEquals($getTranslationPage->ID, $translatedPage->ID); } @@ -189,13 +189,13 @@ class TranslatableTest extends FunctionalTest { 'A translated page without an original doesn\'t return anything through getTranslatedLang()' ); } - + function testTranslationCantHaveSameURLSegmentAcrossLanguages() { $origPage = $this->objFromFixture('Page', 'testpage_en'); $translatedPage = $origPage->createTranslation('de_DE'); $translatedPage->URLSegment = 'testpage'; $translatedPage->write(); - + $this->assertNotEquals($origPage->URLSegment, $translatedPage->URLSegment); } @@ -341,7 +341,7 @@ class TranslatableTest extends FunctionalTest { $translatedPage->flushCache(); $origPage->flushCache(); - + $this->assertNull($origPage->getTranslation('de_DE')); $this->assertNotNull(DataObject::get_by_id('Page', $origPage->ID)); } @@ -467,10 +467,10 @@ class TranslatableTest extends FunctionalTest { function testCreateTranslationOnSiteTree() { $origPage = $this->objFromFixture('Page', 'testpage_en'); $translatedPage = $origPage->createTranslation('de_DE'); - + $this->assertEquals($translatedPage->Locale, 'de_DE'); $this->assertNotEquals($translatedPage->ID, $origPage->ID); - + $subsequentTranslatedPage = $origPage->createTranslation('de_DE'); $this->assertEquals( $translatedPage->ID, @@ -478,7 +478,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'); @@ -511,12 +511,12 @@ class TranslatableTest extends FunctionalTest { function testCreateTranslationWithoutOriginal() { $origParentPage = $this->objFromFixture('Page', 'testpage_en'); $translatedParentPage = $origParentPage->createTranslation('de_DE'); - + $translatedPageWithoutOriginal = new SiteTree(); $translatedPageWithoutOriginal->ParentID = $translatedParentPage->ID; $translatedPageWithoutOriginal->Locale = 'de_DE'; $translatedPageWithoutOriginal->write(); - + Translatable::set_current_locale('de_DE'); $this->assertEquals( $translatedParentPage->stageChildren()->column('ID'), @@ -535,28 +535,28 @@ class TranslatableTest extends FunctionalTest { $child1PageOrigID = $child1Page->ID; $grandChild1Page = $this->objFromFixture('Page', 'grandchild1'); $grandChild2Page = $this->objFromFixture('Page', 'grandchild2'); - + $this->assertFalse($grandChild1Page->hasTranslation('de_DE')); $this->assertFalse($child1Page->hasTranslation('de_DE')); $this->assertFalse($parentPage->hasTranslation('de_DE')); - + $translatedGrandChild1Page = $grandChild1Page->createTranslation('de_DE'); $translatedGrandChild2Page = $grandChild2Page->createTranslation('de_DE'); $translatedChildPage = $child1Page->getTranslation('de_DE'); $translatedParentPage = $parentPage->getTranslation('de_DE'); - + $this->assertTrue($grandChild1Page->hasTranslation('de_DE')); $this->assertEquals($translatedGrandChild1Page->ParentID, $translatedChildPage->ID); - + $this->assertTrue($grandChild2Page->hasTranslation('de_DE')); $this->assertEquals($translatedGrandChild2Page->ParentID, $translatedChildPage->ID); - + $this->assertTrue($child1Page->hasTranslation('de_DE')); $this->assertEquals($translatedChildPage->ParentID, $translatedParentPage->ID); - + $this->assertTrue($parentPage->hasTranslation('de_DE')); } - + function testHierarchyAllChildrenIncludingDeleted() { // Original tree in 'en_US': // parent @@ -614,7 +614,7 @@ class TranslatableTest extends FunctionalTest { ), "Showing AllChildrenIncludingDeleted() in default language doesnt show deleted children in other languages" ); - + // on original parent in translation mode Translatable::set_current_locale('de_DE'); SiteTree::flush_and_destroy_cache(); @@ -640,7 +640,7 @@ class TranslatableTest extends FunctionalTest { // reset language Translatable::set_current_locale('en_US'); } - + function testRootUrlDefaultsToTranslatedUrlSegment() { $origPage = $this->objFromFixture('Page', 'homepage_en'); $origPage->publish('Stage', 'Live'); @@ -726,8 +726,8 @@ class TranslatableTest extends FunctionalTest { $this->assertNotNull($compareLive); $this->assertEquals($compareLive->Title, 'Publiziert'); } - - function testCanTranslate() { + + function testCanTranslateAllowedLocales() { $origAllowedLocales = Translatable::get_allowed_locales(); $cmseditor = $this->objFromFixture('Member', 'cmseditor'); @@ -735,17 +735,17 @@ class TranslatableTest extends FunctionalTest { $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" + "Users with canEdit() and TRANSLATE_ALL 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()" + "Users with canEdit() and TRANSLATE_ALL 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()" + "Users with canEdit() and TRANSLATE_ALL permission can't create a new translation if locale is not in Translatable::get_allowed_locales()" ); $this->assertType( @@ -760,6 +760,35 @@ class TranslatableTest extends FunctionalTest { Translatable::set_allowed_locales($origAllowedLocales); } + function testCanTranslatePermissionCodes() { + $origAllowedLocales = Translatable::get_allowed_locales(); + + Translatable::set_allowed_locales(array('ja_JP','de_DE')); + + $cmseditor = $this->objFromFixture('Member', 'cmseditor'); + + $testPage = $this->objFromFixture('Page', 'testpage_en'); + $this->assertTrue( + $testPage->canTranslate($cmseditor, 'de_DE'), + "Users with TRANSLATE_ALL permission can create a new translation" + ); + + $translator = $this->objFromFixture('Member', 'germantranslator'); + + $testPage = $this->objFromFixture('Page', 'testpage_en'); + $this->assertTrue( + $testPage->canTranslate($translator, 'de_DE'), + "Users with TRANSLATE_ permission can create a new translation" + ); + + $this->assertFalse( + $testPage->canTranslate($translator, 'ja_JP'), + "Users without TRANSLATE_ permission can create a new translation" + ); + + Translatable::set_allowed_locales($origAllowedLocales); + } + function testSavePageInCMS() { $adminUser = $this->objFromFixture('Member', 'admin'); $enPage = $this->objFromFixture('Page', 'testpage_en'); diff --git a/tests/model/TranslatableTest.yml b/tests/model/TranslatableTest.yml index d14f5b533..43707e499 100644 --- a/tests/model/TranslatableTest.yml +++ b/tests/model/TranslatableTest.yml @@ -50,6 +50,8 @@ Group: Code: cmseditorgroup admingroup: Code: admingroup + germantranslators: + Code: germantranslators Member: cmseditor: FirstName: Editor @@ -59,10 +61,22 @@ Member: admin: FirstName: Admin Groups: =>Group.admingroup + germantranslator: + FirstName: German + Groups: =>Group.germantranslators Permission: + admincode: + Code: ADMIN + Group: =>Group.admingroup cmsmaincode: Code: CMS_ACCESS_CMSMain Group: =>Group.cmseditorgroup - admincode: - Code: ADMIN - Group: =>Group.admingroup \ No newline at end of file + translateAllCode: + Code: TRANSLATE_ALL + Group: =>Group.cmseditorgroup + cmsmaincode2: + Code: CMS_ACCESS_CMSMain + Group: =>Group.germantranslators + translateDeCode2: + Code: TRANSLATE_de_DE + Group: =>Group.germantranslators \ No newline at end of file