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
32 merge requests!11131[10.4.x-only-DO-NOT-MERGE]: Issue ##2842525 Ajax attached to Views exposed filter form does not trigger callbacks,!9470[10.3.x-only-DO-NOT-MERGE]: #3331771 Fix file_get_contents(): Passing null to parameter,!8540Issue #3457061: Bootstrap Modal dialog Not closing after 10.3.0 Update,!8528Issue #3456871 by Tim Bozeman: Support NULL services,!8373Issue #3427374 by danflanagan8, Vighneshh: taxonomy_tid ViewsArgumentDefault...,!7526Expose roles in response,!7352Draft: Resolve #3203489 "Set filename as",!6791Issue #3163299: Ajax exposed filters not working for multiple instances of the same Views block placed on one page,!3878Removed unused condition head title for views,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3668Resolve #3347842 "Deprecate the trusted",!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3133core/modules/system/css/components/hidden.module.css,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2794Issue #3100732: Allow specifying `meta` data on JSON:API objects,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493
Pipeline #104411 passed
Pipeline: drupal

#104413

    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}
    */ */
    ......
    • catch @catch

      mentioned in commit 578a7ba1

      ·

      mentioned in commit 578a7ba1

      Toggle commit list
    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