Issue #3373653 by phenaproxima, Wim Leers, borisson_, penyaskito, andypost, lauriii, smustgrave, larowlan: Add a `langcode` data type to config schema
parent
b78ae9ef49
commit
6f3215938f
|
@ -104,6 +104,15 @@ machine_name:
|
|||
# @see \Drupal\Core\Config\Entity\ConfigEntityStorage::MAX_ID_LENGTH
|
||||
max: 166
|
||||
|
||||
# A language identifier.
|
||||
langcode:
|
||||
type: string
|
||||
label: 'Language code'
|
||||
constraints:
|
||||
NotNull: []
|
||||
Choice:
|
||||
callback: 'Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLangcodes'
|
||||
|
||||
# Complex extended data types:
|
||||
|
||||
# Root of a configuration object.
|
||||
|
@ -130,8 +139,7 @@ config_object:
|
|||
_core:
|
||||
type: _core_config_info
|
||||
langcode:
|
||||
type: string
|
||||
label: 'Language code'
|
||||
type: langcode
|
||||
|
||||
# Mail text with subject and body parts.
|
||||
mail:
|
||||
|
@ -305,8 +313,7 @@ config_entity:
|
|||
type: uuid
|
||||
label: 'UUID'
|
||||
langcode:
|
||||
type: string
|
||||
label: 'Language code'
|
||||
type: langcode
|
||||
status:
|
||||
type: boolean
|
||||
label: 'Status'
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Core\TypedData\Plugin\DataType;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\TypedData\DataReferenceBase;
|
||||
|
||||
/**
|
||||
|
@ -30,4 +31,34 @@ class LanguageReference extends DataReferenceBase {
|
|||
return isset($language) ? $language->id() : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all valid values for a `langcode` config value.
|
||||
*
|
||||
* @return string[]
|
||||
* All possible valid langcodes. This includes all langcodes in the standard
|
||||
* list of human languages, along with special langcodes like `und`, `zxx`,
|
||||
* and `site_default`, which Drupal uses internally. If any custom languages
|
||||
* are defined, they will be included as well.
|
||||
*
|
||||
* @see \Drupal\Core\Language\LanguageManagerInterface::getLanguages()
|
||||
* @see \Drupal\Core\Language\LanguageManagerInterface::getStandardLanguageList()
|
||||
*/
|
||||
public static function getAllValidLangcodes(): array {
|
||||
$language_manager = \Drupal::languageManager();
|
||||
|
||||
return array_unique([
|
||||
...array_keys($language_manager::getStandardLanguageList()),
|
||||
// We can't use LanguageInterface::STATE_ALL because it will exclude the
|
||||
// site default language in certain situations.
|
||||
// @see \Drupal\Core\Language\LanguageManager::filterLanguages()
|
||||
...array_keys($language_manager->getLanguages(LanguageInterface::STATE_LOCKED | LanguageInterface::STATE_CONFIGURABLE | LanguageInterface::STATE_SITE_DEFAULT)),
|
||||
// Include special language codes used internally.
|
||||
LanguageInterface::LANGCODE_NOT_APPLICABLE,
|
||||
LanguageInterface::LANGCODE_SITE_DEFAULT,
|
||||
LanguageInterface::LANGCODE_DEFAULT,
|
||||
LanguageInterface::LANGCODE_SYSTEM,
|
||||
LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,11 @@ use Drupal\Core\Cache\CacheBackendInterface;
|
|||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\Validation\Plugin\Validation\Constraint\EmailConstraint;
|
||||
use Symfony\Component\Validator\Constraints\Blank;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
/**
|
||||
* Constraint plugin manager.
|
||||
|
@ -87,24 +92,29 @@ class ConstraintManager extends DefaultPluginManager {
|
|||
public function registerDefinitions() {
|
||||
$this->getDiscovery()->setDefinition('Callback', [
|
||||
'label' => new TranslatableMarkup('Callback'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\Callback',
|
||||
'class' => Callback::class,
|
||||
'type' => FALSE,
|
||||
]);
|
||||
$this->getDiscovery()->setDefinition('Blank', [
|
||||
'label' => new TranslatableMarkup('Blank'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\Blank',
|
||||
'class' => Blank::class,
|
||||
'type' => FALSE,
|
||||
]);
|
||||
$this->getDiscovery()->setDefinition('NotBlank', [
|
||||
'label' => new TranslatableMarkup('Not blank'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\NotBlank',
|
||||
'class' => NotBlank::class,
|
||||
'type' => FALSE,
|
||||
]);
|
||||
$this->getDiscovery()->setDefinition('Email', [
|
||||
'label' => new TranslatableMarkup('Email'),
|
||||
'class' => '\Drupal\Core\Validation\Plugin\Validation\Constraint\EmailConstraint',
|
||||
'class' => EmailConstraint::class,
|
||||
'type' => ['string'],
|
||||
]);
|
||||
$this->getDiscovery()->setDefinition('Choice', [
|
||||
'label' => new TranslatableMarkup('Choice'),
|
||||
'class' => Choice::class,
|
||||
'type' => FALSE,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,6 @@ use Drupal\Core\Render\Element;
|
|||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\editor\EditorInterface;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
|
@ -568,24 +567,6 @@ function ckeditor5_js_alter(&$javascript, AttachedAssetsInterface $assets, Langu
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_validation_constraint_alter().
|
||||
*/
|
||||
function ckeditor5_validation_constraint_alter(array &$definitions) {
|
||||
// Add the Symfony validation constraints that Drupal core does not add in
|
||||
// \Drupal\Core\Validation\ConstraintManager::registerDefinitions() for
|
||||
// unknown reasons. Do it defensively, to not break when this changes.
|
||||
if (!isset($definitions['Choice'])) {
|
||||
$definitions['Choice'] = [
|
||||
'label' => 'Choice',
|
||||
'class' => Choice::class,
|
||||
'type' => FALSE,
|
||||
'provider' => 'core',
|
||||
'id' => 'Choice',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_config_schema_info_alter().
|
||||
*/
|
||||
|
|
|
@ -71,7 +71,7 @@ language.negotiation:
|
|||
type: string
|
||||
label: 'Domain'
|
||||
selected_langcode:
|
||||
type: string
|
||||
type: langcode
|
||||
label: 'Selected language'
|
||||
|
||||
language.mappings:
|
||||
|
@ -118,7 +118,7 @@ language.content_settings.*.*:
|
|||
type: string
|
||||
label: 'Bundle'
|
||||
default_langcode:
|
||||
type: string
|
||||
type: langcode
|
||||
label: 'Default language'
|
||||
language_alterable:
|
||||
type: boolean
|
||||
|
@ -130,7 +130,7 @@ condition.plugin.language:
|
|||
langcodes:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: string
|
||||
type: langcode
|
||||
|
||||
field.widget.settings.language_select:
|
||||
type: mapping
|
||||
|
|
|
@ -39,7 +39,7 @@ system.site:
|
|||
type: integer
|
||||
label: 'Weight element maximum value'
|
||||
default_langcode:
|
||||
type: string
|
||||
type: langcode
|
||||
label: 'Site default language code'
|
||||
mail_notification:
|
||||
type: string
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace Drupal\KernelTests\Core\Config;
|
|||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\LanguageReference;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Base class for testing validation of config entities.
|
||||
|
@ -311,4 +315,53 @@ abstract class ConfigEntityValidationTestBase extends KernelTestBase {
|
|||
$this->assertSame($expected_messages, $actual_messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the config entity's langcode is validated.
|
||||
*/
|
||||
public function testLangcode(): void {
|
||||
$this->entity->set('langcode', NULL);
|
||||
$this->assertValidationErrors([
|
||||
'langcode' => 'This value should not be null.',
|
||||
]);
|
||||
|
||||
// A langcode from the standard list should always be acceptable.
|
||||
$standard_languages = LanguageManager::getStandardLanguageList();
|
||||
$this->assertNotEmpty($standard_languages);
|
||||
$this->entity->set('langcode', key($standard_languages));
|
||||
$this->assertValidationErrors([]);
|
||||
|
||||
// All special, internal langcodes should be acceptable.
|
||||
$system_langcodes = [
|
||||
LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
LanguageInterface::LANGCODE_NOT_APPLICABLE,
|
||||
LanguageInterface::LANGCODE_DEFAULT,
|
||||
LanguageInterface::LANGCODE_SITE_DEFAULT,
|
||||
LanguageInterface::LANGCODE_SYSTEM,
|
||||
];
|
||||
foreach ($system_langcodes as $langcode) {
|
||||
$this->entity->set('langcode', $langcode);
|
||||
$this->assertValidationErrors([]);
|
||||
}
|
||||
|
||||
// An invalid langcode should be unacceptable, even if it "looks" right.
|
||||
$fake_langcode = 'definitely-not-a-language';
|
||||
$this->assertArrayNotHasKey($fake_langcode, LanguageReference::getAllValidLangcodes());
|
||||
$this->entity->set('langcode', $fake_langcode);
|
||||
$this->assertValidationErrors([
|
||||
'langcode' => 'The value you selected is not a valid choice.',
|
||||
]);
|
||||
|
||||
// If a new configurable language is created with a non-standard langcode,
|
||||
// it should be acceptable.
|
||||
$this->enableModules(['language']);
|
||||
// The language doesn't exist yet, so it shouldn't be a valid choice.
|
||||
$this->entity->set('langcode', 'kthxbai');
|
||||
$this->assertValidationErrors([
|
||||
'langcode' => 'The value you selected is not a valid choice.',
|
||||
]);
|
||||
// Once we create the language, it should be a valid choice.
|
||||
ConfigurableLanguage::createFromLangcode('kthxbai')->save();
|
||||
$this->assertValidationErrors([]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,8 +68,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$expected = [];
|
||||
$expected['label'] = 'Schema test data';
|
||||
$expected['class'] = Mapping::class;
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['langcode']['type'] = 'langcode';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testitem'] = ['label' => 'Test item'];
|
||||
$expected['mapping']['testlist'] = ['label' => 'Test list'];
|
||||
|
@ -115,8 +114,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
'type' => 'text',
|
||||
];
|
||||
$expected['mapping']['langcode'] = [
|
||||
'label' => 'Language code',
|
||||
'type' => 'string',
|
||||
'type' => 'langcode',
|
||||
];
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['type'] = 'system.maintenance';
|
||||
|
@ -131,8 +129,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$expected['class'] = Mapping::class;
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['langcode'] = [
|
||||
'type' => 'string',
|
||||
'label' => 'Language code',
|
||||
'type' => 'langcode',
|
||||
];
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['label'] = [
|
||||
|
@ -179,8 +176,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$expected['mapping']['name']['type'] = 'machine_name';
|
||||
$expected['mapping']['uuid']['type'] = 'uuid';
|
||||
$expected['mapping']['uuid']['label'] = 'UUID';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['langcode']['type'] = 'langcode';
|
||||
$expected['mapping']['status']['type'] = 'boolean';
|
||||
$expected['mapping']['status']['label'] = 'Status';
|
||||
$expected['mapping']['dependencies']['type'] = 'config_dependencies';
|
||||
|
@ -247,8 +243,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$expected = [];
|
||||
$expected['label'] = 'Schema multiple filesystem marker test';
|
||||
$expected['class'] = Mapping::class;
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['langcode']['type'] = 'langcode';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
|
@ -518,8 +513,7 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$expected['class'] = Mapping::class;
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['unwrap_for_canonical_representation'] = TRUE;
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['langcode']['type'] = 'langcode';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
|
|
Loading…
Reference in New Issue