Same filename and directory in other branches
  1. 8.9.x core/modules/field_ui/field_ui.module
  2. 9 core/modules/field_ui/field_ui.module

Allows administrators to attach custom fields to fieldable types.

File

core/modules/field_ui/field_ui.module
View source
<?php

/**
 * @file
 * Allows administrators to attach custom fields to fieldable types.
 */
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityViewModeInterface;
use Drupal\Core\Entity\EntityFormModeInterface;
use Drupal\Core\Url;
use Drupal\field\FieldConfigInterface;
use Drupal\field_ui\FieldUI;
use Drupal\field_ui\Form\FieldConfigEditForm;
use Drupal\field_ui\Form\FieldStorageConfigEditForm;
use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask;

/**
 * Implements hook_help().
 */
function field_ui_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.field_ui':
      $output = '';
      $output .= '<h2>' . t('About') . '</h2>';
      $output .= '<p>' . t('The Field UI module provides an administrative user interface (UI) for managing and displaying fields. Fields can be attached to most content entity sub-types. Different field types, widgets, and formatters are provided by the modules installed on your site, and managed by the Field module. For background information and terminology related to fields and entities, see the <a href=":field">Field module help page</a>. For more information about the Field UI, see the <a href=":field_ui_docs">online documentation for the Field UI module</a>.', [
        ':field' => Url::fromRoute('help.page', [
          'name' => 'field',
        ])
          ->toString(),
        ':field_ui_docs' => 'https://www.drupal.org/documentation/modules/field-ui',
      ]) . '</p>';
      $output .= '<h2>' . t('Uses') . '</h2>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating a field') . '</dt>';
      $output .= '<dd>' . t('On the <em>Manage fields</em> page for your entity type or sub-type, you can add, configure, and delete fields for that entity type or sub-type. Each field has a <em>machine name</em>, which is used internally to identify the field and must be unique across an entity type; once a field is created, you cannot change the machine name. Most fields have two types of settings. The field-level settings depend on the field type, and affect how the data in the field is stored. Once they are set, they can no longer be changed; examples include how many data values are allowed for the field and where files are stored. The sub-type-level settings are specific to each entity sub-type the field is used on, and they can be changed later; examples include the field label, help text, default value, and whether the field is required or not. You can return to these settings by choosing the <em>Edit</em> link for the field from the <em>Manage fields</em> page.');
      $output .= '<dt>' . t('Re-using fields') . '</dt>';
      $output .= '<dd>' . t('Once you have created a field, you can use it again in other sub-types of the same entity type. For instance, if you create a field for the article content type, you can also use it for the page content type, but you cannot use it for content blocks or taxonomy terms. If there are fields available for re-use, after clicking <em>Add field</em> from the <em>Manage fields</em> page, you will see a list of available fields for re-use. After selecting a field for re-use, you can configure the sub-type-level settings.') . '</dd>';
      $output .= '<dt>' . t('Configuring field editing') . '</dt>';
      $output .= '<dd>' . t('On the <em>Manage form display</em> page of your entity type or sub-type, you can configure how the field data is edited by default and in each form mode. If your entity type has multiple form modes (on most sites, most entities do not), you can toggle between the form modes at the top of the page, and you can toggle whether each form mode uses the default settings or custom settings in the <em>Custom display settings</em> section. For each field in each form mode, you can select the widget to use for editing; some widgets have additional configuration options, such as the size for a text field, and these can be edited using the Edit button (which looks like a wheel). You can also change the order of the fields on the form. You can exclude a field from a form by choosing <em>Hidden</em> from the widget drop-down list, or by dragging it into the <em>Disabled</em> section.') . '</dd>';
      $output .= '<dt>' . t('Configuring field display') . '</dt>';
      $output .= '<dd>' . t('On the <em>Manage display</em> page of your entity type or sub-type, you can configure how each field is displayed by default and in each view mode. If your entity type has multiple view modes, you can toggle between the view modes at the top of the page, and you can toggle whether each view mode uses the default settings or custom settings in the <em>Custom display settings</em> section. For each field in each view mode, you can choose whether and how to display the label of the field from the <em>Label</em> drop-down list. You can also select the formatter to use for display; some formatters have configuration options, which you can edit using the Edit button (which looks like a wheel). You can also change the display order of fields. You can exclude a field from a specific view mode by choosing <em>Hidden</em> from the formatter drop-down list, or by dragging it into the <em>Disabled</em> section.') . '</dd>';
      $output .= '<dt>' . t('Configuring view and form modes') . '</dt>';
      $output .= '<dd>' . t('You can add, edit, and delete view modes for entities on the <a href=":view_modes">View modes page</a>, and you can add, edit, and delete form modes for entities on the <a href=":form_modes">Form modes page</a>. Once you have defined a view mode or form mode for an entity type, it will be available on the Manage display or Manage form display page for each sub-type of that entity.', [
        ':view_modes' => Url::fromRoute('entity.entity_view_mode.collection')
          ->toString(),
        ':form_modes' => Url::fromRoute('entity.entity_form_mode.collection')
          ->toString(),
      ]) . '</dd>';
      $output .= '<dt>' . t('Listing fields') . '</dt>';
      $output .= '<dd>' . t('There are two reports available that list the fields defined on your site. The <a href=":entity-list" title="Entities field list report">Entities</a> report lists all your fields, showing the field machine names, types, and the entity types or sub-types they are used on (each sub-type links to the Manage fields page). If the <a href=":views">Views</a> and <a href=":views-ui">Views UI</a> modules are installed, the <a href=":views-list" title="Used in views field list report">Used in views</a> report lists each field that is used in a view, with a link to edit that view.', [
        ':entity-list' => Url::fromRoute('entity.field_storage_config.collection')
          ->toString(),
        ':views-list' => \Drupal::moduleHandler()
          ->moduleExists('views_ui') ? Url::fromRoute('views_ui.reports_fields')
          ->toString() : '#',
        ':views' => \Drupal::moduleHandler()
          ->moduleExists('views') ? Url::fromRoute('help.page', [
          'name' => 'views',
        ])
          ->toString() : '#',
        ':views-ui' => \Drupal::moduleHandler()
          ->moduleExists('views_ui') ? Url::fromRoute('help.page', [
          'name' => 'views_ui',
        ])
          ->toString() : '#',
      ]) . '</dd>';
      $output .= '</dl>';
      return $output;
    case 'entity.field_storage_config.collection':
      return '<p>' . t('This list shows all fields currently in use for easy reference.') . '</p>';
  }
}

/**
 * Implements hook_theme().
 */
function field_ui_theme() {
  return [
    'field_ui_table' => [
      'variables' => [
        'header' => NULL,
        'rows' => NULL,
        'footer' => NULL,
        'attributes' => [],
        'caption' => NULL,
        'colgroups' => [],
        'sticky' => FALSE,
        'responsive' => TRUE,
        'empty' => '',
      ],
    ],
    // Provide a dedicated template for new storage options as their styling
    // is quite different from a typical form element, so it works best to not
    // include default form element classes.
    'form_element__new_storage_type' => [
      'base hook' => 'form_element',
      'render element' => 'element',
    ],
  ];
}

/**
 * Implements hook_entity_type_build().
 */
function field_ui_entity_type_build(array &$entity_types) {

  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  $entity_types['field_config']
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\FieldConfigEditForm');
  $entity_types['field_config']
    ->setFormClass('default', FieldConfigEditForm::class);
  $entity_types['field_config']
    ->setFormClass('delete', 'Drupal\\field_ui\\Form\\FieldConfigDeleteForm');
  $entity_types['field_config']
    ->setListBuilderClass('Drupal\\field_ui\\FieldConfigListBuilder');
  $entity_types['field_storage_config']
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\FieldStorageConfigEditForm');
  $entity_types['field_storage_config']
    ->setFormClass('default', FieldStorageConfigEditForm::class);
  $entity_types['field_storage_config']
    ->setListBuilderClass('Drupal\\field_ui\\FieldStorageConfigListBuilder');
  $entity_types['field_storage_config']
    ->setLinkTemplate('collection', '/admin/reports/fields');
  $entity_types['entity_form_display']
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\EntityFormDisplayEditForm');
  $entity_types['entity_view_display']
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\EntityViewDisplayEditForm');
  $form_mode = $entity_types['entity_form_mode'];
  $form_mode
    ->setListBuilderClass('Drupal\\field_ui\\EntityFormModeListBuilder');
  $form_mode
    ->setFormClass('add', 'Drupal\\field_ui\\Form\\EntityFormModeAddForm');
  $form_mode
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\EntityDisplayModeEditForm');
  $form_mode
    ->setFormClass('delete', 'Drupal\\field_ui\\Form\\EntityDisplayModeDeleteForm');
  $form_mode
    ->set('admin_permission', 'administer display modes');
  $form_mode
    ->setLinkTemplate('delete-form', '/admin/structure/display-modes/form/manage/{entity_form_mode}/delete');
  $form_mode
    ->setLinkTemplate('edit-form', '/admin/structure/display-modes/form/manage/{entity_form_mode}');
  $form_mode
    ->setLinkTemplate('add-form', '/admin/structure/display-modes/form/add/{entity_type_id}');
  $form_mode
    ->setLinkTemplate('collection', '/admin/structure/display-modes/form');
  $view_mode = $entity_types['entity_view_mode'];
  $view_mode
    ->setListBuilderClass('Drupal\\field_ui\\EntityDisplayModeListBuilder');
  $view_mode
    ->setFormClass('add', 'Drupal\\field_ui\\Form\\EntityDisplayModeAddForm');
  $view_mode
    ->setFormClass('edit', 'Drupal\\field_ui\\Form\\EntityDisplayModeEditForm');
  $view_mode
    ->setFormClass('delete', 'Drupal\\field_ui\\Form\\EntityDisplayModeDeleteForm');
  $view_mode
    ->set('admin_permission', 'administer display modes');
  $view_mode
    ->setLinkTemplate('delete-form', '/admin/structure/display-modes/view/manage/{entity_view_mode}/delete');
  $view_mode
    ->setLinkTemplate('edit-form', '/admin/structure/display-modes/view/manage/{entity_view_mode}');
  $view_mode
    ->setLinkTemplate('add-form', '/admin/structure/display-modes/view/add/{entity_type_id}');
  $view_mode
    ->setLinkTemplate('collection', '/admin/structure/display-modes/view');
}

/**
 * Implements hook_entity_bundle_create().
 */
function field_ui_entity_bundle_create($entity_type, $bundle) {

  // When a new bundle is created, the menu needs to be rebuilt to add our
  // menu item tabs.
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
}

/**
 * Implements hook_entity_operation().
 */
function field_ui_entity_operation(EntityInterface $entity) {
  $operations = [];
  $info = $entity
    ->getEntityType();

  // Add manage fields and display links if this entity type is the bundle
  // of another and that type has field UI enabled.
  if (($bundle_of = $info
    ->getBundleOf()) && \Drupal::entityTypeManager()
    ->getDefinition($bundle_of)
    ->get('field_ui_base_route')) {
    $account = \Drupal::currentUser();
    if ($account
      ->hasPermission('administer ' . $bundle_of . ' fields')) {
      $operations['manage-fields'] = [
        'title' => t('Manage fields'),
        'weight' => 15,
        'url' => Url::fromRoute("entity.{$bundle_of}.field_ui_fields", [
          $entity
            ->getEntityTypeId() => $entity
            ->id(),
        ]),
      ];
    }
    if ($account
      ->hasPermission('administer ' . $bundle_of . ' form display')) {
      $operations['manage-form-display'] = [
        'title' => t('Manage form display'),
        'weight' => 20,
        'url' => Url::fromRoute("entity.entity_form_display.{$bundle_of}.default", [
          $entity
            ->getEntityTypeId() => $entity
            ->id(),
        ]),
      ];
    }
    if ($account
      ->hasPermission('administer ' . $bundle_of . ' display')) {
      $operations['manage-display'] = [
        'title' => t('Manage display'),
        'weight' => 25,
        'url' => Url::fromRoute("entity.entity_view_display.{$bundle_of}.default", [
          $entity
            ->getEntityTypeId() => $entity
            ->id(),
        ]),
      ];
    }
  }
  return $operations;
}

/**
 * Implements hook_entity_view_mode_presave().
 */
function field_ui_entity_view_mode_presave(EntityViewModeInterface $view_mode) {
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
}

/**
 * Implements hook_entity_form_mode_presave().
 */
function field_ui_entity_form_mode_presave(EntityFormModeInterface $form_mode) {
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
}

/**
 * Implements hook_entity_view_mode_delete().
 */
function field_ui_entity_view_mode_delete(EntityViewModeInterface $view_mode) {
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
}

/**
 * Implements hook_entity_form_mode_delete().
 */
function field_ui_entity_form_mode_delete(EntityFormModeInterface $form_mode) {
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
}

/**
 * Prepares variables for field UI overview table templates.
 *
 * Default template: field-ui-table.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing a Form API structure to be
 *     rendered as a table.
 */
function template_preprocess_field_ui_table(&$variables) {
  template_preprocess_table($variables);
}

/**
 * Implements hook_local_tasks_alter().
 */
function field_ui_local_tasks_alter(&$local_tasks) {
  $container = \Drupal::getContainer();
  $local_task = FieldUiLocalTask::create($container, 'field_ui.fields');
  $local_task
    ->alterLocalTasks($local_tasks);
}

/**
 * Implements hook_form_FORM_ID_alter() for 'field_ui_field_storage_add_form'.
 */
function field_ui_form_field_ui_field_storage_add_form_alter(array &$form) {
  $optgroup = (string) t('Reference');

  // Move the "Entity reference" option to the end of the list and rename it to
  // "Other".
  unset($form['add']['new_storage_type']['#options'][$optgroup]['entity_reference']);
  $form['add']['new_storage_type']['#options'][$optgroup]['entity_reference'] = t('Other…');
}

/**
 * Implements hook_preprocess_HOOK().
 */
function field_ui_preprocess_form_element__new_storage_type(&$variables) {

  // Add support for a variant string so radios in the add field form can be
  // programmatically distinguished.
  $variables['variant'] = $variables['element']['#variant'] ?? NULL;
}

/**
 * Implements hook_form_alter().
 *
 * Adds a button 'Save and manage fields' to forms.
 *
 * @see \Drupal\node\NodeTypeForm
 * @see \Drupal\comment\CommentTypeForm
 * @see \Drupal\media\MediaTypeForm
 * @see \Drupal\block_content\BlockContentTypeForm
 * @see field_ui_form_manage_field_form_submit()
 */
function field_ui_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $forms = [
    'node_type_add_form',
    'comment_type_add_form',
    'media_type_add_form',
    'block_content_type_add_form',
  ];
  if (!in_array($form_id, $forms)) {
    return;
  }
  if ($form_state
    ->getFormObject()
    ->getEntity()
    ->isNew()) {
    $form['actions']['save_continue'] = $form['actions']['submit'];
    unset($form['actions']['submit']['#button_type']);
    $form['actions']['save_continue']['#value'] = t('Save and manage fields');
    $form['actions']['save_continue']['#weight'] = $form['actions']['save_continue']['#weight'] - 5;
    $form['actions']['save_continue']['#submit'][] = 'field_ui_form_manage_field_form_submit';
  }
}

/**
 * Form submission handler for the 'Save and manage fields' button.
 *
 * @see field_ui_form_alter()
 */
function field_ui_form_manage_field_form_submit($form, FormStateInterface $form_state) {
  $provider = $form_state
    ->getFormObject()
    ->getEntity()
    ->getEntityType()
    ->getProvider();
  $id = $form_state
    ->getFormObject()
    ->getEntity()
    ->id();
  if ($form_state
    ->getTriggeringElement()['#parents'][0] === 'save_continue' && ($route_info = FieldUI::getOverviewRouteInfo($provider, $id))) {
    $form_state
      ->setRedirectUrl($route_info);
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for field_config_edit_form.
 */
function field_ui_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state) {
  $field_config = $form_state
    ->getFormObject()
    ->getEntity();
  assert($field_config instanceof FieldConfigInterface);
  $form_id = 'field_storage_config_edit_form';
  $hook = 'form_' . $form_id;
  $field_storage_form = \Drupal::entityTypeManager()
    ->getFormObject('field_storage_config', $form_state
    ->getFormObject()
    ->getOperation());
  $field_storage_form
    ->setEntity($field_config
    ->getFieldStorageDefinition());
  $subform_state = SubformState::createForSubform($form['field_storage']['subform'], $form, $form_state, $field_storage_form);
  \Drupal::moduleHandler()
    ->alterDeprecated('Use hook_form_field_config_edit_form_alter() instead. See https://www.drupal.org/node/3386675.', $hook, $form['field_storage']['subform'], $subform_state, $form_id);
  \Drupal::theme()
    ->alter($hook, $form['field_storage']['subform'], $subform_state, $form_id);
}

Functions

Namesort descending Description
field_ui_entity_bundle_create Implements hook_entity_bundle_create().
field_ui_entity_form_mode_delete Implements hook_entity_form_mode_delete().
field_ui_entity_form_mode_presave Implements hook_entity_form_mode_presave().
field_ui_entity_operation Implements hook_entity_operation().
field_ui_entity_type_build Implements hook_entity_type_build().
field_ui_entity_view_mode_delete Implements hook_entity_view_mode_delete().
field_ui_entity_view_mode_presave Implements hook_entity_view_mode_presave().
field_ui_form_alter Implements hook_form_alter().
field_ui_form_field_config_edit_form_alter Implements hook_form_FORM_ID_alter() for field_config_edit_form.
field_ui_form_field_ui_field_storage_add_form_alter Implements hook_form_FORM_ID_alter() for 'field_ui_field_storage_add_form'.
field_ui_form_manage_field_form_submit Form submission handler for the 'Save and manage fields' button.
field_ui_help Implements hook_help().
field_ui_local_tasks_alter Implements hook_local_tasks_alter().
field_ui_preprocess_form_element__new_storage_type Implements hook_preprocess_HOOK().
field_ui_theme Implements hook_theme().
template_preprocess_field_ui_table Prepares variables for field UI overview table templates.