Issue #2476563 by Gábor Hojtsy, penyaskito, amateescu: Entity operations links tied to original entity language, ignore everything else

8.0.x
Nathaniel Catchpole 2015-10-02 11:24:37 +01:00
parent 400baf1609
commit 1ae0ed8796
8 changed files with 206 additions and 68 deletions

View File

@ -386,6 +386,8 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
* {@inheritdoc} * {@inheritdoc}
*/ */
public function urlInfo($rel = 'edit-form', array $options = []) { public function urlInfo($rel = 'edit-form', array $options = []) {
// Unless language was already provided, avoid setting an explicit language.
$options += ['language' => NULL];
return parent::urlInfo($rel, $options); return parent::urlInfo($rel, $options);
} }

View File

@ -0,0 +1,68 @@
<?php
/**
* @file
* Definition of Drupal\config\Tests\ConfigEntityListMultilingualTest.
*/
namespace Drupal\config\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the listing of configuration entities in a multilingual scenario.
*
* @group config
*/
class ConfigEntityListMultilingualTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('config_test', 'language', 'block');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Delete the override config_test entity. It is not required by this test.
\Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
ConfigurableLanguage::createFromLangcode('hu')->save();
$this->drupalPlaceBlock('local_actions_block');
}
/**
* Tests the listing UI with different language scenarios.
*/
function testListUI() {
// Log in as an administrative user to access the full menu trail.
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer site configuration')));
// Get the list page.
$this->drupalGet('admin/structure/config_test');
$this->assertLinkByHref('admin/structure/config_test/manage/dotted.default');
// Add a new entity using the action link.
$this->clickLink('Add test configuration');
$edit = array(
'label' => 'Antilop',
'id' => 'antilop',
'langcode' => 'hu',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
// Ensure that operations for editing the Hungarian entity appear in English.
$this->assertLinkByHref('admin/structure/config_test/manage/antilop');
// Get the list page in Hungarian and assert Hungarian admin links
// regardless of language of config entities.
$this->drupalGet('hu/admin/structure/config_test');
$this->assertLinkByHref('hu/admin/structure/config_test/manage/dotted.default');
$this->assertLinkByHref('hu/admin/structure/config_test/manage/antilop');
}
}

View File

@ -10,6 +10,7 @@ namespace Drupal\config_test;
use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
@ -126,6 +127,13 @@ class ConfigTestForm extends EntityForm {
'#access' => !empty($size), '#access' => !empty($size),
); );
$form['langcode'] = array(
'#type' => 'language_select',
'#title' => t('Language'),
'#languages' => LanguageInterface::STATE_ALL,
'#default_value' => $entity->language()->getId(),
);
$form['actions'] = array('#type' => 'actions'); $form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array( $form['actions']['submit'] = array(
'#type' => 'submit', '#type' => 'submit',

View File

@ -9,7 +9,9 @@ namespace Drupal\views\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\RedirectDestinationTrait; use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
use Drupal\views\ResultRow; use Drupal\views\ResultRow;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
@ -22,6 +24,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/ */
class EntityOperations extends FieldPluginBase { class EntityOperations extends FieldPluginBase {
use EntityTranslationRenderTrait;
use RedirectDestinationTrait; use RedirectDestinationTrait;
/** /**
@ -31,6 +34,13 @@ class EntityOperations extends FieldPluginBase {
*/ */
protected $entityManager; protected $entityManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/** /**
* Constructor. * Constructor.
* *
@ -42,10 +52,14 @@ class EntityOperations extends FieldPluginBase {
* The plugin implementation definition. * The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager. * The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/ */
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $entity_manager) { public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition); parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager; $this->entityManager = $entity_manager;
$this->languageManager = $language_manager;
} }
/** /**
@ -56,7 +70,8 @@ class EntityOperations extends FieldPluginBase {
$configuration, $configuration,
$plugin_id, $plugin_id,
$plugin_definition, $plugin_definition,
$container->get('entity.manager') $container->get('entity.manager'),
$container->get('language_manager')
); );
} }
@ -98,7 +113,7 @@ class EntityOperations extends FieldPluginBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function render(ResultRow $values) { public function render(ResultRow $values) {
$entity = $this->getEntity($values); $entity = $this->getEntityTranslation($this->getEntity($values), $values);
$operations = $this->entityManager->getListBuilder($entity->getEntityTypeId())->getOperations($entity); $operations = $this->entityManager->getListBuilder($entity->getEntityTypeId())->getOperations($entity);
if ($this->options['destination']) { if ($this->options['destination']) {
foreach ($operations as &$operation) { foreach ($operations as &$operation) {
@ -120,8 +135,39 @@ class EntityOperations extends FieldPluginBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function query() { public function query() {
// There is nothing to ensure or add for this handler, so we purposefully do // We purposefully do not call parent::query() because we do not want the
// nothing here and do not call parent::query() either. // default query behavior for Views fields. Instead, let the entity
// translation renderer provide the correct query behavior.
if ($this->languageManager->isMultilingual()) {
$this->getEntityTranslationRenderer()->query($this->query, $this->relationship);
}
}
/**
* {@inheritdoc}
*/
public function getEntityTypeId() {
return $this->getEntityType();
}
/**
* {@inheritdoc}
*/
protected function getEntityManager() {
return $this->entityManager;
}
/**
* {@inheritdoc}
*/
protected function getLanguageManager() {
return $this->languageManager;
}
/**
* {@inheritdoc}
*/
protected function getView() {
return $this->view;
} }
} }

View File

@ -9,6 +9,8 @@ namespace Drupal\views\Tests\Handler;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTest;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
/** /**
* Tests the core Drupal\views\Plugin\views\field\EntityOperations handler. * Tests the core Drupal\views\Plugin\views\field\EntityOperations handler.
@ -29,30 +31,62 @@ class FieldEntityOperationsTest extends HandlerTestBase {
* *
* @var array * @var array
*/ */
public static $modules = array('entity_test'); public static $modules = array('node', 'language');
function setUp() {
parent::setUp();
// Create Article content type.
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
/** /**
* Tests entity operations field. * Tests entity operations field.
*/ */
public function testEntityOperations() { public function testEntityOperations() {
// Create some test entities. // Add languages and refresh the container so the entity manager will have
// fresh data.
ConfigurableLanguage::createFromLangcode('hu')->save();
ConfigurableLanguage::createFromLangcode('es')->save();
$this->rebuildContainer();
// Create some test entities. Every other entity is Hungarian while all
// have a Spanish translation.
$entities = array();
for ($i = 0; $i < 5; $i++) { for ($i = 0; $i < 5; $i++) {
EntityTest::create(array( $entity = Node::create([
'name' => $this->randomString(), 'title' => $this->randomString(),
))->save(); 'type' => 'article',
'langcode' => $i % 2 === 0 ? 'hu' : 'en',
]);
$entity->save();
$translation = $entity->addTranslation('es');
$translation->set('title', $entity->getTitle() . ' in Spanish');
$translation->save();
$entities[$i] = $entity;
} }
$entities = EntityTest::loadMultiple();
$admin_user = $this->drupalCreateUser(array('access administration pages', 'administer entity_test content')); $admin_user = $this->drupalCreateUser(array('access administration pages', 'administer nodes', 'bypass node access'));
$this->drupalLogin($admin_user); $this->drupalLogin($this->rootUser);
$this->drupalGet('test-entity-operations'); $this->drupalGet('test-entity-operations');
/** @var $entity \Drupal\entity_test\Entity\EntityTest */
foreach ($entities as $entity) { foreach ($entities as $entity) {
$operations = \Drupal::entityManager()->getListBuilder('entity_test')->getOperations($entity); /** @var \Drupal\Core\Language\LanguageInterface $language */
foreach ($operations as $operation) { foreach ($entity->getTranslationLanguages() as $language) {
$expected_destination = Url::fromUri('internal:/test-entity-operations')->toString(); $entity = $entity->getTranslation($language->getId());
$result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[contains(@href, :path) and text()=:title]', array(':path' => $operation['url']->toString() . '?destination=' . $expected_destination, ':title' => $operation['title'])); $operations = \Drupal::entityManager()->getListBuilder('node')->getOperations($entity);
$this->assertEqual(count($result), 1, t('Found entity @operation link with destination parameter.', array('@operation' => $operation['title']))); $this->assertTrue(count($operations) > 0, 'There are operations.');
foreach ($operations as $operation) {
$expected_destination = Url::fromUri('internal:/test-entity-operations')->toString();
$result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and text()=:title]', array(':path' => $operation['url']->toString() . '?destination=' . $expected_destination, ':title' => $operation['title']));
$this->assertEqual(count($result), 1, t('Found entity @operation link with destination parameter.', array('@operation' => $operation['title'])));
// Entities which were created in Hungarian should link to the Hungarian
// edit form, others to the English one (which has no path prefix here).
$base_path = \Drupal::request()->getBasePath();
$parts = explode('/', str_replace($base_path, '', $operation['url']->toString()));
$expected_prefix = ($language->getId() != 'en' ? $language->getId() : 'node');
$this->assertEqual($parts[1], $expected_prefix, 'Entity operation links to the correct language for the entity.');
}
} }
} }
} }

View File

@ -2,13 +2,13 @@ langcode: en
status: true status: true
dependencies: dependencies:
module: module:
- entity_test - node
id: test_entity_operations id: test_entity_operations
label: test_entity_operations label: test_entity_operations
module: views module: views
description: '' description: ''
tag: '' tag: ''
base_table: entity_test base_table: node_field_data
base_field: id base_field: id
core: 8.x core: 8.x
display: display:
@ -35,60 +35,29 @@ display:
row: row:
type: fields type: fields
fields: fields:
name: title:
id: name id: title
table: entity_test table: node_field_data
field: name field: title
relationship: none label: Title
group_type: group
admin_label: ''
label: ''
exclude: false exclude: false
alter: alter:
alter_text: false alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: '' element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true element_default_classes: true
empty: '' empty: ''
hide_empty: false hide_empty: false
empty_zero: false empty_zero: false
hide_alter_empty: true hide_alter_empty: true
entity_type: entity_test entity_type: node
entity_field: name entity_field: title
type: string
settings:
link_to_entity: true
plugin_id: field plugin_id: field
operations: operations:
id: operations id: operations
table: entity_test table: node
field: operations field: operations
relationship: none relationship: none
group_type: group group_type: group

View File

@ -24,6 +24,13 @@ class EntityOperationsUnitTest extends UnitTestCase {
*/ */
protected $entityManager; protected $entityManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/** /**
* The plugin under test. * The plugin under test.
* *
@ -38,13 +45,14 @@ class EntityOperationsUnitTest extends UnitTestCase {
*/ */
public function setUp() { public function setUp() {
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
$configuration = array(); $configuration = array();
$plugin_id = $this->randomMachineName(); $plugin_id = $this->randomMachineName();
$plugin_definition = array( $plugin_definition = array(
'title' => $this->randomMachineName(), 'title' => $this->randomMachineName(),
); );
$this->plugin = new EntityOperations($configuration, $plugin_id, $plugin_definition, $this->entityManager); $this->plugin = new EntityOperations($configuration, $plugin_id, $plugin_definition, $this->entityManager, $this->languageManager);
$redirect_service = $this->getMock('Drupal\Core\Routing\RedirectDestinationInterface'); $redirect_service = $this->getMock('Drupal\Core\Routing\RedirectDestinationInterface');
$redirect_service->expects($this->any()) $redirect_service->expects($this->any())

View File

@ -66,10 +66,13 @@ class EntityUrlTest extends UnitTestCase {
$this->assertEquals($langcode, $uri->getOption('language')->getId()); $this->assertEquals($langcode, $uri->getOption('language')->getId());
} }
else { else {
// The expected langcode for a config entity is 'en', because it sets the if ($entity instanceof ConfigEntityInterface) {
// value as default property. // Config entities do not provide a language with their URIs.
$expected_langcode = $entity instanceof ConfigEntityInterface ? 'en' : LanguageInterface::LANGCODE_NOT_SPECIFIED; $this->assertEquals(NULL, $uri->getOption('language'));
$this->assertEquals($expected_langcode, $uri->getOption('language')->getId()); }
else {
$this->assertEquals(LanguageInterface::LANGCODE_NOT_SPECIFIED, $uri->getOption('language')->getId());
}
} }
} }