Revert "Issue #2595535 by tstoeckler, swentel, vijaycs85, alvar0hurtad0,

esolitos: Show helpful message (do not fatal!) when configuration files
have different source language codes and cannot be translated"uthor:
Nathaniel

This reverts commit ef3700fa23.
8.2.x
Nathaniel Catchpole 2016-07-11 15:56:49 +01:00
parent ef3700fa23
commit 7bbcc3ee67
8 changed files with 47 additions and 222 deletions

View File

@ -2,8 +2,6 @@
namespace Drupal\config_translation\Access; namespace Drupal\config_translation\Access;
use Drupal\config_translation\ConfigMapperInterface;
use Drupal\config_translation\Exception\ConfigMapperLanguageException;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
@ -14,68 +12,27 @@ use Drupal\Core\Session\AccountInterface;
class ConfigTranslationFormAccess extends ConfigTranslationOverviewAccess { class ConfigTranslationFormAccess extends ConfigTranslationOverviewAccess {
/** /**
* Checks access to the overview based on permissions and translatability. * {@inheritdoc}
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route_match to check against.
* @param \Drupal\Core\Session\AccountInterface $account
* The account to check access for.
* @param string $langcode
* The language code of the target language.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/ */
public function access(RouteMatchInterface $route_match, AccountInterface $account, $langcode = NULL) { public function access(RouteMatchInterface $route_match, AccountInterface $account, $langcode = NULL) {
$mapper = $this->getMapperFromRouteMatch($route_match); // For the translation forms we have a target language, so we need some
// checks in addition to the checks performed for the translation overview.
try { $base_access = parent::access($route_match, $account);
$source_langcode = $mapper->getLangcode(); if ($base_access->isAllowed()) {
$source_language = $this->languageManager->getLanguage($source_langcode);
$target_language = $this->languageManager->getLanguage($langcode); $target_language = $this->languageManager->getLanguage($langcode);
return $this->doCheckAccess($account, $mapper, $source_language, $target_language); // Make sure that the target language is not locked, and that the target
} // language is not the original submission language. Although technically
catch (ConfigMapperLanguageException $exception) { // configuration can be overlaid with translations in the same language,
return AccessResult::forbidden(); // that is logically not a good idea.
}
}
/**
* Checks access given an account, configuration mapper, and source language.
*
* In addition to the checks performed by
* ConfigTranslationOverviewAccess::doCheckAccess() this makes sure the target
* language is not locked and the target language is not the source language.
*
* Although technically configuration can be overlaid with translations in the
* same language, that is logically not a good idea.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The account to check access for.
* @param \Drupal\config_translation\ConfigMapperInterface $mapper
* The configuration mapper to check access for.
* @param \Drupal\Core\Language\LanguageInterface|null $source_language
* The source language to check for, if any.
* @param \Drupal\Core\Language\LanguageInterface|null $target_language
* The target language to check for, if any.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The result of the access check.
*
* @see \Drupal\config_translation\Access\ConfigTranslationOverviewAccess::doCheckAccess()
*/
protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL, $target_language = NULL) {
$base_access_result = parent::doCheckAccess($account, $mapper, $source_language);
$access = $access =
$target_language && !empty($target_language) &&
!$target_language->isLocked() && !$target_language->isLocked() &&
(!$source_language || ($target_language->getId() !== $source_language->getId())); (empty($this->sourceLanguage) || ($target_language->getId() != $this->sourceLanguage->getId()));
return $base_access_result->andIf(AccessResult::allowedIf($access));
return $base_access->andIf(AccessResult::allowedIf($access));
}
return $base_access;
} }
} }

View File

@ -2,8 +2,7 @@
namespace Drupal\config_translation\Access; namespace Drupal\config_translation\Access;
use Drupal\config_translation\ConfigMapperInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\config_translation\Exception\ConfigMapperLanguageException;
use Drupal\config_translation\ConfigMapperManagerInterface; use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Routing\Access\AccessInterface;
@ -29,6 +28,13 @@ class ConfigTranslationOverviewAccess implements AccessInterface {
*/ */
protected $languageManager; protected $languageManager;
/**
* The source language.
*
* @var \Drupal\Core\Language\LanguageInterface
*/
protected $sourceLanguage;
/** /**
* Constructs a ConfigTranslationOverviewAccess object. * Constructs a ConfigTranslationOverviewAccess object.
* *
@ -48,66 +54,28 @@ class ConfigTranslationOverviewAccess implements AccessInterface {
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route_match to check against. * The route_match to check against.
* @param \Drupal\Core\Session\AccountInterface $account * @param \Drupal\Core\Session\AccountInterface $account
* The account to check access for. * The currently logged in account.
* *
* @return \Drupal\Core\Access\AccessResultInterface * @return \Drupal\Core\Access\AccessResultInterface
* The access result. * The access result.
*/ */
public function access(RouteMatchInterface $route_match, AccountInterface $account) { public function access(RouteMatchInterface $route_match, AccountInterface $account) {
$mapper = $this->getMapperFromRouteMatch($route_match); $route = $route_match->getRouteObject();
try { /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
$langcode = $mapper->getLangcode(); $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
}
catch (ConfigMapperLanguageException $exception) {
// ConfigTranslationController shows a helpful message if the language
// codes do not match, so do not let that prevent granting access.
$langcode = 'en';
}
$source_language = $this->languageManager->getLanguage($langcode);
return $this->doCheckAccess($account, $mapper, $source_language);
}
/**
* Gets a configuration mapper using a route match.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match to populate the mapper with.
*
* @return \Drupal\config_translation\ConfigMapperInterface
* The configuration mapper.
*/
protected function getMapperFromRouteMatch(RouteMatchInterface $route_match) {
$mapper = $this->configMapperManager->createInstance($route_match->getRouteObject()
->getDefault('plugin_id'));
$mapper->populateFromRouteMatch($route_match); $mapper->populateFromRouteMatch($route_match);
return $mapper; $this->sourceLanguage = $this->languageManager->getLanguage($mapper->getLangcode());
}
/** // Allow access to the translation overview if the proper permission is
* Checks access given an account, configuration mapper, and source language. // granted, the configuration has translatable pieces, and the source
* // language is not locked if it is present.
* Grants access if the proper permission is granted to the account, the $source_language_access = is_null($this->sourceLanguage) || !$this->sourceLanguage->isLocked();
* configuration has translatable pieces, and the source language is not
* locked given it is present.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The account to check access for.
* @param \Drupal\config_translation\ConfigMapperInterface $mapper
* The configuration mapper to check access for.
* @param \Drupal\Core\Language\LanguageInterface|null $source_language
* The source language to check for, if any.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The result of the access check.
*/
protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL) {
$access = $access =
$account->hasPermission('translate configuration') && $account->hasPermission('translate configuration') &&
$mapper->hasSchema() && $mapper->hasSchema() &&
$mapper->hasTranslatable() && $mapper->hasTranslatable() &&
(!$source_language || !$source_language->isLocked()); $source_language_access;
return AccessResult::allowedIf($access)->cachePerPermissions(); return AccessResult::allowedIf($access)->cachePerPermissions();
} }

View File

@ -202,17 +202,6 @@ interface ConfigMapperInterface {
*/ */
public function getLangcode(); public function getLangcode();
/**
* Returns the language code of a configuration object given its name.
*
* @param string $config_name
* The name of the configuration object.
*
* @return string
* The language code of the configuration object.
*/
public function getLangcodeFromConfig($config_name);
/** /**
* Sets the original language code. * Sets the original language code.
* *

View File

@ -2,7 +2,6 @@
namespace Drupal\config_translation; namespace Drupal\config_translation;
use Drupal\config_translation\Exception\ConfigMapperLanguageException;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
@ -381,26 +380,22 @@ class ConfigNamesMapper extends PluginBase implements ConfigMapperInterface, Con
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getLangcode() { public function getLangcode() {
$langcodes = array_map([$this, 'getLangcodeFromConfig'], $this->getConfigNames()); $config_factory = $this->configFactory;
$langcodes = array_map(function($name) use ($config_factory) {
// Default to English if no language code was provided in the file.
// Although it is a best practice to include a language code, if the
// developer did not think about a multilingual use-case, we fall back
// on assuming the file is English.
return $config_factory->get($name)->get('langcode') ?: 'en';
}, $this->getConfigNames());
if (count(array_unique($langcodes)) > 1) { if (count(array_unique($langcodes)) > 1) {
throw new ConfigMapperLanguageException('A config mapper can only contain configuration for a single language.'); throw new \RuntimeException('A config mapper can only contain configuration for a single language.');
} }
return reset($langcodes); return reset($langcodes);
} }
/**
* {@inheritdoc}
*/
public function getLangcodeFromConfig($config_name) {
// Default to English if no language code was provided in the file.
// Although it is a best practice to include a language code, if the
// developer did not think about a multilingual use case, we fall back
// on assuming the file is English.
return $this->configFactory->get($config_name)->get('langcode') ?: 'en';
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@ -3,14 +3,11 @@
namespace Drupal\config_translation\Controller; namespace Drupal\config_translation\Controller;
use Drupal\config_translation\ConfigMapperManagerInterface; use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\config_translation\Exception\ConfigMapperLanguageException;
use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Language\Language; use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
@ -66,13 +63,6 @@ class ConfigTranslationController extends ControllerBase {
*/ */
protected $languageManager; protected $languageManager;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/** /**
* Constructs a ConfigTranslationController. * Constructs a ConfigTranslationController.
* *
@ -88,17 +78,14 @@ class ConfigTranslationController extends ControllerBase {
* The current user. * The current user.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager. * The language manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
*/ */
public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager, RendererInterface $renderer) { public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager) {
$this->configMapperManager = $config_mapper_manager; $this->configMapperManager = $config_mapper_manager;
$this->accessManager = $access_manager; $this->accessManager = $access_manager;
$this->router = $router; $this->router = $router;
$this->pathProcessor = $path_processor; $this->pathProcessor = $path_processor;
$this->account = $account; $this->account = $account;
$this->languageManager = $language_manager; $this->languageManager = $language_manager;
$this->renderer = $renderer;
} }
/** /**
@ -111,8 +98,7 @@ class ConfigTranslationController extends ControllerBase {
$container->get('router'), $container->get('router'),
$container->get('path_processor_manager'), $container->get('path_processor_manager'),
$container->get('current_user'), $container->get('current_user'),
$container->get('language_manager'), $container->get('language_manager')
$container->get('renderer')
); );
} }
@ -141,33 +127,7 @@ class ConfigTranslationController extends ControllerBase {
if (count($languages) == 1) { if (count($languages) == 1) {
drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href=":url">languages</a>.', array(':url' => $this->url('entity.configurable_language.collection'))), 'warning'); drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href=":url">languages</a>.', array(':url' => $this->url('entity.configurable_language.collection'))), 'warning');
} }
try {
$original_langcode = $mapper->getLangcode(); $original_langcode = $mapper->getLangcode();
$operations_access = TRUE;
}
catch (ConfigMapperLanguageException $exception) {
$items = [];
foreach ($mapper->getConfigNames() as $config_name) {
$langcode = $mapper->getLangcodeFromConfig($config_name);
$items[] = $this->t('@name: @langcode', [
'@name' => $config_name,
'@langcode' => $langcode,
]);
}
$message = [
'message' => ['#markup' => $this->t('The configuration objects have different language codes so they cannot be translated:')],
'items' => [
'#theme' => 'item_list',
'#items' => $items,
],
];
drupal_set_message($this->renderer->renderPlain($message), 'warning');
$original_langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
$operations_access = FALSE;
}
if (!isset($languages[$original_langcode])) { if (!isset($languages[$original_langcode])) {
// If the language is not configured on the site, create a dummy language // If the language is not configured on the site, create a dummy language
// object for this listing only to ensure the user gets useful info. // object for this listing only to ensure the user gets useful info.
@ -245,9 +205,6 @@ class ConfigTranslationController extends ControllerBase {
$page['languages'][$langcode]['operations'] = array( $page['languages'][$langcode]['operations'] = array(
'#type' => 'operations', '#type' => 'operations',
'#links' => $operations, '#links' => $operations,
// Even if the mapper contains multiple language codes, the source
// configuration can still be edited.
'#access' => ($langcode == $original_langcode) || $operations_access,
); );
} }
return $page; return $page;

View File

@ -1,9 +0,0 @@
<?php
namespace Drupal\config_translation\Exception;
/**
* Provides an exception for configuration mappers with multiple languages.
*/
class ConfigMapperLanguageException extends \RuntimeException {
}

View File

@ -138,12 +138,7 @@ abstract class ConfigTranslationFormBase extends FormBase implements BaseFormIdI
$this->mapper = $mapper; $this->mapper = $mapper;
$this->language = $language; $this->language = $language;
$this->sourceLanguage = $this->languageManager->getLanguage($this->mapper->getLangcode());
// ConfigTranslationFormAccess will not grant access if this raises an
// exception, so we can call this without a try-catch block here.
$langcode = $this->mapper->getLangcode();
$this->sourceLanguage = $this->languageManager->getLanguage($langcode);
// Get base language configuration to display in the form before setting the // Get base language configuration to display in the form before setting the
// language to use for the form. This avoids repetitively settings and // language to use for the form. This avoids repetitively settings and

View File

@ -23,9 +23,7 @@ class NodeTypeTranslationTest extends WebTestBase {
* @var array * @var array
*/ */
public static $modules = array( public static $modules = array(
'block',
'config_translation', 'config_translation',
'field_ui',
'node', 'node',
); );
@ -55,8 +53,6 @@ class NodeTypeTranslationTest extends WebTestBase {
$admin_permissions = array( $admin_permissions = array(
'administer content types', 'administer content types',
'administer node fields',
'administer languages',
'administer site configuration', 'administer site configuration',
'administer themes', 'administer themes',
'translate configuration', 'translate configuration',
@ -148,29 +144,6 @@ class NodeTypeTranslationTest extends WebTestBase {
$this->assertText('Edited title'); $this->assertText('Edited title');
$this->drupalGet("$langcode/node/add/$type"); $this->drupalGet("$langcode/node/add/$type");
$this->assertText('Translated title'); $this->assertText('Translated title');
// Add an e-mail field.
$this->drupalPostForm("admin/structure/types/manage/$type/fields/add-field", array('new_storage_type' => 'email', 'label' => 'Email', 'field_name' => 'email'), 'Save and continue');
$this->drupalPostForm(NULL, array(), 'Save field settings');
$this->drupalPostForm(NULL, array(), 'Save settings');
$type = Unicode::strtolower($this->randomMachineName(16));
$name = $this->randomString();
$this->drupalCreateContentType(array('type' => $type, 'name' => $name));
// Set tabs.
$this->drupalPlaceBlock('local_tasks_block', array('primary' => TRUE));
// Change default language.
$this->drupalPostForm('admin/config/regional/language', array('site_default_language' => 'es'), 'Save configuration');
// Try re-using the email field.
$this->drupalGet("es/admin/structure/types/manage/$type/fields/add-field");
$this->drupalPostForm(NULL, array('existing_storage_name' => 'field_email', 'existing_storage_label' => 'Email'), 'Save and continue');
$this->assertResponse(200);
$this->drupalGet("es/admin/structure/types/manage/$type/fields/node.$type.field_email/translate");
$this->assertResponse(200);
$this->assertText("The configuration objects have different language codes so they cannot be translated");
} }
} }