diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index c88ca10bd3..953dae9632 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -257,6 +257,9 @@ field.widget.settings.entity_reference_autocomplete_tags: placeholder: type: label label: 'Placeholder' + show_id: + type: boolean + label: 'Show ID' field.widget.settings.entity_reference_autocomplete: type: mapping @@ -274,6 +277,9 @@ field.widget.settings.entity_reference_autocomplete: placeholder: type: label label: 'Placeholder' + show_id: + type: boolean + label: 'Show ID' field.formatter.settings.boolean: type: mapping diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php index 2cb823aaed..1f2f0a492a 100644 --- a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php +++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php @@ -116,7 +116,7 @@ public static function valueCallback(&$element, $input, FormStateInterface $form // Extract the labels from the passed-in entity objects, taking access // checks into account. - return static::getEntityLabels($element['#default_value']); + return static::getEntityLabels($element['#default_value'], $element['#show_id'] ?? TRUE); } } @@ -365,15 +365,18 @@ protected static function matchEntityByTitle(SelectionInterface $handler, $input * * @param \Drupal\Core\Entity\EntityInterface[] $entities * An array of entity objects. + * @param bool $show_id + * If the label should show the entity id. * * @return string * A string of entity labels separated by commas. */ - public static function getEntityLabels(array $entities) { + public static function getEntityLabels(array $entities, $show_id = TRUE) { /** @var \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository */ $entity_repository = \Drupal::service('entity.repository'); $entity_labels = []; + $entity_ids = []; foreach ($entities as $entity) { // Set the entity in the correct language for display. $entity = $entity_repository->getTranslationFromContext($entity); @@ -383,12 +386,22 @@ public static function getEntityLabels(array $entities) { $label = ($entity->access('view label')) ? $entity->label() : t('- Restricted access -'); // Take into account "autocreated" entities. - if (!$entity->isNew()) { + if (!$entity->isNew() && $show_id) { $label .= ' (' . $entity->id() . ')'; } // Labels containing commas or quotes must be wrapped in quotes. $entity_labels[] = Tags::encode($label); + $entity_ids[] = ($entity->access('view label')) ? $entity->id() : -1; + } + + $duplicates = array_diff_assoc($entity_labels, array_unique($entity_labels)); + if (!$show_id && count($duplicates) > 0) { + foreach ($entity_labels as $key => $value) { + if (in_array($value, $duplicates) && $entity_ids[$key] > 0) { + $entity_labels[$key] .= ' (' . $entity_ids[$key] . ')'; + } + } } return implode(', ', $entity_labels); diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php index 68885f24df..b5af9e1859 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php @@ -31,6 +31,7 @@ public static function defaultSettings() { 'match_limit' => 10, 'size' => 60, 'placeholder' => '', + 'show_id' => TRUE, ] + parent::defaultSettings(); } @@ -65,6 +66,12 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#default_value' => $this->getSetting('placeholder'), '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'), ]; + $element['show_id'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Show Entity ID'), + '#default_value' => $this->getSetting('show_id') ?? TRUE, + '#description' => $this->t('Will display the entity id of the selection. When disabled, the entity id will still be shown for items with duplicate titles.'), + ]; return $element; } @@ -87,6 +94,8 @@ public function settingsSummary() { $summary[] = $this->t('No placeholder'); } + $summary[] = $this->t('Show entity IDs: @show', ['@show' => $this->getSetting('match_operator') ? 'Yes' : 'No']); + return $summary; } @@ -120,8 +129,13 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#default_value' => $referenced_entities[$delta] ?? NULL, '#size' => $this->getSetting('size'), '#placeholder' => $this->getSetting('placeholder'), + '#show_id' => $this->getSetting('show_id'), ]; + if (empty($this->getSetting('show_id'))) { + $element['#attributes']['data-drupal-autocomplete-hide-ids'] = ''; + } + if ($bundle = $this->getAutocreateBundle()) { $element['#autocreate'] = [ 'bundle' => $bundle, diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js index 74ca14e33e..b6b6664188 100644 --- a/core/misc/autocomplete.js +++ b/core/misc/autocomplete.js @@ -120,7 +120,19 @@ suggestions.splice(index, 1); } } - response(suggestions); + + const labels = suggestions.map((item) => item.label); + const duplicates = new Set( + labels.filter( + (item) => labels.indexOf(item) !== labels.lastIndexOf(item), + ), + ); + + response( + suggestions.map((item) => + duplicates.has(item.label) ? { ...item, showId: true } : item, + ), + ); } // Get the desired term and construct the autocomplete URL for it. @@ -177,7 +189,12 @@ // Remove the current input. terms.pop(); // Add the selected item. - terms.push(ui.item.value); + // @todo this should be dynamic based on the formatter show_id setting. Use + // the value property to display entity ids and use the label property to + // not display them. + const hideIds = this.hasAttribute('data-drupal-autocomplete-hide-ids'); + + terms.push(hideIds && !ui.item.showId ? ui.item.label : ui.item.value); event.target.value = terms.join(', '); // Return false to tell jQuery UI that we've filled in the value already. @@ -196,7 +213,9 @@ * jQuery collection of the ul element. */ function renderItem(ul, item) { - return $('
  • ').append($('').html(item.label)).appendTo(ul); + return $('
  • ') + .append($('').html(item.showId ? item.value : item.label)) + .appendTo(ul); } /** diff --git a/core/modules/jsonapi/tests/src/Functional/EntityFormDisplayTest.php b/core/modules/jsonapi/tests/src/Functional/EntityFormDisplayTest.php index afaf28eaf9..e42e3e782c 100644 --- a/core/modules/jsonapi/tests/src/Functional/EntityFormDisplayTest.php +++ b/core/modules/jsonapi/tests/src/Functional/EntityFormDisplayTest.php @@ -148,6 +148,7 @@ protected function getExpectedDocument() { 'match_limit' => 10, 'size' => 60, 'placeholder' => '', + 'show_id' => TRUE, ], 'region' => 'content', 'third_party_settings' => [], diff --git a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml index 9ec9758185..d9c3ac5596 100644 --- a/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml +++ b/core/modules/taxonomy/config/schema/taxonomy.views.schema.yml @@ -141,6 +141,9 @@ views.filter.taxonomy_index_tid: sequence: type: integer label: 'Value' + show_id: + type: boolean + label: 'Show ID' views.filter.taxonomy_index_tid_depth: type: views.filter.taxonomy_index_tid diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php index d83a028edd..7050bf2942 100644 --- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php +++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php @@ -176,10 +176,11 @@ protected function valueForm(&$form, FormStateInterface $form_state) { if ($this->options['type'] == 'textfield') { $terms = $this->value ? Term::loadMultiple(($this->value)) : []; + $show_id = !empty($this->options['show_id']); $form['value'] = [ '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', ['@voc' => $vocabulary->label()]) : $this->t('Select terms'), '#type' => 'textfield', - '#default_value' => EntityAutocomplete::getEntityLabels($terms), + '#default_value' => EntityAutocomplete::getEntityLabels($terms, $show_id), ]; if ($this->options['limit']) { @@ -188,6 +189,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) { $form['value']['#selection_settings']['target_bundles'] = [$vocabulary->id()]; $form['value']['#tags'] = TRUE; $form['value']['#process_default_value'] = FALSE; + if (!$show_id) { + $form['value']['#attributes']['data-drupal-autocomplete-hide-ids'] = ''; + } } } else { diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php index bb2e318adc..2eeeef38dc 100644 --- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php +++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepth.php @@ -28,6 +28,7 @@ protected function defineOptions() { $options = parent::defineOptions(); $options['depth'] = ['default' => 0]; + $options['show_id'] = ['default' => TRUE]; return $options; } @@ -41,6 +42,13 @@ public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { '#default_value' => $this->options['depth'], '#description' => $this->t('The depth will match nodes tagged with terms in the hierarchy. For example, if you have the term "fruit" and a child term "apple", with a depth of 1 (or higher) then filtering for the term "fruit" will get nodes that are tagged with "apple" as well as "fruit". If negative, the reverse is true; searching for "apple" will also pick up nodes tagged with "fruit" if depth is -1 (or lower).'), ]; + + $form['show_id'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Show Entity ID'), + '#default_value' => $this->options['show_id'], + '#description' => $this->t('Will display the entity id of the selection. When disabled, the entity id will still be shown for items with duplicate titles.'), + ]; } public function query() { diff --git a/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php b/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php index 65c925320e..7146dfdc70 100644 --- a/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php @@ -112,6 +112,7 @@ protected function getExpectedNormalizedEntity() { 'match_limit' => 10, 'size' => 60, 'placeholder' => '', + 'show_id' => TRUE, ], 'region' => 'content', 'third_party_settings' => [],