diff --git a/core/modules/field/modules/text/lib/Drupal/text/Tests/TextTranslationTest.php b/core/modules/field/modules/text/lib/Drupal/text/Tests/TextTranslationTest.php index c3c696a6084d12143b26639a4d1400474abbabb3..4213e4be46140b7f8f5d212ae4667f6da78dafa7 100644 --- a/core/modules/field/modules/text/lib/Drupal/text/Tests/TextTranslationTest.php +++ b/core/modules/field/modules/text/lib/Drupal/text/Tests/TextTranslationTest.php @@ -43,7 +43,7 @@ function setUp() { 'bypass node access', filter_permission_name($full_html_format), )); - $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content')); + $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate all content')); // Enable an additional language. $this->drupalLogin($this->admin); diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php index 09518ab9e87a7662fc53b150170329f566e12e6b..f9aa0de0312eda57ab03cd4a45f7cd098da2cc7f 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php @@ -31,7 +31,7 @@ function setUp() { parent::setUp(); // Create and login user. - $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages')); + $this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate all content', 'access administration pages')); $this->drupalLogin($this->web_user); // Enable French language. diff --git a/core/modules/poll/lib/Drupal/poll/Tests/PollTranslateTest.php b/core/modules/poll/lib/Drupal/poll/Tests/PollTranslateTest.php index 819b5f7bab5c2fc4447ab0475e20b60ecae9042c..84cf03f28f6323d2b77f55546fc5830c9b8ddcd7 100644 --- a/core/modules/poll/lib/Drupal/poll/Tests/PollTranslateTest.php +++ b/core/modules/poll/lib/Drupal/poll/Tests/PollTranslateTest.php @@ -34,7 +34,7 @@ public static function getInfo() { * the vote count values are set to 0. */ function testPollTranslate() { - $admin_user = $this->drupalCreateUser(array('administer content types', 'administer languages', 'edit any poll content', 'create poll content', 'administer nodes', 'translate content')); + $admin_user = $this->drupalCreateUser(array('administer content types', 'administer languages', 'edit any poll content', 'create poll content', 'administer nodes', 'translate all content')); // Set up a poll with two choices. $title = $this->randomName(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php index f9f76a7dc2649caacbbbda73f21ac69597ae01a8..7a30cb07a47508c714848dbfd119b600f5a3e34d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php @@ -159,4 +159,24 @@ public function testLanguageNoPluralsUpgrade() { // whether index 'plural' has been removed. $this->assertFalse(db_index_exists('locales_target', 'plural'), t('Translations without plurals upgraded.')); } + + /** + * Tests upgrading translations permissions. + */ + public function testLanguagePermissionsUpgrade() { + db_insert('role_permission')->fields(array( + 'rid' => 2, + 'permission' => 'translate content', + 'module' => 'translation', + ))->execute(); + + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Check that translate content role doesn't exist on database. + $old_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate content'))->fetchObject(); + $this->assertFalse($old_permission_exists, t('No translate content role left on database.')); + // Check that translate content has been renamed to translate all content. + $new_permission_exists = db_query('SELECT * FROM {role_permission} WHERE permission LIKE ?', array('translate all content'))->fetchObject(); + $this->assertTrue($new_permission_exists, t('Rename role translate content to translate all content was completed successfully.')); + } } diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php index d2ff1e99e31fac35cf441d525a8f49aa2affea73..a9fae77ccae4dc566a773c212cdc036772710792 100644 --- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php +++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php @@ -38,8 +38,9 @@ function setUp() { parent::setUp(); // Setup users. - $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages', 'translate content')); - $this->translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content')); + $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages', 'translate all content')); + $this->translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate all content')); + $this->limited_translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate own content')); $this->drupalLogin($this->admin_user); @@ -249,6 +250,35 @@ function testLanguageSwitcherBlockIntegration() { $this->assertLanguageSwitchLinks($node, $node_it, TRUE, $type); } + /** + * Checks that users with "translate own content" role only can translate own content. + */ + function testTranslateOwnContentRole() { + // Create a Basic page in English and its translation in Spanish with user + // that has "translate own content" role. + $this->drupalLogin($this->limited_translator); + $node = $this->createPage($this->randomName(), $this->randomName(), 'en'); + $this->assertLinkByHref('node/' . $node->nid . '/translate', 0, t('User with "translate own content" role can see translate link')); + $this->drupalGet('node/' . $node->nid . '/translate'); + $this->assertResponse(200, t('User with "translate own content" role can get translate page')); + $translation_es = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'es'); + + // Create a page as translator user. + $this->drupalLogin($this->translator); + $node = $this->createPage($this->randomName(), $this->randomName(), 'en'); + // Change to limited_translator and check that translate links aren't shown. + $this->drupalLogin($this->limited_translator); + $this->assertNoLinkByHref('node/' . $node->nid . '/translate', t('User with "translate own content" role can\'t see translate link')); + // Check if user with "translate own content" role can see translate page + // from other user's node. + $this->drupalGet('node/' . $node->nid . '/translate'); + $this->assertResponse(403, t('User with "translate own content" role can\'t get translate page')); + + // Try to change to translate with "brute force". + $this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => 'es'))); + $this->assertResponse(403, t('User with "translate own content" role can\'t get create translate page')); + } + /** * Resets static caches to make the test code match the client-side behavior. */ diff --git a/core/modules/translation/translation.install b/core/modules/translation/translation.install new file mode 100644 index 0000000000000000000000000000000000000000..d0c5157f66687b5632eefe2e8f77934ccf8cb84c --- /dev/null +++ b/core/modules/translation/translation.install @@ -0,0 +1,17 @@ +fields(array('permission' => 'translate all content')) + ->condition('permission', 'translate content') + ->execute(); +} diff --git a/core/modules/translation/translation.module b/core/modules/translation/translation.module index ce4163fd7f71c3ffeb8fc7cc8f4daa8f4ebfe39f..bf1e528ac5d573e7505871b09e8b59862b3bcdd4 100644 --- a/core/modules/translation/translation.module +++ b/core/modules/translation/translation.module @@ -37,7 +37,7 @@ function translation_help($path, $arg) { $output .= '
' . t('Assigning a language to content') . '
'; $output .= '
' . t('Use the Language drop down to select the appropriate language when creating or editing content.') . '
'; $output .= '
' . t('Translating content') . '
'; - $output .= '
' . t('Users with the translate content permission can translate content, if the content type has been configured to allow translations. To translate content, select the Translations tab when viewing the content, select the language for which you wish to provide content, and then enter the content.') . '
'; + $output .= '
' . t('Users with the translate all content or translate own content permission can translate content, if the content type has been configured to allow translations. To translate content, select the Translations tab when viewing the content, select the language for which you wish to provide content, and then enter the content.') . '
'; $output .= '
' . t('Maintaining translations') . '
'; $output .= '
' . t('If editing content in one language requires that translated versions also be updated to reflect the change, use the Flag translations as outdated check box to mark the translations as outdated and in need of revision. Individual translations may also be marked for revision by selecting the This translation needs to be updated check box on the translation editing form.') . '
'; $output .= ''; @@ -67,7 +67,9 @@ function translation_menu() { } /** - * Access callback: Checks that the user has permission to 'translate content'. + * Access callback: Checks that the user has permission to 'translate + * all content' or to 'translate own content' and has created the node + * being translated. * * Only displays the translation tab for nodes that are not language-neutral * of types that have translation enabled. @@ -82,7 +84,7 @@ function translation_menu() { */ function _translation_tab_access($node) { if ($node->langcode != LANGUAGE_NOT_SPECIFIED && translation_supported_type($node->type) && node_access('view', $node)) { - return user_access('translate content'); + return translation_user_can_translate_node($node); } return FALSE; } @@ -104,12 +106,49 @@ function translation_admin_paths() { */ function translation_permission() { return array( - 'translate content' => array( - 'title' => t('Translate content'), + 'translate all content' => array( + 'title' => t('Translate all content'), + ), + 'translate own content' => array( + 'title' => t('Translate own content'), ), ); } +/** + * Implements hook_node_access(). + */ +function translation_node_access($node, $op, $account, $langcode) { + $request_has_translation_arg = isset($_GET['translation']) && isset($_GET['target']) && is_numeric($_GET['translation']); + if ($op == 'create' && $request_has_translation_arg) { + $source_node = node_load($_GET['translation']); + if (empty($source_node) || !translation_user_can_translate_node($source_node, $account)){ + return NODE_ACCESS_DENY; + } + } + + return NODE_ACCESS_IGNORE; +} + +/** + * Check if the user has permissions to translate a node. + * + * @param $node + * Node being checked. + * @param $account + * User object to check translation permissions. + * + * @return + * TRUE if the user can translate a node, FALSE otherwise. + */ +function translation_user_can_translate_node($node, $account = NULL) { + // If no user object is supplied, the access check is for the current user. + if (empty($account)) { + $account = $GLOBALS['user']; + } + return node_access('view', $node, $account) && (user_access('translate all content', $account) || ($node->uid == $account->uid && user_access('translate own content', $account))); +} + /** * Implements hook_form_FORM_ID_alter() for node_type_form(). */ @@ -173,7 +212,7 @@ function translation_form_node_form_alter(&$form, &$form_state) { $form['translation'] = array( '#type' => 'fieldset', '#title' => t('Translation settings'), - '#access' => user_access('translate content'), + '#access' => translation_user_can_translate_node($node), '#collapsible' => TRUE, '#collapsed' => !$node->translate, '#tree' => TRUE, @@ -267,20 +306,12 @@ function translation_node_prepare(Node $node) { if (translation_supported_type($node->type) && // And it's a new node. empty($node->nid) && - // And the user has permission to translate content. - user_access('translate content') && // And the $_GET variables are set properly. isset($_GET['translation']) && isset($_GET['target']) && is_numeric($_GET['translation'])) { $source_node = node_load($_GET['translation']); - if (empty($source_node) || !node_access('view', $source_node)) { - // Source node not found or no access to view. We should not check - // for edit access, since the translator might not have permissions - // to edit the source node but should still be able to translate. - return; - } $language_list = language_list(); $langcode = $_GET['target'];