From 2d6516afec8bee5651dc94cfa6b413ba56cec73c Mon Sep 17 00:00:00 2001 From: Nathaniel Catchpole Date: Tue, 9 Sep 2014 11:34:38 +0100 Subject: [PATCH] Issue #2288123 by Berdir, dawehner: Fixed Basic authentication broken for non-english sites. --- core/lib/Drupal/Core/Cache/CacheCollector.php | 24 +++++++++---- .../Entity/Annotation/ConfigEntityType.php | 3 +- .../Entity/Annotation/ContentEntityType.php | 3 +- core/lib/Drupal/Core/Entity/EntityType.php | 6 ++-- .../Plugin/Field/FieldType/StringItem.php | 5 ++- .../Drupal/Core/Language/LanguageManager.php | 7 ++-- .../Tests/Authentication/BasicAuthTest.php | 18 +++++++++- core/modules/locale/src/LocaleLookup.php | 34 +++++++++++++++---- .../Tests/Entity/EntityLanguageTestBase.php | 6 +--- 9 files changed, 79 insertions(+), 27 deletions(-) diff --git a/core/lib/Drupal/Core/Cache/CacheCollector.php b/core/lib/Drupal/Core/Cache/CacheCollector.php index 53139b8cafa8..b90327ec927f 100644 --- a/core/lib/Drupal/Core/Cache/CacheCollector.php +++ b/core/lib/Drupal/Core/Cache/CacheCollector.php @@ -121,6 +121,15 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn $this->lock = $lock; } + /** + * Gets the cache ID. + * + * @return string + */ + protected function getCid() { + return $this->cid; + } + /** * {@inheritdoc} */ @@ -221,19 +230,20 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn } // Lock cache writes to help avoid stampedes. - $lock_name = $this->cid . ':' . __CLASS__; + $cid = $this->getCid(); + $lock_name = $cid . ':' . __CLASS__; if (!$lock || $this->lock->acquire($lock_name)) { // Set and delete operations invalidate the cache item. Try to also load // an eventually invalidated cache entry, only update an invalidated cache // entry if the creation date did not change as this could result in an // inconsistent cache. - if ($cache = $this->cache->get($this->cid, $this->cacheInvalidated)) { + if ($cache = $this->cache->get($cid, $this->cacheInvalidated)) { if ($this->cacheInvalidated && $cache->created != $this->cacheCreated) { // We have invalidated the cache in this request and got a different // cache entry. Do not attempt to overwrite data that might have been // changed in a different request. We'll let the cache rebuild in // later requests. - $this->cache->delete($this->cid); + $this->cache->delete($cid); $this->lock->release($lock_name); return; } @@ -243,7 +253,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn foreach ($this->keysToRemove as $delete_key) { unset($data[$delete_key]); } - $this->cache->set($this->cid, $data, Cache::PERMANENT, $this->tags); + $this->cache->set($cid, $data, Cache::PERMANENT, $this->tags); if ($lock) { $this->lock->release($lock_name); } @@ -272,7 +282,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn Cache::deleteTags($this->tags); } else { - $this->cache->delete($this->cid); + $this->cache->delete($this->getCid()); } } @@ -293,7 +303,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn // The cache was not yet loaded, set flag to TRUE. $this->cacheLoaded = TRUE; - if ($cache = $this->cache->get($this->cid)) { + if ($cache = $this->cache->get($this->getCid())) { $this->cacheCreated = $cache->created; $this->storage = $cache->data; } @@ -305,7 +315,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn protected function invalidateCache() { // Invalidate the cache to make sure that other requests immediately see the // deletion before this request is terminated. - $this->cache->invalidate($this->cid); + $this->cache->invalidate($this->getCid()); $this->cacheInvalidated = TRUE; } diff --git a/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php b/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php index 20245750484b..4720b359df68 100644 --- a/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php +++ b/core/lib/Drupal/Core/Entity/Annotation/ConfigEntityType.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\Entity\Annotation; +use Drupal\Core\StringTranslation\TranslationWrapper; /** * Defines a config entity type annotation object. @@ -36,7 +37,7 @@ class ConfigEntityType extends EntityType { * {@inheritdoc} */ public function get() { - $this->definition['group_label'] = $this->t('Configuration', array(), array('context' => 'Entity type group')); + $this->definition['group_label'] = new TranslationWrapper('Configuration', array(), array('context' => 'Entity type group')); return parent::get(); } diff --git a/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php b/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php index 9f12bbebd07c..34363a52517b 100644 --- a/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php +++ b/core/lib/Drupal/Core/Entity/Annotation/ContentEntityType.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\Entity\Annotation; +use Drupal\Core\StringTranslation\TranslationWrapper; /** * Defines a content entity type annotation object. @@ -36,7 +37,7 @@ class ContentEntityType extends EntityType { * {@inheritdoc} */ public function get() { - $this->definition['group_label'] = $this->t('Content', array(), array('context' => 'Entity type group')); + $this->definition['group_label'] = new TranslationWrapper('Content', array(), array('context' => 'Entity type group')); return parent::get(); } diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index 37ed343e5140..79c092e38bac 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -578,7 +578,7 @@ class EntityType implements EntityTypeInterface { * {@inheritdoc} */ public function getBundleLabel() { - return $this->bundle_label; + return (string) $this->bundle_label; } /** @@ -635,7 +635,7 @@ class EntityType implements EntityTypeInterface { * {@inheritdoc} */ public function getLabel() { - return $this->label; + return (string) $this->label; } /** @@ -672,7 +672,7 @@ class EntityType implements EntityTypeInterface { * {@inheritdoc} */ public function getGroupLabel() { - return !empty($this->group_label) ? $this->group_label : $this->t('Other', array(), array('context' => 'Entity type group')); + return !empty($this->group_label) ? (string) $this->group_label : $this->t('Other', array(), array('context' => 'Entity type group')); } } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php index ba72dc38012e..c066e0ab0f00 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php @@ -9,6 +9,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldItemBase; +use Drupal\Core\StringTranslation\TranslationWrapper; use Drupal\Core\TypedData\DataDefinition; /** @@ -38,8 +39,10 @@ class StringItem extends FieldItemBase { * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { + // This is called very early by the user entity roles field. Prevent + // early t() calls by using the TranslationWrapper. $properties['value'] = DataDefinition::create('string') - ->setLabel(t('Text value')); + ->setLabel(new TranslationWrapper('Text value')); return $properties; } diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index d86789e0905f..2b8153343dbf 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -10,6 +10,7 @@ namespace Drupal\Core\Language; use Drupal\Component\Utility\String; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\StringTranslation\TranslationWrapper; /** * Class responsible for providing language support on language-unaware sites. @@ -195,15 +196,17 @@ class LanguageManager implements LanguageManagerInterface { 'default' => FALSE, 'locked' => TRUE, ); + // This is called very early while initializing the language system. Prevent + // early t() calls by using the TranslationWrapper. $languages[LanguageInterface::LANGCODE_NOT_SPECIFIED] = new Language(array( 'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'name' => $this->t('Not specified'), + 'name' => new TranslationWrapper('Not specified'), 'weight' => ++$weight, ) + $locked_language); $languages[LanguageInterface::LANGCODE_NOT_APPLICABLE] = new Language(array( 'id' => LanguageInterface::LANGCODE_NOT_APPLICABLE, - 'name' => $this->t('Not applicable'), + 'name' => new TranslationWrapper('Not applicable'), 'weight' => ++$weight, ) + $locked_language); diff --git a/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php b/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php index 77e732a8eda6..7c79554123db 100644 --- a/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php +++ b/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php @@ -7,6 +7,7 @@ namespace Drupal\basic_auth\Tests\Authentication; +use Drupal\Core\Language\Language; use Drupal\simpletest\WebTestBase; /** @@ -21,7 +22,7 @@ class BasicAuthTest extends WebTestBase { * * @var array */ - public static $modules = array('basic_auth', 'router_test'); + public static $modules = array('basic_auth', 'router_test', 'locale'); /** * Test http basic authentication. @@ -114,6 +115,21 @@ class BasicAuthTest extends WebTestBase { $this->assertResponse('200', 'Per user flood prevention does not block access for other users.'); } + /** + * Tests compatibility with locale/UI translation. + */ + function testLocale() { + $language = new Language(array('id' => 'de', 'default' => TRUE)); + language_save($language); + + $account = $this->drupalCreateUser(); + + $this->basicAuthGet('router_test/test11', $account->getUsername(), $account->pass_raw); + $this->assertText($account->getUsername(), 'Account name is displayed.'); + $this->assertResponse('200', 'HTTP response is OK'); + $this->curlClose(); + } + /** * Does HTTP basic auth request. * diff --git a/core/modules/locale/src/LocaleLookup.php b/core/modules/locale/src/LocaleLookup.php index 74c0358cb22b..2f03bc326ca1 100644 --- a/core/modules/locale/src/LocaleLookup.php +++ b/core/modules/locale/src/LocaleLookup.php @@ -92,12 +92,34 @@ class LocaleLookup extends CacheCollector { $this->configFactory = $config_factory; $this->languageManager = $language_manager; - // Add the current user's role IDs to the cache key, this ensures that, for - // example, strings for admin menu items and settings forms are not cached - // for anonymous users. - $user = \Drupal::currentUser(); - $rids = $user ? implode(':', array_keys($user->getRoles())) : '0'; - parent::__construct("locale:$langcode:$context:$rids", $cache, $lock, array('locale' => TRUE)); + $this->cache = $cache; + $this->lock = $lock; + $this->tags = array('locale' => TRUE); + } + + /** + * {@inheritdoc} + */ + protected function getCid() { + if (!isset($this->cid)) { + // Add the current user's role IDs to the cache key, this ensures that, + // for example, strings for admin menu items and settings forms are not + // cached for anonymous users. + $user = \Drupal::currentUser(); + $rids = $user ? implode(':', array_keys($user->getRoles())) : '0'; + $this->cid = "locale:{$this->langcode}:{$this->context}:$rids"; + + // Getting the roles from the current user might have resulted in t() + // calls that attempted to get translations from the locale cache. In that + // case they would not go into this method again as + // CacheCollector::lazyLoadCache() already set the loaded flag. They would + // however call resolveCacheMiss() and add that string to the list of + // cache misses that need to be written into the cache. Prevent that by + // resetting that list. All that happens in such a case are a few uncached + // translation lookups. + $this->keysToPersist = array(); + } + return $this->cid; } /** diff --git a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php index b73495eb8d02..21f1c56a34be 100644 --- a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php @@ -106,11 +106,7 @@ abstract class EntityLanguageTestBase extends EntityUnitTestBase { } // Create the default languages. - $default_language = language_save($this->languageManager->getDefaultLanguage()); - $languages = $this->languageManager->getDefaultLockedLanguages($default_language->weight); - foreach ($languages as $language) { - language_save($language); - } + $this->installConfig(array('language')); // Create test languages. $this->langcodes = array();