Issue #3041659 by tedbow, tim.plunkett, amateescu, plach, xjm, effulgentsia, Wim Leers, webchick: Remove the layout tab from translations because Layout Builder does not support translations yet

merge-requests/1119/head
effulgentsia 2019-03-26 21:47:30 -07:00
parent 73af6069c2
commit cbf7788676
8 changed files with 354 additions and 155 deletions

View File

@ -355,3 +355,15 @@ function layout_builder_quickedit_render_field(EntityInterface $entity, $field_n
$quick_edit_integration = \Drupal::classResolver(QuickEditIntegration::class);
return $quick_edit_integration->quickEditRenderField($entity, $field_name, $view_mode_id, $langcode);
}
/**
* Implements hook_entity_translation_create().
*/
function layout_builder_entity_translation_create(EntityInterface $translation) {
/** @var \Drupal\Core\Entity\FieldableEntityInterface $translation */
if ($translation->hasField(OverridesSectionStorage::FIELD_NAME) && $translation->getFieldDefinition(OverridesSectionStorage::FIELD_NAME)->isTranslatable()) {
// When creating a new translation do not copy untranslated sections because
// per-language layouts are not supported.
$translation->set(OverridesSectionStorage::FIELD_NAME, []);
}
}

View File

@ -207,6 +207,7 @@ class LayoutBuilderEntityViewDisplay extends BaseEntityViewDisplay implements La
'type' => 'layout_section',
'locked' => TRUE,
]);
$field_storage->setTranslatable(FALSE);
$field_storage->save();
}
@ -215,6 +216,7 @@ class LayoutBuilderEntityViewDisplay extends BaseEntityViewDisplay implements La
'bundle' => $bundle,
'label' => t('Layout'),
]);
$field->setTranslatable(FALSE);
$field->save();
}
}

View File

@ -397,9 +397,29 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
// Access also depends on the default being enabled.
$result = $result->andIf($this->getDefaultSectionStorage()->access($operation, $account, TRUE));
$result = $this->handleTranslationAccess($result, $operation, $account);
return $return_as_object ? $result : $result->isAllowed();
}
/**
* Handles access checks related to translations.
*
* @param \Drupal\Core\Access\AccessResult $result
* The access result.
* @param string $operation
* The operation to be performed.
* @param \Drupal\Core\Session\AccountInterface $account
* The user for which to check access.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
protected function handleTranslationAccess(AccessResult $result, $operation, AccountInterface $account) {
$entity = $this->getEntity();
// Access is always denied on non-default translations.
return $result->andIf(AccessResult::allowedIf(!($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation())))->addCacheableDependency($entity);
}
/**
* {@inheritdoc}
*/

View File

@ -1,110 +0,0 @@
<?php
namespace Drupal\Tests\layout_builder\Functional;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\Tests\BrowserTestBase;
/**
* Tests Layout Builder functionality with multiple languages installed.
*
* @group layout_builder
*/
class LayoutBuilderMultilingualTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'layout_builder',
'node',
'block_content',
'content_translation',
'locale',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// There must be more than one block type available to trigger
// \Drupal\layout_builder\Controller\ChooseBlockController::inlineBlockList().
BlockContentType::create([
'id' => 'first_type',
'label' => 'First type',
])->save();
BlockContentType::create([
'id' => 'second_type',
'label' => 'Second type',
])->save();
// Create a translatable content type with layout overrides enabled.
$this->createContentType([
'type' => 'bundle_with_section_field',
]);
$this->container->get('content_translation.manager')->setEnabled('node', 'bundle_with_section_field', TRUE);
LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default')
->enableLayoutBuilder()
->setOverridable()
->save();
// Create a second language.
ConfigurableLanguage::createFromLangcode('es')->save();
// Create a node and translate it.
$node = $this->createNode([
'type' => 'bundle_with_section_field',
'title' => 'The untranslated node title',
]);
$node->addTranslation('es', [
'title' => 'The translated node title',
]);
$node->save();
$this->drupalLogin($this->createUser([
'configure any layout',
'translate interface',
'create and edit custom blocks',
]));
}
/**
* Tests that custom blocks are available for translated entities.
*/
public function testCustomBlocks() {
// Check translated and untranslated entities before translating the string.
$this->assertCustomBlocks('node/1/layout');
$this->assertCustomBlocks('es/node/1/layout');
// Translate the 'Inline blocks' string used as a category in
// \Drupal\layout_builder\Controller\ChooseBlockController::inlineBlockList().
$this->drupalPostForm('admin/config/regional/translate', ['string' => 'Inline blocks'], 'Filter');
$this->drupalPostForm(NULL, ['Translated string (Spanish)' => 'Bloques en linea'], 'Save translations');
// Check translated and untranslated entities after translating the string.
$this->assertCustomBlocks('node/1/layout');
$this->assertCustomBlocks('es/node/1/layout');
}
/**
* Asserts that custom blocks are available.
*
* @param string $url
* The URL for a Layout Builder enabled entity.
*/
protected function assertCustomBlocks($url) {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$this->drupalGet($url);
$page->clickLink('Add Block');
$page->clickLink('Create custom block');
$assert_session->linkExists('First type');
$assert_session->linkExists('Second type');
}
}

View File

@ -0,0 +1,218 @@
<?php
namespace Drupal\Tests\layout_builder\Functional;
use Drupal\Tests\content_translation\Functional\ContentTranslationTestBase;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Url;
/**
* Tests that the Layout Builder works with translated content.
*
* @group layout_builder
*/
class LayoutBuilderTranslationTest extends ContentTranslationTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'content_translation',
'contextual',
'entity_test',
'layout_builder',
'block',
];
/**
* The entity used for testing.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->setUpViewDisplay();
$this->setUpEntities();
}
/**
* Tests that layout overrides work when created after a translation.
*/
public function testTranslationBeforeLayoutOverride() {
$assert_session = $this->assertSession();
$this->addEntityTranslation();
$entity_url = $this->entity->toUrl()->toString();
$language = \Drupal::languageManager()->getLanguage($this->langcodes[2]);
$translated_entity_url = $this->entity->toUrl('canonical', ['language' => $language])->toString();
$translated_layout_url = $translated_entity_url . '/layout';
$this->drupalGet($entity_url);
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
$assert_session->linkExists('Layout');
$this->drupalGet($translated_entity_url);
$assert_session->pageTextNotContains('The untranslated field value');
$assert_session->pageTextContains('The translated field value');
$assert_session->linkNotExists('Layout');
$this->drupalGet($translated_layout_url);
$assert_session->pageTextContains('Access denied');
$this->addLayoutOverride();
$this->drupalGet($entity_url);
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
$assert_session->pageTextContains('Powered by Drupal');
// Ensure that the layout change propagates to the translated entity.
$this->drupalGet($translated_entity_url);
$assert_session->pageTextNotContains('The untranslated field value');
$assert_session->pageTextContains('The translated field value');
$assert_session->pageTextContains('Powered by Drupal');
}
/**
* Tests that layout overrides work when created before a translation.
*/
public function testLayoutOverrideBeforeTranslation() {
$assert_session = $this->assertSession();
$entity_url = $this->entity->toUrl()->toString();
$language = \Drupal::languageManager()->getLanguage($this->langcodes[2]);
$this->addLayoutOverride();
$this->drupalGet($entity_url);
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
$assert_session->pageTextContains('Powered by Drupal');
$assert_session->linkExists('Layout');
$this->addEntityTranslation();
$translated_entity_url = $this->entity->toUrl('canonical', ['language' => $language])->toString();
$translated_layout_url = $translated_entity_url . '/layout';
$this->drupalGet($entity_url);
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
$assert_session->pageTextContains('Powered by Drupal');
$assert_session->linkExists('Layout');
$this->drupalGet($translated_entity_url);
$assert_session->pageTextNotContains('The untranslated field value');
$assert_session->pageTextContains('The translated field value');
$assert_session->pageTextContains('Powered by Drupal');
$assert_session->linkNotExists('Layout');
$this->drupalGet($translated_layout_url);
$assert_session->pageTextContains('Access denied');
}
/**
* {@inheritdoc}
*/
protected function getAdministratorPermissions() {
$permissions = parent::getAdministratorPermissions();
$permissions[] = 'administer entity_test_mul display';
return $permissions;
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
$permissions = parent::getTranslatorPermissions();
$permissions[] = 'view test entity translations';
$permissions[] = 'view test entity';
$permissions[] = 'configure any layout';
return $permissions;
}
/**
* Setup translated entity with layouts.
*/
protected function setUpEntities() {
$this->drupalLogin($this->administrator);
$field_ui_prefix = 'entity_test_mul/structure/entity_test_mul';
// Allow overrides for the layout.
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => TRUE], 'Save');
// @todo The Layout Builder UI relies on local tasks; fix in
// https://www.drupal.org/project/drupal/issues/2917777.
$this->drupalPlaceBlock('local_tasks_block');
// Create a test entity.
$id = $this->createEntity([
$this->fieldName => [['value' => 'The untranslated field value']],
], $this->langcodes[0]);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$id]);
$this->entity = $storage->load($id);
}
/**
* Set up the View Display.
*/
protected function setUpViewDisplay() {
EntityViewDisplay::create([
'targetEntityType' => $this->entityTypeId,
'bundle' => $this->bundle,
'mode' => 'default',
'status' => TRUE,
])->setComponent($this->fieldName, ['type' => 'string'])->save();
}
/**
* Adds an entity translation.
*/
protected function addEntityTranslation() {
$user = $this->loggedInUser;
$this->drupalLogin($this->translator);
$add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [
$this->entityTypeId => $this->entity->id(),
'source' => $this->langcodes[0],
'target' => $this->langcodes[2],
]);
$this->drupalPostForm($add_translation_url, [
"{$this->fieldName}[0][value]" => 'The translated field value',
], 'Save');
$this->drupalLogin($user);
}
/**
* Adds a layout override.
*/
protected function addLayoutOverride() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$entity_url = $this->entity->toUrl()->toString();
$layout_url = $entity_url . '/layout';
$this->drupalGet($layout_url);
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
// Adjust the layout.
$assert_session->linkExists('Add Block');
$this->clickLink('Add Block');
$assert_session->linkExists('Powered by Drupal');
$this->clickLink('Powered by Drupal');
$page->pressButton('Add Block');
$assert_session->pageTextContains('Powered by Drupal');
$assert_session->buttonExists('Save layout');
$page->pressButton('Save layout');
}
}

View File

@ -2,7 +2,6 @@
namespace Drupal\Tests\layout_builder\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
use Drupal\layout_builder\Section;
@ -199,50 +198,6 @@ class LayoutSectionTest extends BrowserTestBase {
$this->assertLayoutSection('.layout--onecol', 'Hello test world', '', '', 'UNCACHEABLE');
}
/**
* Tests the multilingual support of the section formatter.
*/
public function testMultilingualLayoutSectionFormatter() {
$this->container->get('module_installer')->install(['content_translation']);
$this->rebuildContainer();
ConfigurableLanguage::createFromLangcode('es')->save();
$this->container->get('content_translation.manager')->setEnabled('node', 'bundle_with_section_field', TRUE);
$entity = $this->createSectionNode([
[
'section' => new Section('layout_onecol', [], [
'baz' => new SectionComponent('baz', 'content', [
'id' => 'system_powered_by_block',
]),
]),
],
]);
$entity->addTranslation('es', [
'title' => 'Translated node title',
OverridesSectionStorage::FIELD_NAME => [
[
'section' => new Section('layout_twocol', [], [
'foo' => new SectionComponent('foo', 'first', [
'id' => 'test_block_instantiation',
'display_message' => 'foo text',
]),
'bar' => new SectionComponent('bar', 'second', [
'id' => 'test_block_instantiation',
'display_message' => 'bar text',
]),
]),
],
],
]);
$entity->save();
$this->drupalGet($entity->toUrl('canonical'));
$this->assertLayoutSection('.layout--onecol', 'Powered by');
$this->drupalGet($entity->toUrl('canonical')->setOption('prefix', 'es/'));
$this->assertLayoutSection('.layout--twocol', ['foo text', 'bar text']);
}
/**
* Ensures that the entity title is displayed.
*/

View File

@ -7,6 +7,7 @@ use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\layout_builder\DefaultsSectionStorageInterface;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
@ -34,6 +35,7 @@ class OverridesSectionStorageTest extends KernelTestBase {
'field',
'system',
'user',
'language',
];
/**
@ -97,6 +99,20 @@ class OverridesSectionStorageTest extends KernelTestBase {
$this->assertSame($expected, $result);
$result = $this->plugin->access('view', $account);
$this->assertSame($expected, $result);
// Create a translation.
ConfigurableLanguage::createFromLangcode('es')->save();
$entity = EntityTest::load($entity->id());
$translation = $entity->addTranslation('es');
$translation->save();
$this->plugin->setContext('entity', EntityContext::fromEntity($translation));
// Perform the same checks again but with a non default translation which
// should always deny access.
$result = $this->plugin->access('view');
$this->assertSame(FALSE, $result);
$result = $this->plugin->access('view', $account);
$this->assertSame(FALSE, $result);
}
/**

View File

@ -0,0 +1,86 @@
<?php
namespace Drupal\Tests\layout_builder\Kernel;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
/**
* Tests Layout Builder with a translatable layout field.
*
* @group layout_builder
*/
class TranslatableFieldTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'layout_discovery',
'layout_builder',
'entity_test',
'field',
'system',
'user',
'language',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['key_value_expire']);
$this->installEntitySchema('entity_test');
// Create a translation.
ConfigurableLanguage::createFromLangcode('es')->save();
LayoutBuilderEntityViewDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
'status' => TRUE,
])
->enableLayoutBuilder()
->setOverridable()
->save();
FieldStorageConfig::loadByName('entity_test', OverridesSectionStorage::FIELD_NAME)
->setTranslatable(TRUE)
->save();
FieldConfig::loadByName('entity_test', 'entity_test', OverridesSectionStorage::FIELD_NAME)
->setTranslatable(TRUE)
->save();
}
/**
* Tests that sections on cleared when creating a new translation.
*/
public function testSectionsClearedOnCreateTranslation() {
$section_data = [
new Section('layout_default', [], [
'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
]),
];
$entity = EntityTest::create([OverridesSectionStorage::FIELD_NAME => $section_data]);
$entity->save();
$this->assertFalse($entity->get(OverridesSectionStorage::FIELD_NAME)->isEmpty());
$entity = EntityTest::load($entity->id());
/** @var \Drupal\entity_test\Entity\EntityTest $translation */
$translation = $entity->addTranslation('es', $entity->toArray());
// Per-language layouts are not supported.
$this->assertTrue($translation->get(OverridesSectionStorage::FIELD_NAME)->isEmpty());
}
}