diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 739ccbc6b96..42e4d77f6a7 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -120,6 +120,11 @@ use Drupal\Core\Cache\CacheBackendInterface; * entity. * - menu_path_wildcard: (optional) A string identifying the menu loader in the * router path. + * - permission_granularity: (optional) Specifies whether a module exposing + * permissions for the current entity type should use entity-type level + * granularity, bundle level granularity or just skip this entity. The allowed + * values are respectively "entity_type", "bundle" or FALSE. Defaults to + * "entity_type". * * The defaults for the plugin definition are provided in * \Drupal\Core\Entity\EntityManager::defaults. @@ -159,6 +164,7 @@ class EntityManager extends PluginManagerBase { 'access_controller_class' => 'Drupal\Core\Entity\EntityAccessController', 'static_cache' => TRUE, 'translation' => array(), + 'permission_granularity' => 'entity_type', ); /** diff --git a/core/modules/comment/lib/Drupal/comment/CommentTranslationController.php b/core/modules/comment/lib/Drupal/comment/CommentTranslationController.php index 0cfa52b2089..644694fb225 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentTranslationController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentTranslationController.php @@ -9,12 +9,29 @@ namespace Drupal\comment; use Drupal\Core\Entity\EntityInterface; -use Drupal\translation_entity\EntityTranslationController; +use Drupal\translation_entity\EntityTranslationControllerNG; /** * Defines the translation controller class for comments. */ -class CommentTranslationController extends EntityTranslationController { +class CommentTranslationController extends EntityTranslationControllerNG { + + /** + * Overrides EntityTranslationController::getAccess(). + */ + public function getAccess(EntityInterface $entity, $op) { + switch ($op) { + case 'view': + return user_access('access comments'); + case 'update': + return comment_access('edit', $entity); + case 'delete': + return user_access('administer comments'); + case 'create': + return user_access('post comments'); + } + return parent::getAccess($entity, $op); + } /** * Overrides EntityTranslationController::entityFormTitle(). diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php index 94934881b50..259a8f2d489 100644 --- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php @@ -25,7 +25,7 @@ use Drupal\Core\Annotation\Translation; * form_controller_class = { * "default" = "Drupal\comment\CommentFormController" * }, - * translation_controller_class = "Drupal\translation_entity\EntityTranslationControllerNG", + * translation_controller_class = "Drupal\comment\CommentTranslationController", * base_table = "comment", * uri_callback = "comment_uri", * fieldable = TRUE, diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php index 60cb4746f89..2b44138cab9 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php @@ -34,9 +34,6 @@ class CommentTranslationUITest extends EntityTranslationUITest { ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ function setUp() { $this->entityType = 'comment'; $this->nodeBundle = 'article'; @@ -58,7 +55,7 @@ class CommentTranslationUITest extends EntityTranslationUITest { * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). */ function getTranslatorPermissions() { - return array('post comments', 'administer comments', "translate $this->entityType entities", 'edit original values'); + return array_merge(parent::getTranslatorPermissions(), array('post comments', 'administer comments')); } /** @@ -100,7 +97,7 @@ class CommentTranslationUITest extends EntityTranslationUITest { */ function testTranslateLinkCommentAdminPage() { $this->drupalCreateContentType(array('type' => 'page', 'name' => 'page')); - $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer comments', 'translate any entity')); + $this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer comments'))); $this->drupalLogin($this->admin_user); $cid_translatable = $this->createEntity(array(), $this->langcodes[0], $this->nodeBundle); diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index d1b32dc10c9..283a516569f 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -336,7 +336,7 @@ class NodeFormController extends EntityFormController { } $element['preview'] = array( - '#access' => $preview_mode != DRUPAL_DISABLED, + '#access' => $preview_mode != DRUPAL_DISABLED && (node_access('create', $node) || node_access('update', $node)), '#value' => t('Preview'), '#weight' => 20, '#validate' => array( @@ -429,6 +429,9 @@ class NodeFormController extends EntityFormController { * A reference to a keyed array containing the current state of the form. */ public function preview(array $form, array &$form_state) { + // @todo Remove this: we should not have explicit includes in autoloaded + // classes. + module_load_include('inc', 'node', 'node.pages'); drupal_set_title(t('Preview'), PASS_THROUGH); $form_state['node_preview'] = node_preview($this->getEntity($form_state)); $form_state['rebuild'] = TRUE; diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php index a78309ba004..a4cf26428c2 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php @@ -39,7 +39,8 @@ use Drupal\Core\Annotation\Translation; * }, * bundle_keys = { * "bundle" = "type" - * } + * }, + * permission_granularity = "bundle" * ) */ class Node extends Entity implements ContentEntityInterface { diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php index df997496999..7b13462bd7e 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php @@ -34,9 +34,6 @@ class NodeTranslationUITest extends EntityTranslationUITest { ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ function setUp() { $this->entityType = 'node'; $this->bundle = 'article'; @@ -56,7 +53,7 @@ class NodeTranslationUITest extends EntityTranslationUITest { * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). */ function getTranslatorPermissions() { - return array("edit any $this->bundle content", "translate $this->entityType entities", 'edit original values'); + return array_merge(parent::getTranslatorPermissions(), array("edit any $this->bundle content")); } /** @@ -80,7 +77,7 @@ class NodeTranslationUITest extends EntityTranslationUITest { * Tests field translation form. */ function testFieldTranslationForm() { - $admin_user = $this->drupalCreateUser(array('translate any entity', 'access administration pages', 'bypass node access', 'administer node fields')); + $admin_user = $this->drupalCreateUser(array_merge($this->getTranslatorPermissions(), array('access administration pages', 'bypass node access', 'administer node fields'))); $this->drupalLogin($admin_user); $article = $this->drupalCreateNode(array('type' => 'article', 'langcode' => 'en')); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index e6cc158edd5..1eb16b0e78e 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -462,12 +462,14 @@ abstract class WebTestBase extends TestBase { * @param array $permissions * Array of permission names to assign to user. Note that the user always * has the default permissions derived from the "authenticated users" role. + * @param $name + * The user name. * * @return object|false * A fully loaded user object with pass_raw property, or FALSE if account * creation fails. */ - protected function drupalCreateUser(array $permissions = array()) { + protected function drupalCreateUser(array $permissions = array(), $name = NULL) { // Create a role with the given permission set, if any. $rid = FALSE; if ($permissions) { @@ -479,7 +481,7 @@ abstract class WebTestBase extends TestBase { // Create a user assigned to that role. $edit = array(); - $edit['name'] = $this->randomName(); + $edit['name'] = !empty($name) ? $name : $this->randomName(); $edit['mail'] = $edit['name'] . '@example.com'; $edit['pass'] = user_password(); $edit['status'] = 1; diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index d3c124f9a1f..74bae2fad87 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -68,12 +68,12 @@ class EntityAccessTest extends WebTestBase { 'view' => TRUE, ), $entity); - // The custom user is not allowed to view test entities. + // The custom user is not allowed to perform any operation on test entities. $custom_user = $this->drupalCreateUser(); $this->assertEntityAccess(array( - 'create' => TRUE, - 'update' => TRUE, - 'delete' => TRUE, + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, 'view' => FALSE, ), $entity, $custom_user); } diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php index 9b66b0308dc..2ab66997ef1 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php @@ -30,21 +30,21 @@ class EntityTestAccessController implements EntityAccessControllerInterface { * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::createAccess(). */ public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) { - return TRUE; + return user_access('administer entity_test content', $account); } /** * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::updateAccess(). */ public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) { - return TRUE; + return user_access('administer entity_test content', $account); } /** * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess(). */ public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) { - return TRUE; + return user_access('administer entity_test content', $account); } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php index be46f2ab808..d1a93142306 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php @@ -38,7 +38,8 @@ use Drupal\Core\Annotation\Translation; * bundle_keys = { * "bundle" = "vid" * }, - * menu_base_path = "taxonomy/term/%taxonomy_term" + * menu_base_path = "taxonomy/term/%taxonomy_term", + * permission_granularity = "bundle" * ) */ class Term extends Entity implements ContentEntityInterface { diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php index d32df127cd0..59226fd6cf6 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php @@ -15,6 +15,20 @@ use Drupal\translation_entity\EntityTranslationController; */ class TermTranslationController extends EntityTranslationController { + /** + * Overrides EntityTranslationController::getAccess(). + */ + public function getAccess(EntityInterface $entity, $op) { + switch ($op) { + case 'create': + case 'update': + return taxonomy_term_access('edit', $entity); + case 'delete': + return taxonomy_term_access('delete', $entity); + } + return parent::getAccess($entity, $op); + } + /** * Overrides EntityTranslationController::entityFormAlter(). */ diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTranslationUITest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTranslationUITest.php index dd2ed0c5407..4c0dde2f8a2 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTranslationUITest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTranslationUITest.php @@ -41,9 +41,6 @@ class TermTranslationUITest extends EntityTranslationUITest { ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ function setUp() { $this->entityType = 'taxonomy_term'; $this->bundle = 'tags'; @@ -73,7 +70,7 @@ class TermTranslationUITest extends EntityTranslationUITest { * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). */ function getTranslatorPermissions() { - return array('administer taxonomy', "translate $this->entityType entities", 'edit original values'); + return array_merge(parent::getTranslatorPermissions(), array('administer taxonomy')); } /** @@ -102,7 +99,7 @@ class TermTranslationUITest extends EntityTranslationUITest { * Tests translate link on vocabulary term list. */ function testTranslateLinkVocabularyAdminPage() { - $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer taxonomy', 'translate any entity')); + $this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer taxonomy'))); $this->drupalLogin($this->admin_user); $translatable_tid = $this->createEntity(array(), $this->langcodes[0], $this->vocabulary->id()); diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php index 36202ebba52..a76da8892e2 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php @@ -99,9 +99,17 @@ class EntityTranslationController implements EntityTranslationControllerInterfac /** * Implements EntityTranslationControllerInterface::getTranslationAccess(). */ - public function getTranslationAccess(EntityInterface $entity, $langcode) { - $entity_type = $entity->entityType(); - return (user_access('translate any entity') || user_access("translate $entity_type entities")) && ($langcode != $entity->language()->langcode || user_access('edit original values')); + public function getTranslationAccess(EntityInterface $entity, $op) { + // @todo Move this logic into a translation access controller checking also + // the translation language and the given account. + $info = $entity->entityInfo(); + $translate_permission = TRUE; + // If no permission granularity is defined this entity type does not need an + // explicit translate permission. + if (!user_access('translate any entity') && !empty($info['permission_granularity'])) { + $translate_permission = user_access($info['permission_granularity'] == 'bundle' ? "translate {$entity->bundle()} {$entity->entityType()}" : "translate {$entity->entityType()}"); + } + return $translate_permission && user_access("$op entity translations"); } /** @@ -203,6 +211,7 @@ class EntityTranslationController implements EntityTranslationControllerInterfac '#value' => t('Delete translation'), '#weight' => $weight, '#submit' => array(array($this, 'entityFormDeleteTranslation')), + '#access' => $this->getTranslationAccess($entity, 'delete'), ); } @@ -220,7 +229,7 @@ class EntityTranslationController implements EntityTranslationControllerInterfac '#collapsed' => TRUE, '#tree' => TRUE, '#weight' => 10, - '#access' => $this->getTranslationAccess($entity, $form_langcode), + '#access' => $this->getTranslationAccess($entity, $source_langcode ? 'create' : 'update'), '#multilingual' => TRUE, ); @@ -259,17 +268,11 @@ class EntityTranslationController implements EntityTranslationControllerInterfac } /** - * Process callback: Determines which elements get clue in the form. - * - * @param array $element - * Form API element. - * - * @return array - * A processed element with the shared elements marked with a clue. + * Process callback: determines which elements get clue in the form. * * @see \Drupal\translation_entity\EntityTranslationController::entityFormAlter() */ - public function entityFormSharedElements($element) { + public function entityFormSharedElements($element, $form_state, $form) { static $ignored_types; // @todo Find a more reliable way to determine if a form element concerns a @@ -280,7 +283,7 @@ class EntityTranslationController implements EntityTranslationControllerInterfac foreach (element_children($element) as $key) { if (!isset($element[$key]['#type'])) { - $this->entityFormSharedElements($element[$key]); + $this->entityFormSharedElements($element[$key], $form_state, $form); } else { // Ignore non-widget form elements. @@ -289,7 +292,15 @@ class EntityTranslationController implements EntityTranslationControllerInterfac } // Elements are considered to be non multilingual by default. if (empty($element[$key]['#multilingual'])) { - $this->addTranslatabilityClue($element[$key]); + // If we are displaying a multilingual entity form we need to provide + // translatability clues, otherwise the shared form elements should be + // hidden. + if (empty($form_state['translation_entity']['translation_form'])) { + $this->addTranslatabilityClue($element[$key]); + } + else { + $element[$key]['#access'] = FALSE; + } } } } diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php index ed08fc08c6a..a42a0192adb 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php @@ -132,17 +132,21 @@ interface EntityTranslationControllerInterface { public function getAccess(EntityInterface $entity, $op); /** - * Checks if a user is allowed to edit the given translation. + * Checks if the user can perform the given operation on translations of the + * wrapped entity. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity whose translation has to be accessed. - * @param string $langcode - * The language code identifying the translation to be accessed. + * @param $op + * The operation to be performed on the translation. Possible values are: + * - "create" + * - "update" + * - "delete" * * @return boolean * TRUE if the operation may be performed, FALSE otherwise. */ - public function getTranslationAccess(EntityInterface $entity, $langcode); + public function getTranslationAccess(EntityInterface $entity, $op); /** * Retrieves the source language for the translation being created. diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php index 4c4e9e60ac4..fd007cd5c02 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php @@ -15,7 +15,14 @@ use Drupal\Core\Entity\EntityInterface; class EntityTranslationControllerNG extends EntityTranslationController { /** - * Overrides EntityTranslationController::removeTranslation(). + * Overrides \Drupal\translation_entity\EntityTranslationController::getAccess(). + */ + public function getAccess(EntityInterface $entity, $op) { + return $entity->access($op); + } + + /** + * Overrides \Drupal\translation_entity\EntityTranslationControllerInterface::removeTranslation(). */ public function removeTranslation(EntityInterface $entity, $langcode) { $translation = $entity->getTranslation($langcode); @@ -23,4 +30,5 @@ class EntityTranslationControllerNG extends EntityTranslationController { $translation->$property_name = array(); } } + } diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php index 7d5a38b28a7..2439c8b1a18 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/ConfigTestTranslationUITest.php @@ -29,21 +29,11 @@ class ConfigTestTranslationUITest extends EntityTranslationUITest { ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ function setUp() { $this->entityType = 'config_test'; parent::setUp(); } - /** - * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). - */ - function getTranslatorPermissions() { - return array("translate $this->entityType entities", 'edit original values'); - } - /** * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getNewEntityValues(). */ diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php index 2fbb509370b..78bcc46741a 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTestTranslationUITest.php @@ -21,7 +21,7 @@ class EntityTestTranslationUITest extends EntityTranslationUITest { public static function getInfo() { return array( - 'name' => 'Entity Test Translation UI', + 'name' => 'Entity Test translation UI', 'description' => 'Tests the test entity translation UI with the test entity.', 'group' => 'Entity Translation UI', ); @@ -40,7 +40,7 @@ class EntityTestTranslationUITest extends EntityTranslationUITest { * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). */ function getTranslatorPermissions() { - return array('administer entity_test content', "translate $this->entityType entities", 'edit original values'); + return array_merge(parent::getTranslatorPermissions(), array('administer entity_test content')); } /** diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationTestBase.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationTestBase.php new file mode 100644 index 00000000000..13e1f59d104 --- /dev/null +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationTestBase.php @@ -0,0 +1,215 @@ +setupLanguages(); + $this->setupBundle(); + $this->enableTranslation(); + $this->setupUsers(); + $this->setupTestFields(); + + $this->controller = translation_entity_controller($this->entityType); + } + + /** + * Enables additional languages. + */ + protected function setupLanguages() { + $this->langcodes = array('it', 'fr'); + foreach ($this->langcodes as $langcode) { + language_save(new Language(array('langcode' => $langcode))); + } + array_unshift($this->langcodes, language_default()->langcode); + } + + /** + * Returns an array of permissions needed for the translator. + */ + protected function getTranslatorPermissions() { + return array_filter(array($this->getTranslatePermission(), 'create entity translations', 'update entity translations', 'delete entity translations')); + } + + /** + * Returns the translate permissions for the current entity and bundle. + */ + protected function getTranslatePermission() { + $info = entity_get_info($this->entityType); + if (!empty($info['permission_granularity'])) { + return $info['permission_granularity'] == 'bundle' ? "translate {$this->bundle} {$this->entityType}" : "translate {$this->entityType}"; + } + } + + /** + * Returns an array of permissions needed for the editor. + */ + protected function getEditorPermissions() { + // Every entity-type-specific test needs to define these. + return array(); + } + + /** + * Creates and activates translator, editor and admin users. + */ + protected function setupUsers() { + $this->translator = $this->drupalCreateUser($this->getTranslatorPermissions(), 'translator'); + $this->editor = $this->drupalCreateUser($this->getEditorPermissions(), 'editor'); + $this->administrator = $this->drupalCreateUser(array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions()), 'administrator'); + $this->drupalLogin($this->translator); + } + + /** + * Creates or initializes the bundle date if needed. + */ + protected function setupBundle() { + if (empty($this->bundle)) { + $this->bundle = $this->entityType; + } + } + + /** + * Enables translation for the current entity type and bundle. + */ + protected function enableTranslation() { + // Enable translation for the current entity type and ensure the change is + // picked up. + translation_entity_set_config($this->entityType, $this->bundle, 'enabled', TRUE); + drupal_static_reset(); + entity_info_cache_clear(); + menu_router_rebuild(); + } + + /** + * Creates the test fields. + */ + protected function setupTestFields() { + $this->fieldName = 'field_test_et_ui_test'; + + $field = array( + 'field_name' => $this->fieldName, + 'type' => 'text', + 'cardinality' => 1, + 'translatable' => TRUE, + ); + field_create_field($field); + + $instance = array( + 'entity_type' => $this->entityType, + 'field_name' => $this->fieldName, + 'bundle' => $this->bundle, + 'label' => 'Test translatable text-field', + 'widget' => array( + 'type' => 'text_textfield', + 'weight' => 0, + ), + ); + field_create_instance($instance); + } + + /** + * Creates the entity to be translated. + * + * @param array $values + * An array of initial values for the entity. + * @param string $langcode + * The initial language code of the entity. + * @param string $bundle_name + * (optional) The entity bundle, if the entity uses bundles. Defaults to + * NULL. If left NULL, $this->bundle will be used. + * + * @return + * The entity id. + */ + protected function createEntity($values, $langcode, $bundle_name = NULL) { + $entity_values = $values; + $entity_values['langcode'] = $langcode; + $info = entity_get_info($this->entityType); + if (!empty($info['entity_keys']['bundle'])) { + $entity_values[$info['entity_keys']['bundle']] = $bundle_name ?: $this->bundle; + } + $controller = $this->container->get('plugin.manager.entity')->getStorageController($this->entityType); + if (!($controller instanceof DatabaseStorageControllerNG)) { + foreach ($values as $property => $value) { + if (is_array($value)) { + $entity_values[$property] = array($langcode => $value); + } + } + } + $entity = entity_create($this->entityType, $entity_values); + $entity->save(); + return $entity->id(); + } + +} diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php index 8c2dcefc55d..1aa23c4cbd6 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php @@ -7,45 +7,14 @@ namespace Drupal\translation_entity\Tests; -use Drupal\Core\Entity\DatabaseStorageControllerNG; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityNG; -use Drupal\Core\Language\Language; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\simpletest\WebTestBase; /** * Tests the Entity Translation UI. */ -abstract class EntityTranslationUITest extends WebTestBase { - - /** - * The enabled languages. - * - * @var array - */ - protected $langcodes; - - /** - * The entity type being tested. - * - * @var string - */ - protected $entityType; - - /** - * The bundle being tested. - * - * @var string - */ - protected $bundle; - - /** - * The name of the field used to test translation. - * - * @var string - */ - protected $fieldName; +abstract class EntityTranslationUITest extends EntityTranslationTestBase { /** * Whether the behavior of the language selector should be tested. @@ -54,92 +23,6 @@ abstract class EntityTranslationUITest extends WebTestBase { */ protected $testLanguageSelector = TRUE; - - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ - function setUp() { - parent::setUp(); - - $this->setupLanguages(); - $this->setupBundle(); - $this->enableTranslation(); - $this->setupTranslator(); - $this->setupTestFields(); - } - - /** - * Enables additional languages. - */ - protected function setupLanguages() { - $this->langcodes = array('it', 'fr'); - foreach ($this->langcodes as $langcode) { - language_save(new Language(array('langcode' => $langcode))); - } - array_unshift($this->langcodes, language_default()->langcode); - } - - /** - * Creates or initializes the bundle date if needed. - */ - protected function setupBundle() { - if (empty($this->bundle)) { - $this->bundle = $this->entityType; - } - } - - /** - * Enables translation for the current entity type and bundle. - */ - protected function enableTranslation() { - // Enable translation for the current entity type and ensure the change is - // picked up. - translation_entity_set_config($this->entityType, $this->bundle, 'enabled', TRUE); - drupal_static_reset(); - entity_info_cache_clear(); - menu_router_rebuild(); - } - - /** - * Returns an array of permissions needed for the translator. - */ - abstract function getTranslatorPermissions(); - - /** - * Creates and activates a translator user. - */ - protected function setupTranslator() { - $translator = $this->drupalCreateUser($this->getTranslatorPermissions()); - $this->drupalLogin($translator); - } - - /** - * Creates the test fields. - */ - protected function setupTestFields() { - $this->fieldName = 'field_test_et_ui_test'; - - $field = array( - 'field_name' => $this->fieldName, - 'type' => 'text', - 'cardinality' => 1, - 'translatable' => TRUE, - ); - field_create_field($field); - - $instance = array( - 'entity_type' => $this->entityType, - 'field_name' => $this->fieldName, - 'bundle' => $this->bundle, - 'label' => 'Test translatable text-field', - 'widget' => array( - 'type' => 'text_textfield', - 'weight' => 0, - ), - ); - field_create_instance($instance); - } - /** * Tests the basic translation UI. */ @@ -163,8 +46,7 @@ abstract class EntityTranslationUITest extends WebTestBase { $langcode = 'it'; $values[$langcode] = $this->getNewEntityValues($langcode); - $controller = translation_entity_controller($this->entityType); - $base_path = $controller->getBasePath($entity); + $base_path = $this->controller->getBasePath($entity); $path = $langcode . '/' . $base_path . '/translations/add/' . $default_langcode . '/' . $langcode; $this->drupalPost($path, $this->getEditValues($values, $langcode), t('Save')); if ($this->testLanguageSelector) { @@ -200,7 +82,7 @@ abstract class EntityTranslationUITest extends WebTestBase { // Check that every translation has the correct "outdated" status. foreach ($this->langcodes as $enabled_langcode) { $prefix = $enabled_langcode != $default_langcode ? $enabled_langcode . '/' : ''; - $path = $prefix . $controller->getEditPath($entity); + $path = $prefix . $this->controller->getEditPath($entity); $this->drupalGet($path); if ($enabled_langcode == $langcode) { $this->assertFieldByXPath('//input[@name="translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.'); @@ -226,40 +108,6 @@ abstract class EntityTranslationUITest extends WebTestBase { } } - /** - * Creates the entity to be translated. - * - * @param array $values - * An array of initial values for the entity. - * @param string $langcode - * The initial language code of the entity. - * @param string $bundle_name - * (optional) The entity bundle, if the entity uses bundles. Defaults to - * NULL. If left NULL, $this->bundle will be used. - * - * @return - * The entity id. - */ - protected function createEntity($values, $langcode, $bundle_name = NULL) { - $entity_values = $values; - $entity_values['langcode'] = $langcode; - $info = entity_get_info($this->entityType); - if (!empty($info['entity_keys']['bundle'])) { - $entity_values[$info['entity_keys']['bundle']] = $bundle_name ?: $this->bundle; - } - $controller = $this->container->get('plugin.manager.entity')->getStorageController($this->entityType); - if (!($controller instanceof DatabaseStorageControllerNG)) { - foreach ($values as $property => $value) { - if (is_array($value)) { - $entity_values[$property] = array($langcode => $value); - } - } - } - $entity = entity_create($this->entityType, $entity_values); - $entity->save(); - return $entity->id(); - } - /** * Returns an array of entity field values to be tested. */ diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationWorkflowsTest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationWorkflowsTest.php new file mode 100644 index 00000000000..7ffe6c08d5c --- /dev/null +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationWorkflowsTest.php @@ -0,0 +1,185 @@ + 'Entity Test translation workflows', + 'description' => 'Tests the entity translation workflows for the test entity.', + 'group' => 'Entity Translation UI', + ); + } + + function setUp() { + parent::setUp(); + $this->setupEntity(); + } + + /** + * Overrides \Drupal\translation_entity\Tests\EntityTranslationTestBase::getEditorPermissions(). + */ + protected function getEditorPermissions() { + return array('administer entity_test content'); + } + + /** + * Creates a test entity and translate it. + */ + protected function setupEntity() { + $default_langcode = $this->langcodes[0]; + + // Create a test entity. + $values = array( + 'name' => $this->randomName(), + 'user_id' => mt_rand(1, 128), + $this->fieldName => array(array('value' => $this->randomName(16))), + ); + $id = $this->createEntity($values, $default_langcode); + $this->entity = entity_load($this->entityType, $id, TRUE); + + // Create a translation. + $this->drupalLogin($this->translator); + $add_translation_path = $this->controller->getBasePath($this->entity) . "/translations/add/$default_langcode/{$this->langcodes[2]}"; + $this->drupalPost($add_translation_path, array(), t('Save')); + } + + /** + * Test simple and editorial translation workflows. + */ + function testWorkflows() { + // Test workflows for the editor. + $expected_status = array('edit' => 200, 'overview' => 403, 'add_translation' => 403, 'edit_translation' => 403); + $this->assertWorkflows($this->editor, $expected_status); + + // Test workflows for the translator. + $expected_status = array('edit' => 403, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 200); + $this->assertWorkflows($this->translator, $expected_status); + + // Test workflows for the admin. + $expected_status = array('edit' => 200, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 200); + $this->assertWorkflows($this->administrator, $expected_status); + + // Check that translation permissions governate the associated operations. + $ops = array('create' => t('add'), 'update' => t('edit'), 'delete' => t('delete')); + $translations_path = $this->controller->getBasePath($this->entity) . "/translations"; + foreach ($ops as $current_op => $label) { + $user = $this->drupalCreateUser(array($this->getTranslatePermission(), "$current_op entity translations")); + $this->drupalLogin($user); + $this->drupalGet($translations_path); + + foreach ($ops as $op => $label) { + if ($op != $current_op) { + $this->assertNoLink($label, format_string('No %op link found.', array('%op' => $label))); + } + else { + $this->assertLink($label, 0, format_string('%op link found.', array('%op' => $label))); + } + } + } + } + + /** + * Checks that workflows have the expected behaviors for the given user. + * + * @param \Drupal\user\Plugin\Core\Entity\User $user + * The user to test the workflow behavior against. + * @param array $expected_status + * The an associative array with the operation name as key and the expected + * status as value. + */ + protected function assertWorkflows(User $user, $expected_status) { + $default_langcode = $this->langcodes[0]; + $languages = language_list(); + $args = array('@user_label' => $user->name); + $this->drupalLogin($user); + + // Check whether the user is allowed to access the entity form in edit mode. + $edit_path = $this->controller->getEditPath($this->entity); + $options = array('language' => $languages[$default_langcode]); + $this->drupalGet($edit_path, $options); + $this->assertResponse($expected_status['edit'], format_string('The @user_label has the expected edit access.', $args)); + + // Check whether the user is allowed to access the translation overview. + $langcode = $this->langcodes[1]; + $translations_path = $this->controller->getBasePath($this->entity) . "/translations"; + $options = array('language' => $languages[$langcode]); + $this->drupalGet($translations_path, $options); + $this->assertResponse($expected_status['overview'], format_string('The @user_label has the expected translation overview access.', $args)); + + // Check whether the user is allowed to create a translation. + $add_translation_path = $translations_path . "/add/$default_langcode/$langcode"; + if ($expected_status['add_translation'] == 200) { + $this->clickLink('add'); + $this->assertUrl($add_translation_path, $options, 'The translation overview points to the translation form when creating translations.'); + // Check that the translation form does not contain shared elements for + // translators. + if ($expected_status['edit'] == 403) { + $this->assertNoSharedElements(); + } + } + else { + $this->drupalGet($add_translation_path, $options); + } + $this->assertResponse($expected_status['add_translation'], format_string('The @user_label has the expected translation creation access.', $args)); + + // Check whether the user is allowed to edit a translation. + $langcode = $this->langcodes[2]; + $edit_translation_path = $translations_path . "/edit/$langcode"; + $options = array('language' => $languages[$langcode]); + if ($expected_status['edit_translation'] == 200) { + $this->drupalGet($translations_path, $options); + $editor = $expected_status['edit'] == 200; + $this->clickLink('edit', intval($editor)); + + if ($editor) { + // An editor should be pointed to the entity form in multilingual mode. + $this->assertUrl($edit_path, $options, 'The translation overview points to the edit form for editors when editing translations.'); + } + else { + // While a translator should be pointed to the translation form. + $this->assertUrl($edit_translation_path, $options, 'The translation overview points to the translation form for translators when editing translations.'); + // Check that the translation form does not contain shared elements. + $this->assertNoSharedElements(); + } + } + else { + $this->drupalGet($edit_translation_path, $options); + } + $this->assertResponse($expected_status['edit_translation'], format_string('The @user_label has the expected translation creation access.', $args)); + } + + /** + * Assert that the current page does not contain shared form elements. + */ + protected function assertNoSharedElements() { + $language_none = LANGUAGE_NOT_SPECIFIED; + return $this->assertNoFieldByXPath("//input[@name='field_test_text[$language_none][0][value]']", NULL, 'Shared elements are not available on the translation form.'); + } + +} diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/Views/TranslationLinkTest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/Views/TranslationLinkTest.php index 72cdd46ac5b..4c7868b4dcc 100644 --- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/Views/TranslationLinkTest.php +++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/Views/TranslationLinkTest.php @@ -8,7 +8,7 @@ namespace Drupal\translation_entity\Tests\Views; use Drupal\views\Tests\ViewTestBase; -use Drupal\translation_entity\Tests\EntityTranslationUITest; +use Drupal\translation_entity\Tests\EntityTranslationTestBase; use Drupal\views\Tests\ViewTestData; /** @@ -16,7 +16,7 @@ use Drupal\views\Tests\ViewTestData; * * @see \Drupal\translation_entity\Plugin\views\field\TranslationLink */ -class TranslationLinkTest extends EntityTranslationUITest { +class TranslationLinkTest extends EntityTranslationTestBase { /** * Views used by this test. @@ -49,13 +49,6 @@ class TranslationLinkTest extends EntityTranslationUITest { ViewTestData::importTestViews(get_class($this), array('translation_entity_test_views')); } - /** - * Implements \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). - */ - function getTranslatorPermissions() { - return array("translate $this->entityType entities", 'edit original values'); - } - /** * Tests the Entity translation overview link field handler. */ @@ -65,12 +58,4 @@ class TranslationLinkTest extends EntityTranslationUITest { $this->assertNoLinkByHref('user/2/translations', 'The translations link is not present when translation_entity_translate_access() is FALSE.'); } - /** - * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::testTranslationUI(). - */ - public function testTranslationUI() { - // @todo \Drupal\translation_entity\Tests\EntityTranslationUITest contains - // essential helper methods that should be seprarated from test methods. - } - } diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module index ee16a9c8a7b..cdaf9d29a26 100644 --- a/core/modules/translation_entity/translation_entity.module +++ b/core/modules/translation_entity/translation_entity.module @@ -155,18 +155,37 @@ function translation_entity_menu() { 'weight' => 2, ) + $item; + $items["$path/translations/overview"] = array( + 'title' => 'Overview', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 0, + ); + // Add translation callback. // @todo Add the access callback instead of replacing it as soon as the // routing system supports multiple callbacks. - $add_path = "$path/translations/add/%language/%language"; $language_position = $entity_position + 3; $args = array($entity_position, $language_position, $language_position + 1); - $items[$add_path] = array( + $items["$path/translations/add/%language/%language"] = array( 'title' => 'Add', 'page callback' => 'translation_entity_add_page', 'page arguments' => $args, 'access callback' => 'translation_entity_add_access', 'access arguments' => $args, + 'type' => MENU_LOCAL_TASK, + 'weight' => 1, + ) + $item; + + // Edit translation callback. + $args = array($entity_position, $language_position); + $items["$path/translations/edit/%language"] = array( + 'title' => 'Edit', + 'page callback' => 'translation_entity_edit_page', + 'page arguments' => $args, + 'access callback' => 'translation_entity_edit_access', + 'access arguments' => $args, + 'type' => MENU_LOCAL_TASK, + 'weight' => 1, ) + $item; // Delete translation callback. @@ -174,6 +193,8 @@ function translation_entity_menu() { 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('translation_entity_delete_confirm', $entity_position, $language_position), + 'access callback' => 'translation_entity_delete_access', + 'access arguments' => $args, ) + $item; } } @@ -257,7 +278,10 @@ function _translation_entity_menu_strip_loaders($path) { */ function translation_entity_translate_access(EntityInterface $entity) { $entity_type = $entity->entityType(); - return empty($entity->language()->locked) && language_multilingual() && translation_entity_enabled($entity_type, $entity->bundle()) && (user_access('translate any entity') || user_access("translate $entity_type entities")); + return empty($entity->language()->locked) && + language_multilingual() && + translation_entity_enabled($entity_type, $entity->bundle()) && + (user_access('create entity translations') || user_access('update entity translations') || user_access('delete entity translations')); } /** @@ -266,16 +290,50 @@ function translation_entity_translate_access(EntityInterface $entity) { * @param \Drupal\Core\Entity\EntityInterface $entity * The entity being translated. * @param \Drupal\Core\Language\Language $source - * The language of the values being translated. + * (optional) The language of the values being translated. Defaults to the + * entity language. * @param \Drupal\Core\Language\Language $target - * The language of the translated values. + * (optional) The language of the translated values. Defaults to the current + * content language. */ function translation_entity_add_access(EntityInterface $entity, Language $source = NULL, Language $target = NULL) { $source = !empty($source) ? $source : $entity->language(); $target = !empty($target) ? $target : language(LANGUAGE_TYPE_CONTENT); $translations = $entity->getTranslationLanguages(); $languages = language_list(); - return $source->langcode != $target->langcode && isset($languages[$source->langcode]) && isset($languages[$target->langcode]) && !isset($translations[$target->langcode]) && translation_entity_access($entity, $target->langcode); + return $source->langcode != $target->langcode && isset($languages[$source->langcode]) && isset($languages[$target->langcode]) && !isset($translations[$target->langcode]) && translation_entity_access($entity, 'create'); +} + +/** + * Access callback for the translation edit page. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being translated. + * @param \Drupal\Core\Language\Language $language + * (optional) The language of the translated values. Defaults to the current + * content language. + */ +function translation_entity_edit_access(EntityInterface $entity, Language $language = NULL) { + $language = !empty($language) ? $language : language(LANGUAGE_TYPE_CONTENT); + $translations = $entity->getTranslationLanguages(); + $languages = language_list(); + return isset($languages[$language->langcode]) && $language->langcode != $entity->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'update'); +} + +/** + * Access callback for the translation delete page. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being translated. + * @param \Drupal\Core\Language\Language $language + * (optional) The language of the translated values. Defaults to the current + * content language. + */ +function translation_entity_delete_access(EntityInterface $entity, Language $language = NULL) { + $language = !empty($language) ? $language : language(LANGUAGE_TYPE_CONTENT); + $translations = $entity->getTranslationLanguages(); + $languages = language_list(); + return isset($languages[$language->langcode]) && $language->langcode != $entity->language()->langcode && isset($translations[$language->langcode]) && translation_entity_access($entity, 'delete'); } /** @@ -454,14 +512,18 @@ function translation_entity_form_controller(array $form_state) { * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity to be accessed. - * @param string $langcode - * The language of the translation to be accessed. + * @param $op + * The operation to be performed on the translation. Possible values are: + * - "view" + * - "update" + * - "delete" + * - "create" * * @return * TRUE if the current user is allowed to view the translation. */ -function translation_entity_access(EntityInterface $entity, $langcode) { - return translation_entity_controller($entity->entityType())->getTranslationAccess($entity, $langcode) ; +function translation_entity_access(EntityInterface $entity, $op) { + return translation_entity_controller($entity->entityType())->getTranslationAccess($entity, $op) ; } /** @@ -469,27 +531,50 @@ function translation_entity_access(EntityInterface $entity, $langcode) { */ function translation_entity_permission() { $permission = array( - 'edit original values' => array( - 'title' => t('Edit original values'), - 'description' => t('Access the entity form in the original language.'), - ), 'administer entity translation' => array( - 'title' => t('Administer entity translation'), + 'title' => t('Administer translation settings'), 'description' => t('Configure translatability of entities and fields.'), ), + 'create entity translations' => array( + 'title' => t('Create translations'), + ), + 'update entity translations' => array( + 'title' => t('Edit translations'), + ), + 'delete entity translations' => array( + 'title' => t('Delete translations'), + ), 'translate any entity' => array( 'title' => t('Translate any entity'), - 'description' => t('Translate field content for any fieldable entity.'), ), ); + // Create a translate permission for each enabled entity type and (optionally) + // bundle. foreach (entity_get_info() as $entity_type => $info) { - if (translation_entity_enabled($entity_type)) { - $label = !empty($info['label']) ? t($info['label']) : $entity_type; - $permission["translate $entity_type entities"] = array( - 'title' => t('Translate entities of type @type', array('@type' => $label)), - 'description' => t('Translate field content for entities of type @type.', array('@type' => $label)), - ); + if (!empty($info['permission_granularity'])) { + $t_args = array('@entity_label' => drupal_strtolower(t($info['label']))); + + switch ($info['permission_granularity']) { + case 'bundle': + foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) { + if (translation_entity_enabled($entity_type, $bundle)) { + $t_args['%bundle_label'] = isset($info['bundles'][$bundle]['label']) ? $info['bundles'][$bundle]['label'] : $bundle; + $permission["translate $bundle $entity_type"] = array( + 'title' => t('Translate %bundle_label @entity_label', $t_args), + ); + } + } + break; + + case 'entity_type': + if (translation_entity_enabled($entity_type)) { + $permission["translate $entity_type"] = array( + 'title' => t('Translate @entity_label', $t_args), + ); + } + break; + } } } diff --git a/core/modules/translation_entity/translation_entity.pages.inc b/core/modules/translation_entity/translation_entity.pages.inc index e2e76cc02b7..3111e01858c 100644 --- a/core/modules/translation_entity/translation_entity.pages.inc +++ b/core/modules/translation_entity/translation_entity.pages.inc @@ -51,11 +51,13 @@ function translation_entity_overview(EntityInterface $entity) { $language_name = $language->name; $langcode = $language->langcode; $add_path = $base_path . '/translations/add/' . $original . '/' . $langcode; + $translate_path = $base_path . '/translations/edit/' . $langcode; $delete_path = $base_path . '/translations/delete/' . $langcode; if ($base_path) { $add_links = _translation_entity_get_switch_links($add_path); $edit_links = _translation_entity_get_switch_links($edit_path); + $translate_links = _translation_entity_get_switch_links($translate_path); $delete_links = _translation_entity_get_switch_links($delete_path); } @@ -80,8 +82,17 @@ function translation_entity_overview(EntityInterface $entity) { $row_title = $is_original ? $label : t('n/a'); } - if ($edit_path && $controller->getAccess($entity, 'update') && $controller->getTranslationAccess($entity, $langcode)) { + // If the user is allowed to edit the entity we point the edit link to + // the entity form, otherwise if we are not dealing with the original + // language we point the link to the translation form. + if ($edit_path && $controller->getAccess($entity, 'update')) { $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $edit_path, 'language' => $language); + } + elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) { + $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array('href' => $translate_path, 'language' => $language); + } + + if (isset($links['edit'])) { $links['edit']['title'] = t('edit'); } @@ -98,8 +109,10 @@ function translation_entity_overview(EntityInterface $entity) { } else { $source_name = isset($languages[$source]) ? $languages[$source]->name : t('n/a'); - $links['delete'] = isset($delete_links->links[$langcode]['href']) ? $delete_links->links[$langcode] : array('href' => $delete_links, 'language' => $language); - $links['delete']['title'] = t('delete'); + if ($controller->getTranslationAccess($entity, 'delete')) { + $links['delete'] = isset($delete_links->links[$langcode]['href']) ? $delete_links->links[$langcode] : array('href' => $delete_links, 'language' => $language); + $links['delete']['title'] = t('delete'); + } } } else { @@ -107,7 +120,7 @@ function translation_entity_overview(EntityInterface $entity) { $row_title = $source_name = t('n/a'); $source = $entity->language()->langcode; - if ($source != $langcode && $controller->getAccess($entity, 'update')) { + if ($source != $langcode && $controller->getTranslationAccess($entity, 'create')) { if ($translatable) { $links['add'] = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array('href' => $add_path, 'language' => $language); $links['add']['title'] = t('add'); @@ -188,6 +201,30 @@ function translation_entity_add_page(EntityInterface $entity, Language $source = $form_state = entity_form_state_defaults($entity, $operation, $target->langcode); $form_state['translation_entity']['source'] = $source; $form_state['translation_entity']['target'] = $target; + $controller = translation_entity_controller($entity->entityType()); + $form_state['translation_entity']['translation_form'] = !$controller->getAccess($entity, 'update'); + $form_id = entity_form_id($entity); + return drupal_build_form($form_id, $form_state); +} + +/** + * Page callback for the translation edit page. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being translated. + * @param \Drupal\Core\Language\Language $language + * (optional) The language of the translated values. Defaults to the current + * content language. + * + * @return array + * A processed form array ready to be rendered. + */ +function translation_entity_edit_page(EntityInterface $entity, Language $language = NULL) { + $language = !empty($language) ? $language : language(LANGUAGE_TYPE_CONTENT); + $info = $entity->entityInfo(); + $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default'; + $form_state = entity_form_state_defaults($entity, $operation, $language->langcode); + $form_state['translation_entity']['translation_form'] = TRUE; $form_id = entity_form_id($entity); return drupal_build_form($form_id, $form_state); } @@ -256,7 +293,8 @@ function translation_entity_delete_confirm_submit(array $form, array &$form_stat // Remove any existing path alias for the removed translation. if (module_exists('path')) { - path_delete(array('source' => $controller->getViewPath($entity), 'langcode' => $language->langcode)); + $conditions = array('source' => $controller->getViewPath($entity), 'langcode' => $language->langcode); + drupal_container()->get('path.crud')->delete($conditions); } $form_state['redirect'] = $controller->getBasePath($entity) . '/translations'; diff --git a/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php b/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php index 1b6160adcc2..3b97557fd42 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserTranslationUITest.php @@ -34,9 +34,6 @@ class UserTranslationUITest extends EntityTranslationUITest { ); } - /** - * Overrides \Drupal\simpletest\WebTestBase::setUp(). - */ function setUp() { $this->entityType = 'user'; $this->testLanguageSelector = FALSE; @@ -48,7 +45,7 @@ class UserTranslationUITest extends EntityTranslationUITest { * Overrides \Drupal\translation_entity\Tests\EntityTranslationUITest::getTranslatorPermission(). */ function getTranslatorPermissions() { - return array('administer users', "translate $this->entityType entities", 'edit original values'); + return array_merge(parent::getTranslatorPermissions(), array('administer users')); } /** @@ -63,7 +60,7 @@ class UserTranslationUITest extends EntityTranslationUITest { * Tests translate link on user admin list. */ function testTranslateLinkUserAdminPage() { - $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer users', 'translate any entity')); + $this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer users'))); $this->drupalLogin($this->admin_user); $uid = $this->createEntity(array('name' => $this->randomName()), $this->langcodes[0]);