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 c3c696a..4213e4b 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 @@ class TextTranslationTest extends WebTestBase { '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 09518ab..f9aa0de 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 @@ class PathLanguageTest extends PathTestBase { 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 819b5f7..84cf03f 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 @@ class PollTranslateTest extends PollTestBase { * 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/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php index a409150..46ef85c 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 @@ class TranslationTest extends WebTestBase { 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); @@ -250,6 +251,35 @@ class TranslationTest extends WebTestBase { } /** + * 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. */ function resetCaches() { diff --git a/core/modules/translation/translation.install b/core/modules/translation/translation.install new file mode 100644 index 0000000..d0c5157 --- /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 909117f..66f1e2d 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 permission can translate all 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. @@ -81,8 +83,10 @@ function translation_menu() { * @see translation_menu() */ function _translation_tab_access($node) { + global $user; + 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, $user); } return FALSE; } @@ -104,13 +108,46 @@ 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) { + 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(). */ function translation_form_node_type_form_alter(&$form, &$form_state) { @@ -155,6 +192,7 @@ function translation_node_type_language_translation_enabled_validate($element, & function translation_form_node_form_alter(&$form, &$form_state) { $node = $form_state['controller']->getEntity($form_state); if (translation_supported_type($node->type)) { + global $user; if (!empty($node->translation_source)) { // We are creating a translation. Add values and lock language field. $form['translation_source'] = array('#type' => 'value', '#value' => $node->translation_source); @@ -175,7 +213,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, $user), '#collapsible' => TRUE, '#collapsed' => !$node->translate, '#tree' => TRUE, @@ -265,24 +303,17 @@ function translation_node_view(Node $node, $view_mode) { * Implements hook_node_prepare(). */ function translation_node_prepare(Node $node) { + global $user; // Only act if we are dealing with a content type supporting translations. 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'];