Skip to content
Snippets Groups Projects
Commit 9c7640c2 authored by Ben Mullins's avatar Ben Mullins
Browse files

Issue #2458127 by lauriii, benjifisher, hooroomoo, chrisfromredfin,...

Issue #2458127 by lauriii, benjifisher, hooroomoo, chrisfromredfin, marcoscano, izus, tim.plunkett, Gábor Hojtsy, jibran, leanderl, vacho, smustgrave, mcdwayne, amateescu, yched, larowlan, Berdir: Show field/field-storage summary instead of field type on field listings
parent ac64dce7
No related branches found
No related tags found
26 merge requests!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!4100Issue #3249600: Add support for PHP 8.1 Enums as allowed values for list_* data types,!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,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!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,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer,!23Issue #2879087: Use comment access handler instead of hardcoding permissions
Showing
with 309 additions and 33 deletions
......@@ -33,6 +33,20 @@ public static function defaultFieldSettings() {
return [];
}
/**
* {@inheritdoc}
*/
public static function storageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array {
return [];
}
/**
* {@inheritdoc}
*/
public static function fieldSettingsSummary(FieldDefinitionInterface $field_definition): array {
return [];
}
/**
* {@inheritdoc}
*/
......
......@@ -262,6 +262,38 @@ public static function defaultStorageSettings();
*/
public static function defaultFieldSettings();
/**
* Returns a short summary of the field's storage-level settings.
*
* All information returned by this function should communicate fundamental
* information about the field storage settings for users. For example, in the
* case of a reference field, the configured target entity type is a crucial
* piece of information for understanding how the field can be used.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field storage definition.
*
* @return array
* A renderable array summarizing storage-level settings.
*/
public static function storageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array;
/**
* Returns a short summary of the field's field-level settings.
*
* All information returned by this function should communicate fundamental
* information about the field settings for users. For example, in the case of
* a reference field, the selected target entity bundles are a crucial
* piece of information for understanding how the field can be used.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field entity.
*
* @return array
* A renderable array summarizing the field-level settings.
*/
public static function fieldSettingsSummary(FieldDefinitionInterface $field_definition): array;
/**
* Returns a settings array that can be stored as a configuration value.
*
......
......@@ -121,6 +121,30 @@ public function getDefaultFieldSettings($type) {
return [];
}
/**
* {@inheritdoc}
*/
public function getStorageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array {
$plugin_definition = $this->getDefinition($storage_definition->getType(), FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($storage_definition->getType(), $plugin_definition);
return $plugin_class::storageSettingsSummary($storage_definition);
}
return [];
}
/**
* {@inheritdoc}
*/
public function getFieldSettingsSummary(FieldDefinitionInterface $field_definition): array {
$plugin_definition = $this->getDefinition($field_definition->getType(), FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($field_definition->getType(), $plugin_definition);
return $plugin_class::fieldSettingsSummary($field_definition);
}
return [];
}
/**
* {@inheritdoc}
*/
......
......@@ -76,6 +76,30 @@ public function getDefaultFieldSettings($type);
*/
public function getDefaultStorageSettings($type);
/**
* Returns the summary of storage-level settings for a field type.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field storage definition.
*
* @return array
* A renderable array for the field's storage-level settings summary, as
* provided by the plugin definition.
*/
public function getStorageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array;
/**
* Returns the summary of field-level settings for a field type.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field entity.
*
* @return array
* A renderable array for the field's field-level settings summary, as
* provided by the plugin definition.
*/
public function getFieldSettingsSummary(FieldDefinitionInterface $field_definition): array;
/**
* Gets the definition of all field types that can be added via UI.
*
......
......@@ -2,6 +2,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\ContentEntityStorageInterface;
......@@ -110,6 +111,49 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
return $properties;
}
/**
* {@inheritdoc}
*/
public static function storageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array {
$summary = parent::storageSettingsSummary($storage_definition);
$target_type = $storage_definition->getSetting('target_type');
$target_type_info = \Drupal::entityTypeManager()->getDefinition($target_type);
if (!empty($target_type_info)) {
$summary[] = new TranslatableMarkup('Reference type: @entity_type', [
'@entity_type' => $target_type_info->getLabel(),
]);
}
return $summary;
}
/**
* {@inheritdoc}
*/
public static function fieldSettingsSummary(FieldDefinitionInterface $field_definition): array {
$summary = parent::fieldSettingsSummary($field_definition);
$target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
$handler_settings = $field_definition->getSetting('handler_settings');
if (!isset($handler_settings['target_bundles'])) {
return $summary;
}
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_information */
$entity_bundle_information = \Drupal::service('entity_type.bundle.info');
$bundle_info = $entity_bundle_information->getBundleInfo($target_type);
$bundles = array_map(fn($bundle) => $bundle_info[$bundle]['label'], $handler_settings['target_bundles']);
$bundle_label = \Drupal::entityTypeManager()->getDefinition($target_type)->getBundleLabel();
if (!empty($bundles)) {
$summary[] = new FormattableMarkup('@bundle: @entity_type', [
'@bundle' => $bundle_label ?: new TranslatableMarkup('Bundle'),
'@entity_type' => implode(', ', $bundles),
]);
}
return $summary;
}
/**
* {@inheritdoc}
*/
......
......@@ -10,6 +10,17 @@
.field-ui-overview .region-message td {
font-style: italic;
}
.field-settings-summary-cell li,
.storage-settings-summary-cell li {
margin: 0;
list-style-type: none;
}
.field-settings-summary-cell li {
font-size: 0.9em;
}
.field-settings-summary-cell li:first-child {
font-size: 1em;
}
/* 'Manage form display' and 'Manage display' overview */
.field-ui-overview .field-plugin-summary-cell {
......
......@@ -9,7 +9,6 @@
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Url;
use Drupal\field\FieldConfigInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -95,6 +94,7 @@ public function render($target_entity_type_id = NULL, $target_bundle = NULL) {
$build = parent::render();
$build['table']['#attributes']['id'] = 'field-overview';
$build['table']['#empty'] = $this->t('No fields are present yet.');
$build['#attached']['library'][] = 'field_ui/drupal.field_ui';
return $build;
}
......@@ -123,7 +123,7 @@ public function buildHeader() {
'data' => $this->t('Machine name'),
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
],
'field_type' => $this->t('Field type'),
'settings_summary' => $this->t('Field type'),
];
return $header + parent::buildHeader();
}
......@@ -134,23 +134,28 @@ public function buildHeader() {
public function buildRow(EntityInterface $field_config) {
/** @var \Drupal\field\FieldConfigInterface $field_config */
$field_storage = $field_config->getFieldStorageDefinition();
$route_parameters = [
'field_config' => $field_config->id(),
] + FieldUI::getRouteBundleParameter($this->entityTypeManager->getDefinition($this->targetEntityTypeId), $this->targetBundle);
$storage_summary = $this->fieldTypeManager->getStorageSettingsSummary($field_storage);
$instance_summary = $this->fieldTypeManager->getFieldSettingsSummary($field_config);
$summary_list = [...$storage_summary, ...$instance_summary];
$settings_summary = [
'data' => [
'#theme' => 'item_list',
'#items' => [
$this->fieldTypeManager->getDefinitions()[$field_storage->getType()]['label'],
...$summary_list,
],
],
'class' => ['field-settings-summary-cell'],
];
$row = [
'id' => Html::getClass($field_config->getName()),
'data' => [
'label' => $field_config->getLabel(),
'field_name' => $field_config->getName(),
'field_type' => [
'data' => [
'#type' => 'link',
'#title' => $this->fieldTypeManager->getDefinitions()[$field_storage->getType()]['label'],
'#url' => Url::fromRoute("entity.field_config.{$this->targetEntityTypeId}_storage_edit_form", $route_parameters),
'#options' => ['attributes' => ['title' => $this->t('Edit field settings.')]],
],
],
'settings_summary' => $settings_summary,
],
];
......
......@@ -81,6 +81,15 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
);
}
/**
* {@inheritdoc}
*/
public function render() {
$build = parent::render();
$build['#attached']['library'][] = 'field_ui/drupal.field_ui';
return $build;
}
/**
* {@inheritdoc}
*/
......@@ -92,6 +101,7 @@ public function buildHeader() {
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
];
$header['usage'] = $this->t('Used in');
$header['settings_summary'] = $this->t('Summary');
return $header;
}
......@@ -128,6 +138,14 @@ public function buildRow(EntityInterface $field_storage) {
'#items' => $usage,
'#context' => ['list_style' => 'comma-list'],
];
$summary = $this->fieldTypeManager->getStorageSettingsSummary($field_storage);
$row['data']['settings_summary'] = empty($summary) ? '' : [
'data' => [
'#theme' => 'item_list',
'#items' => $summary,
],
'class' => ['storage-settings-summary-cell'],
];
return $row;
}
......
......@@ -4,6 +4,8 @@
use Drupal\Tests\BrowserTestBase;
// cSpell:ignore downlander
/**
* Tests the Manage Display page of a fieldable entity type.
*
......@@ -38,8 +40,14 @@ protected function setUp(): void {
->save();
}
/**
* Tests drop button operations on the manage fields page.
*/
public function testFieldDropButtonOperations() {
$assert_session = $this->assertSession();
$node_type = $this->drupalCreateContentType();
$bundle = $node_type->id();
/** @var \Drupal\field\FieldStorageConfigInterface $storage */
$storage = $this->container->get('entity_type.manager')
......@@ -55,12 +63,53 @@ public function testFieldDropButtonOperations() {
->getStorage('field_config')
->create([
'field_storage' => $storage,
'bundle' => $node_type->id(),
'bundle' => $bundle,
])
->save();
$this->drupalGet('/admin/structure/types/manage/' . $node_type->id() . '/fields');
$this->drupalGet("/admin/structure/types/manage/{$bundle}/fields");
// Check that the summary element for the string field type exists and has
// the correct text (which comes from the FieldItemBase class).
$element = $assert_session->elementExists('css', '#highlander');
$summary = $assert_session->elementExists('css', '.field-settings-summary-cell > ul > li', $element);
$field_label = $this->container->get('plugin.manager.field.field_type')->getDefinitions()['string']['label'];
$this->assertEquals($field_label, $summary->getText());
// Add an entity reference field, and check that its summary is custom.
/** @var \Drupal\field\FieldStorageConfigInterface $storage */
$storage = $this->container->get('entity_type.manager')
->getStorage('field_storage_config')
->create([
'type' => 'entity_reference',
'field_name' => 'downlander',
'entity_type' => 'node',
'settings' => [
'target_type' => 'node',
],
]);
$storage->save();
$this->container->get('entity_type.manager')
->getStorage('field_config')
->create([
'field_storage' => $storage,
'bundle' => $bundle,
'entity_type' => 'node',
'settings' => [
'handler_settings' => [
'target_bundles' => [$bundle => $bundle],
],
],
])
->save();
$this->drupalGet("/admin/structure/types/manage/{$bundle}/fields");
$element = $assert_session->elementExists('css', '#downlander');
$custom_summary_text = 'Reference type: Content';
$allowed_bundles_text = "Content type: $bundle";
$this->assertStringContainsString($custom_summary_text, $element->getText());
$this->assertStringContainsString($allowed_bundles_text, $element->getText());
}
}
......@@ -106,6 +106,14 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
return $properties;
}
/**
* {@inheritdoc}
*/
public static function storageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array {
// Bypass the parent setting summary as it produces redundant information.
return [];
}
/**
* {@inheritdoc}
*/
......
......@@ -160,6 +160,14 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
return $properties;
}
/**
* {@inheritdoc}
*/
public static function storageSettingsSummary(FieldStorageDefinitionInterface $storage_definition): array {
// Bypass the parent setting summary as it produces redundant information.
return [];
}
/**
* {@inheritdoc}
*/
......
......@@ -32,6 +32,8 @@ protected function setDatabaseDumpFiles() {
* Tests that the content and configuration were properly updated.
*/
public function testUpdatedSite() {
$assert_session = $this->assertSession();
$this->runUpdates();
$spanish = \Drupal::languageManager()->getLanguage('es');
......@@ -222,24 +224,24 @@ public function testUpdatedSite() {
$this->drupalGet('admin/structure/types/manage/test_content_type/fields');
// Make sure fields are the right type.
$this->assertSession()->linkExists('Text (formatted, long, with summary)');
$this->assertSession()->linkExists('Boolean');
$this->assertSession()->linkExists('Comments');
$this->assertSession()->linkExists('Date');
$this->assertSession()->linkExists('Email');
$this->assertSession()->linkExists('Link');
$this->assertSession()->linkExists('List (float)');
$this->assertSession()->linkExists('Telephone number');
$this->assertSession()->linkExists('Entity reference');
$this->assertSession()->linkExists('File');
$this->assertSession()->linkExists('Image');
$this->assertSession()->linkExists('Text (plain, long)');
$this->assertSession()->linkExists('List (text)');
$this->assertSession()->linkExists('Text (formatted, long)');
$this->assertSession()->linkExists('Text (plain)');
$this->assertSession()->linkExists('List (integer)');
$this->assertSession()->linkExists('Number (integer)');
$this->assertSession()->linkExists('Number (float)');
$assert_session->elementContains('css', '#body', 'Text (formatted, long, with summary)');
$assert_session->elementContains('css', '#field-test-1', 'Boolean');
$assert_session->elementContains('css', '#field-test-2', 'Comments');
$assert_session->elementContains('css', '#field-test-3', 'Date');
$assert_session->elementContains('css', '#field-test-4', 'Email');
$assert_session->elementContains('css', '#field-test-5', 'Link');
$assert_session->elementContains('css', '#field-test-6', 'List (float)');
$assert_session->elementContains('css', '#field-test-7', 'Telephone number');
$assert_session->elementContains('css', '#field-test-8', 'Entity reference');
$assert_session->elementContains('css', '#field-test-9', 'File');
$assert_session->elementContains('css', '#field-test-10', 'Image');
$assert_session->elementContains('css', '#field-test-15', 'Text (plain, long)');
$assert_session->elementContains('css', '#field-test-16', 'List (text)');
$assert_session->elementContains('css', '#field-test-17', 'Text (formatted)');
$assert_session->elementContains('css', '#field-test-18', 'Text (formatted, long)');
$assert_session->elementContains('css', '#field-test-20', 'List (integer)');
$assert_session->elementContains('css', '#field-test-22', 'Number (float)');
$assert_session->elementContains('css', '#field-test-23', 'Number (integer)');
// Make sure our form mode exists.
$this->drupalGet('admin/structure/display-modes/form');
......
......@@ -80,3 +80,17 @@
.field-plugin-settings-edit-form .plugin-name {
font-weight: bold;
}
.field-settings-summary-cell.field-settings-summary-cell li,
.storage-settings-summary-cell.storage-settings-summary-cell li {
margin: 0;
list-style-type: none;
}
.field-settings-summary-cell li {
font-size: 0.9em;
}
.field-settings-summary-cell li:first-child {
font-size: 1em;
}
......@@ -63,3 +63,15 @@
.field-plugin-settings-edit-form .plugin-name {
font-weight: bold;
}
.field-settings-summary-cell.field-settings-summary-cell li,
.storage-settings-summary-cell.storage-settings-summary-cell li {
margin: 0;
list-style-type: none;
}
.field-settings-summary-cell li {
font-size: 0.9em;
}
.field-settings-summary-cell li:first-child {
font-size: 1em;
}
......@@ -10,6 +10,17 @@
.field-ui-overview .region-message td {
font-style: italic;
}
.field-settings-summary-cell li,
.storage-settings-summary-cell li {
margin: 0;
list-style-type: none;
}
.field-settings-summary-cell li {
font-size: 0.9em;
}
.field-settings-summary-cell li:first-child {
font-size: 1em;
}
/* 'Manage form display' and 'Manage display' overview */
.field-ui-overview .field-plugin-summary-cell {
......
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