Update to Drupal 8.0.2. For more information, see https://www.drupal.org/drupal-8.0.2-release-notes
This commit is contained in:
parent
1a0e9d9fac
commit
a6b049dd05
538 changed files with 5247 additions and 1594 deletions
|
@ -1035,6 +1035,31 @@ function locale_translation_use_remote_source() {
|
|||
* layout issues (div) or a possible attack vector (img).
|
||||
*/
|
||||
function locale_string_is_safe($string) {
|
||||
// Some strings have tokens in them. For tokens in the first part of href or
|
||||
// src HTML attributes, \Drupal\Component\Utility\Xss::filter() removes part
|
||||
// of the token, the part before the first colon.
|
||||
// \Drupal\Component\Utility\Xss::filter() assumes it could be an attempt to
|
||||
// inject javascript. When \Drupal\Component\Utility\Xss::filter() removes
|
||||
// part of tokens, it causes the string to not be translatable when it should
|
||||
// be translatable.
|
||||
// @see \Drupal\locale\Tests\LocaleStringIsSafeTest::testLocaleStringIsSafe()
|
||||
//
|
||||
// We can recognize tokens since they are wrapped with brackets and are only
|
||||
// composed of alphanumeric characters, colon, underscore, and dashes. We can
|
||||
// be sure these strings are safe to strip out before the string is checked in
|
||||
// \Drupal\Component\Utility\Xss::filter() because no dangerous javascript
|
||||
// will match that pattern.
|
||||
//
|
||||
// Strings with tokens should not be assumed to be dangerous because even if
|
||||
// we evaluate them to be safe here, later replacing the token inside the
|
||||
// string will automatically mark it as unsafe as it is not the same string
|
||||
// anymore.
|
||||
//
|
||||
// @todo Do not strip out the token. Fix
|
||||
// \Drupal\Component\Utility\Xss::filter() to not incorrectly alter the
|
||||
// string. https://www.drupal.org/node/2372127
|
||||
$string = preg_replace('/\[[a-z0-9_-]+(:[a-z0-9_-]+)+\]/i', '', $string);
|
||||
|
||||
return Html::decodeEntities($string) == Html::decodeEntities(Xss::filter($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ services:
|
|||
locale.config_manager:
|
||||
class: Drupal\locale\LocaleConfigManager
|
||||
arguments: ['@config.storage', '@locale.storage', '@config.factory', '@config.typed', '@language_manager', '@locale.default.config.storage']
|
||||
calls:
|
||||
- [_setConfigManager, ['@config.manager']]
|
||||
locale.storage:
|
||||
class: Drupal\locale\StringDatabaseStorage
|
||||
arguments: ['@database']
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\locale;
|
|||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
@ -95,6 +96,17 @@ class LocaleConfigManager {
|
|||
*/
|
||||
protected $defaultConfigStorage;
|
||||
|
||||
/**
|
||||
* The configuration manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigManagerInterface
|
||||
*
|
||||
* @internal
|
||||
* Will be made protected and renamed to $configManager in 8.1.0.
|
||||
* https://www.drupal.org/node/2628132
|
||||
*/
|
||||
private $_configManager;
|
||||
|
||||
/**
|
||||
* Creates a new typed configuration manager.
|
||||
*
|
||||
|
@ -120,6 +132,36 @@ class LocaleConfigManager {
|
|||
$this->defaultConfigStorage = $default_config_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration manager service.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
|
||||
*
|
||||
* @internal
|
||||
* Will be replaced by constructor injection in 8.1.0.
|
||||
* https://www.drupal.org/node/2628132
|
||||
*/
|
||||
public function _setConfigManager(ConfigManagerInterface $config_manager) {
|
||||
$this->_configManager = $config_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration manager service.
|
||||
*
|
||||
* @return \Drupal\Core\Config\ConfigManagerInterface
|
||||
* The config manager
|
||||
*
|
||||
* @internal
|
||||
* Will be replaced by constructor injection in 8.1.0.
|
||||
* https://www.drupal.org/node/2628132
|
||||
*/
|
||||
private final function _getConfigManager() {
|
||||
if (!isset($this->_configManager)) {
|
||||
$this->_configManager = \Drupal::service('config.manager');
|
||||
}
|
||||
return $this->_configManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets array of translated strings for Locale translatable configuration.
|
||||
*
|
||||
|
@ -477,10 +519,21 @@ class LocaleConfigManager {
|
|||
* configuration exists.
|
||||
*/
|
||||
public function getDefaultConfigLangcode($name) {
|
||||
$shipped = $this->defaultConfigStorage->read($name);
|
||||
if (!empty($shipped)) {
|
||||
return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en';
|
||||
// Config entities that do not have the 'default_config_hash' cannot be
|
||||
// shipped configuration regardless of whether there is a name match.
|
||||
// configurable_language entities are a special case since they can be
|
||||
// translated regardless of whether they are shipped if they in the standard
|
||||
// language list.
|
||||
$config_entity_type = $this->_getConfigManager()->getEntityTypeIdByName($name);
|
||||
if (!$config_entity_type || $config_entity_type === 'configurable_language'
|
||||
|| !empty($this->configFactory->get($name)->get('_core.default_config_hash'))
|
||||
) {
|
||||
$shipped = $this->defaultConfigStorage->read($name);
|
||||
if (!empty($shipped)) {
|
||||
return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en';
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,7 +17,7 @@ abstract class StringBase implements StringInterface {
|
|||
/**
|
||||
* The string identifier.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $lid;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\locale\Tests;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
|
@ -22,7 +23,14 @@ class LocaleConfigManagerTest extends KernelTestBase {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'locale', 'locale_test');
|
||||
public static $modules = array('system', 'language', 'locale', 'locale_test', 'block');
|
||||
|
||||
/**
|
||||
* This test creates simple config on the fly breaking schema checking.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Tests hasTranslation().
|
||||
|
@ -72,6 +80,49 @@ class LocaleConfigManagerTest extends KernelTestBase {
|
|||
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('locale_test_translate.settings'), 'Before installing a module the locale config manager can not access the shipped configuration.');
|
||||
\Drupal::service('module_installer')->install(['locale_test_translate']);
|
||||
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('locale_test_translate.settings'), 'After installing a module the locale config manager can get the shipped configuration langcode.');
|
||||
|
||||
$simple_config = \Drupal::configFactory()->getEditable('locale_test_translate.simple_config_extra');
|
||||
$simple_config->set('foo', 'bar')->save();
|
||||
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode($simple_config->getName()), 'Simple config created through the API is not treated as shipped configuration.');
|
||||
|
||||
$block = Block::create(array(
|
||||
'id' => 'test_default_config',
|
||||
'theme' => 'classy',
|
||||
'status' => TRUE,
|
||||
'region' => 'content',
|
||||
'plugin' => 'local_tasks_block',
|
||||
'settings' => [
|
||||
'id' => 'local_tasks_block',
|
||||
'label' => $this->randomMachineName(),
|
||||
'provider' => 'core',
|
||||
'label_display' => FALSE,
|
||||
'primary' => TRUE,
|
||||
'secondary' => TRUE,
|
||||
],
|
||||
));
|
||||
$block->save();
|
||||
|
||||
// Install the theme after creating the block as installing the theme will
|
||||
// install the block provided by the locale_test module.
|
||||
\Drupal::service('theme_installer')->install(['classy']);
|
||||
|
||||
// The test_default_config block provided by the locale_test module will not
|
||||
// be installed because a block with the same ID already exists.
|
||||
$this->installConfig(['locale_test']);
|
||||
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('block.block.test_default_config'), 'The block.block.test_default_config is not shipped configuration.');
|
||||
// Delete the block so we can install the one provided by the locale_test
|
||||
// module.
|
||||
$block->delete();
|
||||
$this->installConfig(['locale_test']);
|
||||
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('block.block.test_default_config'), 'The block.block.test_default_config is shipped configuration.');
|
||||
|
||||
// Test the special case for configurable_language config entities.
|
||||
$fr_language = ConfigurableLanguage::createFromLangcode('fr');
|
||||
$fr_language->save();
|
||||
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('language.entity.fr'), 'The language.entity.fr is treated as shipped configuration because it is a configurable_language config entity and in the standard language list.');
|
||||
$custom_language = ConfigurableLanguage::createFromLangcode('custom');
|
||||
$custom_language->save();
|
||||
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('language.entity.custom'), 'The language.entity.custom is not shipped configuration because it is not in the standard language list.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\locale\Tests\LocaleConfigurableLanguageManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests that the configurable language manager and locale operate correctly.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleConfigurableLanguageManagerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* A list of modules to install for this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'locale'];
|
||||
|
||||
public function testGetLanguages() {
|
||||
$this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']);
|
||||
$default_language = new ConfigurableLanguage(['label' => $this->randomMachineName(), 'id' => 'default', 'weight' => 0], 'configurable_language');
|
||||
$default_language->save();
|
||||
|
||||
// Set new default language.
|
||||
\Drupal::service('language.default')->set($default_language);
|
||||
\Drupal::service('string_translation')->setDefaultLangcode($default_language->getId());
|
||||
|
||||
$languages = \Drupal::service('language_manager')->getLanguages(LanguageInterface::STATE_ALL);
|
||||
$this->assertEqual(['default', 'und', 'zxx'], array_keys($languages));
|
||||
|
||||
$configurableLanguage = new ConfigurableLanguage(['label' => $this->randomMachineName(), 'id' => 'test', 'weight' => 1], 'configurable_language');
|
||||
// Simulate a configuration sync by setting the flag otherwise the locked
|
||||
// language weights would be updated whilst saving.
|
||||
// @see \Drupal\language\Entity\ConfigurableLanguage::postSave()
|
||||
$configurableLanguage->setSyncing(TRUE)->save();
|
||||
|
||||
$languages = \Drupal::service('language_manager')->getLanguages(LanguageInterface::STATE_ALL);
|
||||
$this->assertEqual(['default', 'test', 'und', 'zxx'], array_keys($languages));
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ class LocaleLocaleLookupTest extends WebTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Change the language default object to different values.
|
||||
|
|
106
core/modules/locale/src/Tests/LocaleStringIsSafeTest.php
Normal file
106
core/modules/locale/src/Tests/LocaleStringIsSafeTest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\locale\Tests\LocaleStringIsSafeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests locale translation safe string handling.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleStringIsSafeTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['locale', 'locale_test'];
|
||||
|
||||
/**
|
||||
* Tests for locale_string_is_safe().
|
||||
*/
|
||||
public function testLocaleStringIsSafe() {
|
||||
// Check a translatable string without HTML.
|
||||
$string = 'Hello world!';
|
||||
$result = locale_string_is_safe($string);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Check a translatable string which includes trustable HTML.
|
||||
$string = 'Hello <strong>world</strong>!';
|
||||
$result = locale_string_is_safe($string);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Check an untranslatable string which includes untrustable HTML (according
|
||||
// to the locale_string_is_safe() function definition).
|
||||
$string = 'Hello <img src="world.png" alt="world" />!';
|
||||
$result = locale_string_is_safe($string);
|
||||
$this->assertFalse($result);
|
||||
|
||||
// Check a translatable string which includes a token in an href attribute.
|
||||
$string = 'Hi <a href="[current-user:url]">user</a>';
|
||||
$result = locale_string_is_safe($string);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a translated and tokenized string is properly escaped by Twig.
|
||||
*
|
||||
* In each assert* call we add a new line at the expected result to match the
|
||||
* newline at the end of the template file.
|
||||
*/
|
||||
public function testLocalizedTokenizedString() {
|
||||
$tests_to_do = [
|
||||
1 => [
|
||||
'original' => 'Go to the <a href="[locale_test:security_test1]">frontpage</a>',
|
||||
'replaced' => 'Go to the <a href="javascript:alert(&#039;Mooooh!&#039;);">frontpage</a>',
|
||||
],
|
||||
2 => [
|
||||
'original' => 'Hello <strong>[locale_test:security_test2]</strong>!',
|
||||
'replaced' => 'Hello <strong>&lt;script&gt;alert(&#039;Mooooh!&#039;);&lt;/script&gt;</strong>!',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tests_to_do as $i => $test) {
|
||||
$original_string = $test['original'];
|
||||
$rendered_original_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $original_string]);
|
||||
// Twig assumes that strings are unsafe so it escapes them, and so the
|
||||
// original and the rendered version should be different.
|
||||
$this->assertNotEqual(
|
||||
$rendered_original_string,
|
||||
$original_string . "\n",
|
||||
'Security test ' . $i . ' before translation'
|
||||
);
|
||||
|
||||
// Pass the original string to the t() function to get it marked as safe.
|
||||
$safe_string = t($original_string);
|
||||
$rendered_safe_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $safe_string]);
|
||||
// t() function always marks the string as safe so it won't be escaped,
|
||||
// and should be the same as the original.
|
||||
$this->assertEqual(
|
||||
$rendered_safe_string,
|
||||
$original_string . "\n",
|
||||
'Security test ' . $i . ' after translation before token replacement'
|
||||
);
|
||||
|
||||
// Replace tokens in the safe string to inject it with dangerous content.
|
||||
// @see locale_test_tokens().
|
||||
$unsafe_string = \Drupal::token()->replace($safe_string);
|
||||
$rendered_unsafe_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $unsafe_string]);
|
||||
// Token replacement changes the string so it is not marked as safe
|
||||
// anymore. Check it is escaped the way we expect.
|
||||
$this->assertEqual(
|
||||
$rendered_unsafe_string,
|
||||
$test['replaced'] . "\n",
|
||||
'Security test ' . $i . ' after translation after token replacement'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,28 +19,28 @@ abstract class LocaleUpdateBase extends WebTestBase {
|
|||
/**
|
||||
* Timestamp for an old translation.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $timestampOld;
|
||||
|
||||
/**
|
||||
* Timestamp for a medium aged translation.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $timestampMedium;
|
||||
|
||||
/**
|
||||
* Timestamp for a new translation.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $timestampNew;
|
||||
|
||||
/**
|
||||
* Timestamp for current time.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $timestampNow;
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
theme:
|
||||
- classy
|
||||
id: test_default_config
|
||||
theme: classy
|
||||
region: content
|
||||
weight: -40
|
||||
provider: null
|
||||
plugin: local_tasks_block
|
||||
settings:
|
||||
id: local_tasks_block
|
||||
label: Tabs
|
||||
provider: core
|
||||
label_display: '0'
|
||||
primary: true
|
||||
secondary: true
|
||||
visibility: { }
|
|
@ -146,3 +146,60 @@ function locale_test_language_fallback_candidates_locale_lookup_alter(array &$ca
|
|||
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_candidates', $candidates);
|
||||
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function locale_test_theme() {
|
||||
$return = [];
|
||||
|
||||
$return['locale_test_tokenized'] = [
|
||||
'variable' => ['content' => ''],
|
||||
];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function locale_test_token_info() {
|
||||
$info = [];
|
||||
|
||||
$info['types']['locale_test'] = [
|
||||
'name' => t('Locale test'),
|
||||
'description' => t('Locale test'),
|
||||
];
|
||||
|
||||
$info['tokens']['locale_test']['security_test1'] = [
|
||||
'type' => 'text',
|
||||
'name' => t('Security test 1'),
|
||||
];
|
||||
$info['tokens']['locale_test']['security_test2'] = [
|
||||
'type' => 'text',
|
||||
'name' => t('Security test 2'),
|
||||
];
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function locale_test_tokens($type, $tokens, array $data = [], array $options = []) {
|
||||
$return = [];
|
||||
if ($type == 'locale_test') {
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'security_test1':
|
||||
$return[$original] = "javascript:alert('Mooooh!');";
|
||||
break;
|
||||
case 'security_test2':
|
||||
$return[$original] = "<script>alert('Mooooh!');</script>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{ content }}
|
|
@ -33,7 +33,7 @@ class LocaleLocalTasksTest extends LocalTaskIntegrationTestBase {
|
|||
*/
|
||||
public function testLocalePageLocalTasks($route) {
|
||||
$tasks = array(
|
||||
0 => array('locale.translate_page', 'locale.translate_import', 'locale.translate_export','locale.settings'),
|
||||
0 => array('locale.translate_page', 'locale.translate_import', 'locale.translate_export', 'locale.settings'),
|
||||
);
|
||||
$this->assertLocalTasks($route, $tasks);
|
||||
}
|
||||
|
|
Reference in a new issue