Skip to content
Snippets Groups Projects
Commit 1be0f07e authored by catch's avatar catch
Browse files

Issue #2002174 by Wim Leers, xjm, fago, effulgentsia, catch, smustgrave: Allow...

Issue #2002174 by Wim Leers, xjm, fago, effulgentsia, catch, smustgrave: Allow vocabularies to be validated via the API, not just during form submissions
parent fcc1ba65
No related branches found
No related tags found
No related merge requests found
Showing
with 175 additions and 13 deletions
...@@ -1323,3 +1323,18 @@ function node_comment_delete($comment) { ...@@ -1323,3 +1323,18 @@ function node_comment_delete($comment) {
function node_config_translation_info_alter(&$info) { function node_config_translation_info_alter(&$info) {
$info['node_type']['class'] = 'Drupal\node\ConfigTranslation\NodeTypeMapper'; $info['node_type']['class'] = 'Drupal\node\ConfigTranslation\NodeTypeMapper';
} }
/**
* Implements hook_ENTITY_TYPE_presave().
*/
function node_node_type_presave(NodeTypeInterface $node_type) {
// Content types' `help` and `description` fields must be stored as NULL
// at the config level if they are empty.
// @see node_post_update_set_node_type_description_and_help_to_null()
if (trim($node_type->getDescription()) === '') {
$node_type->set('description', NULL);
}
if (trim($node_type->getHelp()) === '') {
$node_type->set('help', NULL);
}
}
...@@ -14,15 +14,8 @@ ...@@ -14,15 +14,8 @@
function node_post_update_set_node_type_description_and_help_to_null(array &$sandbox): void { function node_post_update_set_node_type_description_and_help_to_null(array &$sandbox): void {
\Drupal::classResolver(ConfigEntityUpdater::class) \Drupal::classResolver(ConfigEntityUpdater::class)
->update($sandbox, 'node_type', function (NodeTypeInterface $node_type): bool { ->update($sandbox, 'node_type', function (NodeTypeInterface $node_type): bool {
// Content types' `help` and `description` fields must be stored as NULL // @see node_node_type_presave()
// at the config level if they are empty. return trim($node_type->getDescription()) === '' || trim($node_type->getHelp()) === '';
if (trim($node_type->getDescription()) === '') {
$node_type->set('description', NULL);
}
if (trim($node_type->getHelp()) === '') {
$node_type->set('help', NULL);
}
return TRUE;
}); });
} }
......
...@@ -22,6 +22,8 @@ taxonomy.settings: ...@@ -22,6 +22,8 @@ taxonomy.settings:
taxonomy.vocabulary.*: taxonomy.vocabulary.*:
type: config_entity type: config_entity
label: 'Vocabulary' label: 'Vocabulary'
constraints:
FullyValidatable: ~
mapping: mapping:
name: name:
type: required_label type: required_label
...@@ -35,11 +37,18 @@ taxonomy.vocabulary.*: ...@@ -35,11 +37,18 @@ taxonomy.vocabulary.*:
Length: Length:
max: 32 max: 32
description: description:
type: label type: text
label: 'Description' label: 'Description'
nullable: true
constraints:
NotBlank:
allowNull: true
weight: weight:
type: integer type: integer
label: 'Weight' label: 'Weight'
# A weight can be any integer, positive or negative.
constraints:
NotNull: []
new_revision: new_revision:
type: boolean type: boolean
label: 'Whether a new revision should be created by default' label: 'Whether a new revision should be created by default'
......
...@@ -80,9 +80,9 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface { ...@@ -80,9 +80,9 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
/** /**
* Description of the vocabulary. * Description of the vocabulary.
* *
* @var string * @var string|null
*/ */
protected $description; protected $description = NULL;
/** /**
* The weight of this vocabulary in relation to other vocabularies. * The weight of this vocabulary in relation to other vocabularies.
...@@ -102,7 +102,7 @@ public function id() { ...@@ -102,7 +102,7 @@ public function id() {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getDescription() { public function getDescription() {
return $this->description; return $this->description ?? '';
} }
/** /**
......
<?php
declare(strict_types=1);
namespace Drupal\taxonomy\Plugin\migrate\destination;
use Drupal\migrate\Plugin\migrate\destination\EntityConfigBase;
use Drupal\migrate\Row;
/**
* @MigrateDestination(
* id = "entity:taxonomy_vocabulary"
* )
*/
class EntityTaxonomyVocabulary extends EntityConfigBase {
/**
* {@inheritdoc}
*/
public function getEntity(Row $row, array $old_destination_id_values) {
/** @var \Drupal\taxonomy\VocabularyInterface $vocabulary */
$vocabulary = parent::getEntity($row, $old_destination_id_values);
// Config schema does not allow description to be empty.
if (trim($vocabulary->getDescription()) === '') {
$vocabulary->set('description', NULL);
}
return $vocabulary;
}
}
...@@ -42,6 +42,21 @@ public static function create(ContainerInterface $container) { ...@@ -42,6 +42,21 @@ public static function create(ContainerInterface $container) {
); );
} }
/**
* {@inheritdoc}
*/
public function buildEntity(array $form, FormStateInterface $form_state) {
/** @var \Drupal\taxonomy\VocabularyInterface $entity */
$entity = parent::buildEntity($form, $form_state);
// The description cannot be an empty string.
if (trim($form_state->getValue('description')) === '') {
$entity->set('description', NULL);
}
return $entity;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\VocabularyInterface;
/** /**
* Implements hook_help(). * Implements hook_help().
...@@ -279,3 +280,15 @@ function taxonomy_taxonomy_term_delete(Term $term) { ...@@ -279,3 +280,15 @@ function taxonomy_taxonomy_term_delete(Term $term) {
/** /**
* @} End of "defgroup taxonomy_index". * @} End of "defgroup taxonomy_index".
*/ */
/**
* Implements hook_ENTITY_TYPE_presave().
*/
function taxonomy_taxonomy_vocabulary_presave(VocabularyInterface $vocabulary) {
// Vocabularies' `description` field must be stored as NULL at the config
// level if it is empty.
// @see taxonomy_post_update_set_vocabulary_description_to_null()
if (trim($vocabulary->getDescription()) === '') {
$vocabulary->set('description', NULL);
}
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
use Drupal\Core\Config\Entity\ConfigEntityUpdater; use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\taxonomy\VocabularyInterface;
/** /**
* Implements hook_removed_post_updates(). * Implements hook_removed_post_updates().
...@@ -31,3 +32,14 @@ function taxonomy_post_update_set_new_revision(&$sandbox = NULL) { ...@@ -31,3 +32,14 @@ function taxonomy_post_update_set_new_revision(&$sandbox = NULL) {
return TRUE; return TRUE;
}); });
} }
/**
* Converts empty `description` in vocabularies to NULL.
*/
function taxonomy_post_update_set_vocabulary_description_to_null(array &$sandbox): void {
\Drupal::classResolver(ConfigEntityUpdater::class)
->update($sandbox, 'taxonomy_vocabulary', function (VocabularyInterface $vocabulary): bool {
// @see taxonomy_taxonomy_vocabulary_presave()
return trim($vocabulary->getDescription()) === '';
});
}
<?php
/**
* @file
* Empties the description of the `tags` vocabulary.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$data = $connection->select('config')
->condition('name', 'taxonomy.vocabulary.tags')
->fields('config', ['data'])
->execute()
->fetchField();
$data = unserialize($data);
$data['description'] = "\n";
$connection->update('config')
->condition('name', 'taxonomy.vocabulary.tags')
->fields([
'data' => serialize($data),
])
->execute();
<?php
declare(strict_types=1);
namespace Drupal\Tests\taxonomy\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the upgrade path for making vocabularies' description NULL.
*
* @group taxonomy
* @see taxonomy_post_update_set_vocabulary_description_to_null()
*/
class NullDescriptionTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz',
__DIR__ . '/../../../fixtures/update/remove-description-from-tags-vocabulary.php',
];
}
/**
* Tests the upgrade path for updating empty description to NULL.
*/
public function testRunUpdates(): void {
$vocabulary = Vocabulary::load('tags');
$this->assertInstanceOf(Vocabulary::class, $vocabulary);
$this->assertSame("\n", $vocabulary->get('description'));
$this->runUpdates();
$vocabulary = Vocabulary::load('tags');
$this->assertInstanceOf(Vocabulary::class, $vocabulary);
$this->assertNull($vocabulary->get('description'));
$this->assertSame('', $vocabulary->getDescription());
}
}
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
*/ */
class VocabularyValidationTest extends ConfigEntityValidationTestBase { class VocabularyValidationTest extends ConfigEntityValidationTestBase {
/**
* {@inheritdoc}
*/
protected static array $propertiesWithOptionalValues = ['description'];
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment