Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -21,7 +21,8 @@ class FieldConfigListController extends EntityListController {
* The current route match.
*
* @return array
* A render array as expected by drupal_render().
* A render array as expected by
* \Drupal\Core\Render\RendererInterface::render().
*/
public function listing($entity_type_id = NULL, $bundle = NULL, RouteMatchInterface $route_match = NULL) {
return $this->entityManager()->getListBuilder('field_config')->render($entity_type_id, $bundle);

View file

@ -38,7 +38,7 @@ class FieldUiTable extends Table {
* @return array
* The $element with prepared variables ready for field-ui-table.html.twig.
*
* @see drupal_render()
* @see \Drupal\Core\Render\RendererInterface::render()
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
*/
public static function tablePreRender($elements) {
@ -159,7 +159,7 @@ class FieldUiTable extends Table {
$elements['#rows'][] = [
'class' => [
'region-title',
'region-' . $region_name_class . '-title'
'region-' . $region_name_class . '-title',
],
'no_striping' => TRUE,
'data' => [

View file

@ -36,6 +36,8 @@ class EntityDisplayModeListBuilder extends ConfigEntityListBuilder {
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, array $entity_types) {
parent::__construct($entity_type, $storage);
// Override the default limit (50) in order to display all view modes.
$this->limit = FALSE;
$this->entityTypes = $entity_types;
}

View file

@ -162,7 +162,7 @@ class FieldConfigListBuilder extends ConfigEntityListBuilder {
'weight' => 10,
'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-edit-form"),
'attributes' => [
'title' => $this->t('Edit field settings.')
'title' => $this->t('Edit field settings.'),
],
];
}
@ -172,7 +172,7 @@ class FieldConfigListBuilder extends ConfigEntityListBuilder {
'weight' => 100,
'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-delete-form"),
'attributes' => [
'title' => $this->t('Delete field.')
'title' => $this->t('Delete field.'),
],
];
}

View file

@ -82,6 +82,7 @@ class FieldStorageConfigListBuilder extends ConfigEntityListBuilder {
*/
public function buildHeader() {
$header['id'] = $this->t('Field name');
$header['entity_type'] = $this->t('Entity type');
$header['type'] = [
'data' => $this->t('Field type'),
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
@ -102,12 +103,15 @@ class FieldStorageConfigListBuilder extends ConfigEntityListBuilder {
$row['data']['id'] = $field_storage->getName();
}
$entity_type_id = $field_storage->getTargetEntityTypeId();
// Adding the entity type.
$row['data']['entity_type'] = $entity_type_id;
$field_type = $this->fieldTypes[$field_storage->getType()];
$row['data']['type'] = $this->t('@type (module: @module)', ['@type' => $field_type['label'], '@module' => $field_type['provider']]);
$usage = [];
foreach ($field_storage->getBundles() as $bundle) {
$entity_type_id = $field_storage->getTargetEntityTypeId();
if ($route_info = FieldUI::getOverviewRouteInfo($entity_type_id, $bundle)) {
$usage[] = \Drupal::l($this->bundles[$entity_type_id][$bundle]['label'], $route_info);
}

View file

@ -55,10 +55,10 @@ class FieldUiPermissions implements ContainerInjectionInterface {
'restrict access' => TRUE,
];
$permissions['administer ' . $entity_type_id . ' form display'] = [
'title' => $this->t('%entity_label: Administer form display', ['%entity_label' => $entity_type->getLabel()])
'title' => $this->t('%entity_label: Administer form display', ['%entity_label' => $entity_type->getLabel()]),
];
$permissions['administer ' . $entity_type_id . ' display'] = [
'title' => $this->t('%entity_label: Administer display', ['%entity_label' => $entity_type->getLabel()])
'title' => $this->t('%entity_label: Administer display', ['%entity_label' => $entity_type->getLabel()]),
];
}
}

View file

@ -94,11 +94,11 @@ abstract class EntityDisplayFormBase extends EntityForm {
'content' => [
'title' => $this->t('Content'),
'invisible' => TRUE,
'message' => $this->t('No field is displayed.')
'message' => $this->t('No field is displayed.'),
],
'hidden' => [
'title' => $this->t('Disabled', [], ['context' => 'Plural']),
'message' => $this->t('No field is hidden.')
'message' => $this->t('No field is hidden.'),
],
];
}
@ -125,7 +125,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
*/
protected function getFieldDefinitions() {
$context = $this->displayContext;
return array_filter($this->entityManager->getFieldDefinitions($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()), function(FieldDefinitionInterface $field_definition) use ($context) {
return array_filter($this->entityManager->getFieldDefinitions($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()), function (FieldDefinitionInterface $field_definition) use ($context) {
return $field_definition->isDisplayConfigurable($context);
});
}
@ -147,7 +147,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
];
if (empty($field_definitions) && empty($extra_fields) && $route_info = FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle())) {
drupal_set_message($this->t('There are no fields yet added. You can add new fields on the <a href=":link">Manage fields</a> page.', [':link' => $route_info->toString()]), 'warning');
$this->messenger()->addWarning($this->t('There are no fields yet added. You can add new fields on the <a href=":link">Manage fields</a> page.', [':link' => $route_info->toString()]));
return $form;
}
@ -210,6 +210,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
if ($enabled_displays = array_filter($this->getDisplayStatuses())) {
$default = array_keys(array_intersect_key($display_mode_options, $enabled_displays));
}
natcasesort($display_mode_options);
$form['modes']['display_modes_custom'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Use custom display settings for the following @display_context modes', ['@display_context' => $this->displayContext]),
@ -241,7 +242,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
// spinners will be added manually by the client-side script.
'progress' => 'none',
],
'#attributes' => ['class' => ['visually-hidden']]
'#attributes' => ['class' => ['visually-hidden']],
];
$form['actions'] = ['#type' => 'actions'];
@ -544,7 +545,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
$display_mode_label = $display_modes[$mode]['label'];
$url = $this->getOverviewUrl($mode);
drupal_set_message($this->t('The %display_mode mode now uses custom display settings. You might want to <a href=":url">configure them</a>.', ['%display_mode' => $display_mode_label, ':url' => $url->toString()]));
$this->messenger()->addStatus($this->t('The %display_mode mode now uses custom display settings. You might want to <a href=":url">configure them</a>.', ['%display_mode' => $display_mode_label, ':url' => $url->toString()]));
}
$statuses[$mode] = !empty($value);
}
@ -552,7 +553,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
$this->saveDisplayStatuses($statuses);
}
drupal_set_message($this->t('Your settings have been saved.'));
$this->messenger()->addStatus($this->t('Your settings have been saved.'));
}
/**
@ -702,7 +703,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
*
* @return array
*
* @see drupal_render()
* @see \Drupal\Core\Render\RendererInterface::render()
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
*
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.

View file

@ -7,6 +7,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides the add form for entity display modes.
*
* @internal
*/
class EntityDisplayModeAddForm extends EntityDisplayModeFormBase {

View file

@ -6,6 +6,8 @@ use Drupal\Core\Entity\EntityDeleteForm;
/**
* Provides the delete form for entity display modes.
*
* @internal
*/
class EntityDisplayModeDeleteForm extends EntityDeleteForm {

View file

@ -4,6 +4,8 @@ namespace Drupal\field_ui\Form;
/**
* Provides the edit form for entity display modes.
*
* @internal
*/
class EntityDisplayModeEditForm extends EntityDisplayModeFormBase {

View file

@ -78,7 +78,7 @@ abstract class EntityDisplayModeFormBase extends EntityForm {
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
drupal_set_message($this->t('Saved the %label @entity-type.', ['%label' => $this->entity->label(), '@entity-type' => $this->entityType->getLowercaseLabel()]));
$this->messenger()->addStatus($this->t('Saved the %label @entity-type.', ['%label' => $this->entity->label(), '@entity-type' => $this->entityType->getLowercaseLabel()]));
$this->entity->save();
\Drupal::entityManager()->clearCachedFieldDefinitions();
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));

View file

@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Edit form for the EntityFormDisplay entity type.
*
* @internal
*/
class EntityFormDisplayEditForm extends EntityDisplayFormBase {

View file

@ -6,6 +6,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides the add form for entity display modes.
*
* @internal
*/
class EntityFormModeAddForm extends EntityDisplayModeAddForm {

View file

@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Edit form for the EntityViewDisplay entity type.
*
* @internal
*/
class EntityViewDisplayEditForm extends EntityDisplayFormBase {
@ -71,8 +73,8 @@ class EntityViewDisplayEditForm extends EntityDisplayFormBase {
// Insert an empty placeholder for the label column.
$label = [
'empty_cell' => [
'#markup' => '&nbsp;'
]
'#markup' => '&nbsp;',
],
];
$label_position = array_search('plugin', array_keys($extra_field_row));
$extra_field_row = array_slice($extra_field_row, 0, $label_position, TRUE) + $label + array_slice($extra_field_row, $label_position, count($extra_field_row) - 1, TRUE);
@ -111,7 +113,7 @@ class EntityViewDisplayEditForm extends EntityDisplayFormBase {
/**
* {@inheritdoc}
*/
protected function getDisplayModesLink() {;
protected function getDisplayModesLink() {
return [
'#type' => 'link',
'#title' => t('Manage view modes'),

View file

@ -12,6 +12,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for removing a field from a bundle.
*
* @internal
*/
class FieldConfigDeleteForm extends EntityDeleteForm {
@ -96,10 +98,10 @@ class FieldConfigDeleteForm extends EntityDeleteForm {
if ($field_storage && !$field_storage->isLocked()) {
$this->entity->delete();
drupal_set_message($this->t('The field %field has been deleted from the %type content type.', ['%field' => $this->entity->label(), '%type' => $bundle_label]));
$this->messenger()->addStatus($this->t('The field %field has been deleted from the %type content type.', ['%field' => $this->entity->label(), '%type' => $bundle_label]));
}
else {
drupal_set_message($this->t('There was a problem removing the %field from the %type content type.', ['%field' => $this->entity->label(), '%type' => $bundle_label]), 'error');
$this->messenger()->addError($this->t('There was a problem removing the %field from the %type content type.', ['%field' => $this->entity->label(), '%type' => $bundle_label]));
}
$form_state->setRedirectUrl($this->getCancelUrl());

View file

@ -12,6 +12,8 @@ use Drupal\field_ui\FieldUI;
/**
* Provides a form for the field settings form.
*
* @internal
*/
class FieldConfigEditForm extends EntityForm {
@ -75,7 +77,7 @@ class FieldConfigEditForm extends EntityForm {
$ids = (object) [
'entity_type' => $this->entity->getTargetEntityTypeId(),
'bundle' => $this->entity->getTargetBundle(),
'entity_id' => NULL
'entity_id' => NULL,
];
$form['#entity'] = _field_create_entity_from_ids($ids);
$items = $form['#entity']->get($this->entity->getName());
@ -175,7 +177,7 @@ class FieldConfigEditForm extends EntityForm {
public function save(array $form, FormStateInterface $form_state) {
$this->entity->save();
drupal_set_message($this->t('Saved %label configuration.', ['%label' => $this->entity->getLabel()]));
$this->messenger()->addStatus($this->t('Saved %label configuration.', ['%label' => $this->entity->getLabel()]));
$request = $this->getRequest();
if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {

View file

@ -14,6 +14,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for the "field storage" add page.
*
* @internal
*/
class FieldStorageAddForm extends FormBase {
@ -310,14 +312,16 @@ class FieldStorageAddForm extends FormBase {
'translatable' => FALSE,
];
$widget_id = $formatter_id = NULL;
$widget_settings = $formatter_settings = [];
// Check if we're dealing with a preconfigured field.
if (strpos($field_storage_values['type'], 'field_ui:') !== FALSE) {
list(, $field_type, $option_key) = explode(':', $field_storage_values['type'], 3);
$field_storage_values['type'] = $field_type;
$field_type_class = $this->fieldTypePluginManager->getDefinition($field_type)['class'];
$field_options = $field_type_class::getPreconfiguredOptions()[$option_key];
$field_definition = $this->fieldTypePluginManager->getDefinition($field_type);
$options = $this->fieldTypePluginManager->getPreconfiguredOptions($field_definition['id']);
$field_options = $options[$option_key];
// Merge in preconfigured field storage options.
if (isset($field_options['field_storage_config'])) {
@ -338,7 +342,9 @@ class FieldStorageAddForm extends FormBase {
}
$widget_id = isset($field_options['entity_form_display']['type']) ? $field_options['entity_form_display']['type'] : NULL;
$widget_settings = isset($field_options['entity_form_display']['settings']) ? $field_options['entity_form_display']['settings'] : [];
$formatter_id = isset($field_options['entity_view_display']['type']) ? $field_options['entity_view_display']['type'] : NULL;
$formatter_settings = isset($field_options['entity_view_display']['settings']) ? $field_options['entity_view_display']['settings'] : [];
}
// Create the field storage and field.
@ -347,8 +353,8 @@ class FieldStorageAddForm extends FormBase {
$field = $this->entityManager->getStorage('field_config')->create($field_values);
$field->save();
$this->configureEntityFormDisplay($values['field_name'], $widget_id);
$this->configureEntityViewDisplay($values['field_name'], $formatter_id);
$this->configureEntityFormDisplay($values['field_name'], $widget_id, $widget_settings);
$this->configureEntityViewDisplay($values['field_name'], $formatter_id, $formatter_settings);
// Always show the field settings step, as the cardinality needs to be
// configured for new fields.
@ -364,7 +370,7 @@ class FieldStorageAddForm extends FormBase {
}
catch (\Exception $e) {
$error = TRUE;
drupal_set_message($this->t('There was a problem creating field %label: @message', ['%label' => $values['label'], '@message' => $e->getMessage()]), 'error');
$this->messenger()->addError($this->t('There was a problem creating field %label: @message', ['%label' => $values['label'], '@message' => $e->getMessage()]));
}
}
@ -395,7 +401,7 @@ class FieldStorageAddForm extends FormBase {
}
catch (\Exception $e) {
$error = TRUE;
drupal_set_message($this->t('There was a problem creating field %label: @message', ['%label' => $values['label'], '@message' => $e->getMessage()]), 'error');
$this->messenger()->addError($this->t('There was a problem creating field %label: @message', ['%label' => $values['label'], '@message' => $e->getMessage()]));
}
}
@ -405,7 +411,7 @@ class FieldStorageAddForm extends FormBase {
$form_state->setRedirectUrl(FieldUI::getNextDestination($destinations, $form_state));
}
elseif (!$error) {
drupal_set_message($this->t('Your settings have been saved.'));
$this->messenger()->addStatus($this->t('Your settings have been saved.'));
}
}
@ -416,12 +422,20 @@ class FieldStorageAddForm extends FormBase {
* The field name.
* @param string|null $widget_id
* (optional) The plugin ID of the widget. Defaults to NULL.
* @param array $widget_settings
* (optional) An array of widget settings. Defaults to an empty array.
*/
protected function configureEntityFormDisplay($field_name, $widget_id = NULL) {
protected function configureEntityFormDisplay($field_name, $widget_id = NULL, array $widget_settings = []) {
$options = [];
if ($widget_id) {
$options['type'] = $widget_id;
if (!empty($widget_settings)) {
$options['settings'] = $widget_settings;
}
}
// Make sure the field is displayed in the 'default' form mode (using
// default widget and settings). It stays hidden for other form modes
// until it is explicitly configured.
$options = $widget_id ? ['type' => $widget_id] : [];
entity_get_form_display($this->entityTypeId, $this->bundle, 'default')
->setComponent($field_name, $options)
->save();
@ -434,12 +448,20 @@ class FieldStorageAddForm extends FormBase {
* The field name.
* @param string|null $formatter_id
* (optional) The plugin ID of the formatter. Defaults to NULL.
* @param array $formatter_settings
* (optional) An array of formatter settings. Defaults to an empty array.
*/
protected function configureEntityViewDisplay($field_name, $formatter_id = NULL) {
protected function configureEntityViewDisplay($field_name, $formatter_id = NULL, array $formatter_settings = []) {
$options = [];
if ($formatter_id) {
$options['type'] = $formatter_id;
if (!empty($formatter_settings)) {
$options['settings'] = $formatter_settings;
}
}
// Make sure the field is displayed in the 'default' view mode (using
// default formatter and settings). It stays hidden for other view
// modes until it is explicitly configured.
$options = $formatter_id ? ['type' => $formatter_id] : [];
entity_get_display($this->entityTypeId, $this->bundle, 'default')
->setComponent($field_name, $options)
->save();

View file

@ -12,6 +12,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a form for the "field storage" edit page.
*
* @internal
*/
class FieldStorageConfigEditForm extends EntityForm {
@ -82,53 +84,80 @@ class FieldStorageConfigEditForm extends EntityForm {
$ids = (object) [
'entity_type' => $form_state->get('entity_type_id'),
'bundle' => $form_state->get('bundle'),
'entity_id' => NULL
'entity_id' => NULL,
];
$entity = _field_create_entity_from_ids($ids);
$items = $entity->get($this->entity->getName());
$item = $items->first() ?: $items->appendItem();
$form['settings'] += $item->storageSettingsForm($form, $form_state, $this->entity->hasData());
// Build the configurable field values.
$cardinality = $this->entity->getCardinality();
$form['cardinality_container'] = [
// Add the cardinality sub-form.
$form['cardinality_container'] = $this->getCardinalityForm();
return $form;
}
/**
* Builds the cardinality form.
*
* @return array
* The cardinality form render array.
*/
protected function getCardinalityForm() {
$form = [
// Reset #parents so the additional container does not appear.
'#parents' => [],
'#type' => 'fieldset',
'#title' => $this->t('Allowed number of values'),
'#attributes' => ['class' => [
'container-inline',
'fieldgroup',
'form-composite'
]],
];
$form['cardinality_container']['cardinality'] = [
'#type' => 'select',
'#title' => $this->t('Allowed number of values'),
'#title_display' => 'invisible',
'#options' => [
'number' => $this->t('Limited'),
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED => $this->t('Unlimited'),
],
'#default_value' => ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : 'number',
];
$form['cardinality_container']['cardinality_number'] = [
'#type' => 'number',
'#default_value' => $cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED ? $cardinality : 1,
'#min' => 1,
'#title' => $this->t('Limit'),
'#title_display' => 'invisible',
'#size' => 2,
'#states' => [
'visible' => [
':input[name="cardinality"]' => ['value' => 'number'],
],
'disabled' => [
':input[name="cardinality"]' => ['value' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED],
'#attributes' => [
'class' => [
'container-inline',
'fieldgroup',
'form-composite',
],
],
];
if ($enforced_cardinality = $this->getEnforcedCardinality()) {
if ($enforced_cardinality === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$markup = $this->t("This field cardinality is set to unlimited and cannot be configured.");
}
else {
$markup = $this->t("This field cardinality is set to @cardinality and cannot be configured.", ['@cardinality' => $enforced_cardinality]);
}
$form['cardinality'] = ['#markup' => $markup];
}
else {
$form['#element_validate'][] = '::validateCardinality';
$cardinality = $this->entity->getCardinality();
$form['cardinality'] = [
'#type' => 'select',
'#title' => $this->t('Allowed number of values'),
'#title_display' => 'invisible',
'#options' => [
'number' => $this->t('Limited'),
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED => $this->t('Unlimited'),
],
'#default_value' => ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) ? FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED : 'number',
];
$form['cardinality_number'] = [
'#type' => 'number',
'#default_value' => $cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED ? $cardinality : 1,
'#min' => 1,
'#title' => $this->t('Limit'),
'#title_display' => 'invisible',
'#size' => 2,
'#states' => [
'visible' => [
':input[name="cardinality"]' => ['value' => 'number'],
],
'disabled' => [
':input[name="cardinality"]' => ['value' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED],
],
],
];
}
return $form;
}
@ -143,16 +172,19 @@ class FieldStorageConfigEditForm extends EntityForm {
}
/**
* {@inheritdoc}
* Validates the cardinality.
*
* @param array $element
* The cardinality form render array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
public function validateCardinality(array &$element, FormStateInterface $form_state) {
$field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($this->entity->getTargetEntityTypeId());
// Validate field cardinality.
if ($form_state->getValue('cardinality') === 'number' && !$form_state->getValue('cardinality_number')) {
$form_state->setErrorByName('cardinality_number', $this->t('Number of values is required.'));
$form_state->setError($element['cardinality_number'], $this->t('Number of values is required.'));
}
// If a specific cardinality is used, validate that there are no entities
// with a higher delta.
@ -166,7 +198,7 @@ class FieldStorageConfigEditForm extends EntityForm {
->count()
->execute();
if ($entities_with_higher_delta) {
$form_state->setErrorByName('cardinality_number', $this->formatPlural($entities_with_higher_delta, 'There is @count entity with @delta or more values in this field.', 'There are @count entities with @delta or more values in this field.', ['@delta' => $form_state->getValue('cardinality') + 1]));
$form_state->setError($element['cardinality_number'], $this->formatPlural($entities_with_higher_delta, 'There is @count entity with @delta or more values in this field.', 'There are @count entities with @delta or more values in this field.', ['@delta' => $form_state->getValue('cardinality') + 1]));
}
}
}
@ -176,7 +208,7 @@ class FieldStorageConfigEditForm extends EntityForm {
*/
public function buildEntity(array $form, FormStateInterface $form_state) {
// Save field cardinality.
if ($form_state->getValue('cardinality') === 'number' && $form_state->getValue('cardinality_number')) {
if (!$this->getEnforcedCardinality() && $form_state->getValue('cardinality') === 'number' && $form_state->getValue('cardinality_number')) {
$form_state->setValue('cardinality', $form_state->getValue('cardinality_number'));
}
@ -190,7 +222,7 @@ class FieldStorageConfigEditForm extends EntityForm {
$field_label = $form_state->get('field_config')->label();
try {
$this->entity->save();
drupal_set_message($this->t('Updated field %label field settings.', ['%label' => $field_label]));
$this->messenger()->addStatus($this->t('Updated field %label field settings.', ['%label' => $field_label]));
$request = $this->getRequest();
if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
$request->query->remove('destinations');
@ -201,8 +233,23 @@ class FieldStorageConfigEditForm extends EntityForm {
}
}
catch (\Exception $e) {
drupal_set_message($this->t('Attempt to update field %label failed: %message.', ['%label' => $field_label, '%message' => $e->getMessage()]), 'error');
$this->messenger()->addStatus($this->t('Attempt to update field %label failed: %message.', ['%label' => $field_label, '%message' => $e->getMessage()]));
}
}
/**
* Returns the cardinality enforced by the field type.
*
* Some field types choose to enforce a fixed cardinality. This method
* returns that cardinality or NULL if no cardinality has been enforced.
*
* @return int|null
*/
protected function getEnforcedCardinality() {
/** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$definition = $field_type_manager->getDefinition($this->entity->getType());
return isset($definition['cardinality']) ? $definition['cardinality'] : NULL;
}
}

View file

@ -3,14 +3,15 @@
namespace Drupal\field_ui\Routing;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Enhances Field UI routes by adding proper information about the bundle name.
*/
class FieldUiRouteEnhancer implements RouteEnhancerInterface {
class FieldUiRouteEnhancer implements EnhancerInterface {
/**
* The entity manager.
@ -33,6 +34,10 @@ class FieldUiRouteEnhancer implements RouteEnhancerInterface {
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
if (!$this->applies($defaults[RouteObjectInterface::ROUTE_OBJECT])) {
return $defaults;
}
if (($bundle = $this->entityManager->getDefinition($defaults['entity_type_id'])->getBundleEntityType()) && isset($defaults[$bundle])) {
// Field UI forms only need the actual name of the bundle they're dealing
// with, not an upcasted entity object, so provide a simple way for them
@ -46,7 +51,7 @@ class FieldUiRouteEnhancer implements RouteEnhancerInterface {
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
protected function applies(Route $route) {
return ($route->hasOption('_field_ui'));
}

View file

@ -1,111 +0,0 @@
<?php
namespace Drupal\field_ui\Tests;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\simpletest\WebTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests deletion of a field and their dependencies in the UI.
*
* @group field_ui
*/
class FieldUIDeleteTest extends WebTestBase {
use FieldUiTestTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['node', 'field_ui', 'field_test', 'block', 'field_test_views'];
/**
* Test views to enable
*
* @var string[]
*/
public static $testViews = ['test_view_field_delete'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
// Create a test user.
$admin_user = $this->drupalCreateUser(['access content', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer users', 'administer account settings', 'administer user display', 'bypass node access']);
$this->drupalLogin($admin_user);
}
/**
* Tests that deletion removes field storages and fields as expected.
*/
public function testDeleteField() {
$field_label = $this->randomMachineName();
$field_name_input = 'test';
$field_name = 'field_test';
// Create an additional node type.
$type_name1 = strtolower($this->randomMachineName(8)) . '_test';
$type1 = $this->drupalCreateContentType(['name' => $type_name1, 'type' => $type_name1]);
$type_name1 = $type1->id();
// Create a new field.
$bundle_path1 = 'admin/structure/types/manage/' . $type_name1;
$this->fieldUIAddNewField($bundle_path1, $field_name_input, $field_label);
// Create an additional node type.
$type_name2 = strtolower($this->randomMachineName(8)) . '_test';
$type2 = $this->drupalCreateContentType(['name' => $type_name2, 'type' => $type_name2]);
$type_name2 = $type2->id();
// Add a field to the second node type.
$bundle_path2 = 'admin/structure/types/manage/' . $type_name2;
$this->fieldUIAddExistingField($bundle_path2, $field_name, $field_label);
\Drupal::service('module_installer')->install(['views']);
ViewTestData::createTestViews(get_class($this), ['field_test_views']);
// Check the config dependencies of the first field, the field storage must
// not be shown as being deleted yet.
$this->drupalGet("$bundle_path1/fields/node.$type_name1.$field_name/delete");
$this->assertNoText(t('The listed configuration will be deleted.'));
$this->assertNoText(t('View'));
$this->assertNoText('test_view_field_delete');
// Delete the first field.
$this->fieldUIDeleteField($bundle_path1, "node.$type_name1.$field_name", $field_label, $type_name1);
// Check that the field was deleted.
$this->assertNull(FieldConfig::loadByName('node', $type_name1, $field_name), 'Field was deleted.');
// Check that the field storage was not deleted
$this->assertNotNull(FieldStorageConfig::loadByName('node', $field_name), 'Field storage was not deleted.');
// Check the config dependencies of the first field.
$this->drupalGet("$bundle_path2/fields/node.$type_name2.$field_name/delete");
$this->assertText(t('The listed configuration will be deleted.'));
$this->assertText(t('View'));
$this->assertText('test_view_field_delete');
$xml = $this->cssSelect('#edit-entity-deletes');
// Remove the wrapping HTML.
$this->assertIdentical(FALSE, strpos($xml[0]->asXml(), $field_label), 'The currently being deleted field is not shown in the entity deletions.');
// Delete the second field.
$this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$field_name", $field_label, $type_name2);
// Check that the field was deleted.
$this->assertNull(FieldConfig::loadByName('node', $type_name2, $field_name), 'Field was deleted.');
// Check that the field storage was deleted too.
$this->assertNull(FieldStorageConfig::loadByName('node', $field_name), 'Field storage was deleted.');
}
}

View file

@ -2,7 +2,6 @@
namespace Drupal\field_ui\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityInterface;
@ -48,7 +47,7 @@ class ManageDisplayTest extends WebTestBase {
$vocabulary = Vocabulary::create([
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
'vid' => Unicode::strtolower($this->randomMachineName()),
'vid' => mb_strtolower($this->randomMachineName()),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'help' => '',
'nodes' => ['article' => 'article'],
@ -85,7 +84,7 @@ class ManageDisplayTest extends WebTestBase {
// Check whether formatter weights are respected.
$result = $this->xpath('//select[@id=:id]/option', [':id' => 'edit-fields-field-test-type']);
$options = array_map(function($item) {
$options = array_map(function ($item) {
return (string) $item->attributes()->value[0];
}, $result);
$expected_options = [
@ -119,7 +118,7 @@ class ManageDisplayTest extends WebTestBase {
$edit = [
'fields[field_test][type]' => 'field_test_multiple',
'fields[field_test][region]' => 'content',
'refresh_rows' => 'field_test'
'refresh_rows' => 'field_test',
];
$this->drupalPostAjaxForm(NULL, $edit, ['op' => t('Refresh')]);
$format = 'field_test_multiple';
@ -247,11 +246,12 @@ class ManageDisplayTest extends WebTestBase {
// Check whether widget weights are respected.
$result = $this->xpath('//select[@id=:id]/option', [':id' => 'edit-fields-field-test-type']);
$options = array_map(function($item) {
$options = array_map(function ($item) {
return (string) $item->attributes()->value[0];
}, $result);
$expected_options = [
'test_field_widget',
'test_field_widget_multilingual',
'test_field_widget_multiple',
];
$this->assertEqual($options, $expected_options, 'The expected widget ordering is respected.');
@ -313,8 +313,8 @@ class ManageDisplayTest extends WebTestBase {
$this->drupalGet($manage_display);
// Checks if the select elements contain the specified options.
$this->assertFieldSelectOptions('fields[field_test][type]', ['test_field_widget', 'test_field_widget_multiple']);
$this->assertFieldSelectOptions('fields[field_onewidgetfield][type]', ['test_field_widget']);
$this->assertFieldSelectOptions('fields[field_test][type]', ['test_field_widget', 'test_field_widget_multilingual', 'test_field_widget_multiple']);
$this->assertFieldSelectOptions('fields[field_onewidgetfield][type]', ['test_field_widget', 'test_field_widget_multilingual']);
// Ensure that fields can be hidden directly by changing the region.
$this->assertFieldByName('fields[field_test][region]', 'content');
@ -452,7 +452,7 @@ class ManageDisplayTest extends WebTestBase {
/**
* Asserts that a string is found in the rendered node in a view mode.
*
* @param EntityInterface $node
* @param \Drupal\Core\Entity\EntityInterface $node
* The node.
* @param $view_mode
* The view mode in which the node should be displayed.
@ -471,7 +471,7 @@ class ManageDisplayTest extends WebTestBase {
/**
* Asserts that a string is not found in the rendered node in a view mode.
*
* @param EntityInterface $node
* @param \Drupal\Core\Entity\EntityInterface $node
* The node.
* @param $view_mode
* The view mode in which the node should be displayed.
@ -487,12 +487,12 @@ class ManageDisplayTest extends WebTestBase {
}
/**
* Asserts that a string is (not) found in the rendered nodein a view mode.
* Asserts that a string is (not) found in the rendered node in a view mode.
*
* This helper function is used by assertNodeViewText() and
* assertNodeViewNoText().
*
* @param EntityInterface $node
* @param \Drupal\Core\Entity\EntityInterface $node
* The node.
* @param $view_mode
* The view mode in which the node should be displayed.

View file

@ -1,768 +0,0 @@
<?php
namespace Drupal\field_ui\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\simpletest\WebTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests the Field UI "Manage fields" screen.
*
* @group field_ui
*/
class ManageFieldsTest extends WebTestBase {
use FieldUiTestTrait;
use EntityReferenceTestTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['node', 'field_ui', 'field_test', 'taxonomy', 'image', 'block'];
/**
* The ID of the custom content type created for testing.
*
* @var string
*/
protected $contentType;
/**
* The label for a random field to be created for testing.
*
* @var string
*/
protected $fieldLabel;
/**
* The input name of a random field to be created for testing.
*
* @var string
*/
protected $fieldNameInput;
/**
* The name of a random field to be created for testing.
*
* @var string
*/
protected $fieldName;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
// Create a test user.
$admin_user = $this->drupalCreateUser(['access content', 'administer content types', 'administer node fields', 'administer node form display', 'administer node display', 'administer taxonomy', 'administer taxonomy_term fields', 'administer taxonomy_term display', 'administer users', 'administer account settings', 'administer user display', 'bypass node access']);
$this->drupalLogin($admin_user);
// Create content type, with underscores.
$type_name = strtolower($this->randomMachineName(8)) . '_test';
$type = $this->drupalCreateContentType(['name' => $type_name, 'type' => $type_name]);
$this->contentType = $type->id();
// Create random field name with markup to test escaping.
$this->fieldLabel = '<em>' . $this->randomMachineName(8) . '</em>';
$this->fieldNameInput = strtolower($this->randomMachineName(8));
$this->fieldName = 'field_' . $this->fieldNameInput;
// Create Basic page and Article node types.
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
// Create a vocabulary named "Tags".
$vocabulary = Vocabulary::create([
'name' => 'Tags',
'vid' => 'tags',
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
]);
$vocabulary->save();
$handler_settings = [
'target_bundles' => [
$vocabulary->id() => $vocabulary->id(),
],
];
$this->createEntityReferenceField('node', 'article', 'field_' . $vocabulary->id(), 'Tags', 'taxonomy_term', 'default', $handler_settings);
entity_get_form_display('node', 'article', 'default')
->setComponent('field_' . $vocabulary->id())
->save();
}
/**
* Runs the field CRUD tests.
*
* In order to act on the same fields, and not create the fields over and over
* again the following tests create, update and delete the same fields.
*/
public function testCRUDFields() {
$this->manageFieldsPage();
$this->createField();
$this->updateField();
$this->addExistingField();
$this->cardinalitySettings();
$this->fieldListAdminPage();
$this->deleteField();
$this->addPersistentFieldStorage();
}
/**
* Tests the manage fields page.
*
* @param string $type
* (optional) The name of a content type.
*/
public function manageFieldsPage($type = '') {
$type = empty($type) ? $this->contentType : $type;
$this->drupalGet('admin/structure/types/manage/' . $type . '/fields');
// Check all table columns.
$table_headers = [
t('Label'),
t('Machine name'),
t('Field type'),
t('Operations'),
];
foreach ($table_headers as $table_header) {
// We check that the label appear in the table headings.
$this->assertRaw($table_header . '</th>', format_string('%table_header table header was found.', ['%table_header' => $table_header]));
}
// Test the "Add field" action link.
$this->assertLink('Add field');
// Assert entity operations for all fields.
$number_of_links = 3;
$number_of_links_found = 0;
$operation_links = $this->xpath('//ul[@class = "dropbutton"]/li/a');
$url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body";
foreach ($operation_links as $link) {
switch ($link['title']) {
case 'Edit field settings.':
$this->assertIdentical($url, (string) $link['href']);
$number_of_links_found++;
break;
case 'Edit storage settings.':
$this->assertIdentical("$url/storage", (string) $link['href']);
$number_of_links_found++;
break;
case 'Delete field.':
$this->assertIdentical("$url/delete", (string) $link['href']);
$number_of_links_found++;
break;
}
}
$this->assertEqual($number_of_links, $number_of_links_found);
}
/**
* Tests adding a new field.
*
* @todo Assert properties can bet set in the form and read back in
* $field_storage and $fields.
*/
public function createField() {
// Create a test field.
$this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel);
}
/**
* Tests editing an existing field.
*/
public function updateField() {
$field_id = 'node.' . $this->contentType . '.' . $this->fieldName;
// Go to the field edit page.
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage');
$this->assertEscaped($this->fieldLabel);
// Populate the field settings with new settings.
$string = 'updated dummy test string';
$edit = [
'settings[test_field_storage_setting]' => $string,
];
$this->drupalPostForm(NULL, $edit, t('Save field settings'));
// Go to the field edit page.
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
$edit = [
'settings[test_field_setting]' => $string,
];
$this->assertText(t('Default value'), 'Default value heading is shown');
$this->drupalPostForm(NULL, $edit, t('Save settings'));
// Assert the field settings are correct.
$this->assertFieldSettings($this->contentType, $this->fieldName, $string);
// Assert redirection back to the "manage fields" page.
$this->assertUrl('admin/structure/types/manage/' . $this->contentType . '/fields');
}
/**
* Tests adding an existing field in another content type.
*/
public function addExistingField() {
// Check "Re-use existing field" appears.
$this->drupalGet('admin/structure/types/manage/page/fields/add-field');
$this->assertRaw(t('Re-use an existing field'), '"Re-use existing field" was found.');
// Check that fields of other entity types (here, the 'comment_body' field)
// do not show up in the "Re-use existing field" list.
$this->assertFalse($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.');
// Validate the FALSE assertion above by also testing a valid one.
$this->assertTrue($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => $this->fieldName]), 'The list of options shows a valid option.');
// Add a new field based on an existing field.
$this->fieldUIAddExistingField("admin/structure/types/manage/page", $this->fieldName, $this->fieldLabel . '_2');
}
/**
* Tests the cardinality settings of a field.
*
* We do not test if the number can be submitted with anything else than a
* numeric value. That is tested already in FormTest::testNumber().
*/
public function cardinalitySettings() {
$field_edit_path = 'admin/structure/types/manage/article/fields/node.article.body/storage';
// Assert the cardinality other field cannot be empty when cardinality is
// set to 'number'.
$edit = [
'cardinality' => 'number',
'cardinality_number' => '',
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertText('Number of values is required.');
// Submit a custom number.
$edit = [
'cardinality' => 'number',
'cardinality_number' => 6,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertText('Updated field Body field settings.');
$this->drupalGet($field_edit_path);
$this->assertFieldByXPath("//select[@name='cardinality']", 'number');
$this->assertFieldByXPath("//input[@name='cardinality_number']", 6);
// Check that tabs displayed.
$this->assertLink(t('Edit'));
$this->assertLinkByHref('admin/structure/types/manage/article/fields/node.article.body');
$this->assertLink(t('Field settings'));
$this->assertLinkByHref($field_edit_path);
// Add two entries in the body.
$edit = ['title[0][value]' => 'Cardinality', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2'];
$this->drupalPostForm('node/add/article', $edit, 'Save');
// Assert that you can't set the cardinality to a lower number than the
// highest delta of this field.
$edit = [
'cardinality' => 'number',
'cardinality_number' => 1,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.');
// Create a second entity with three values.
$edit = ['title[0][value]' => 'Cardinality 3', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2', 'body[2][value]' => 'Body 3'];
$this->drupalPostForm('node/add/article', $edit, 'Save');
// Set to unlimited.
$edit = [
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertText('Updated field Body field settings.');
$this->drupalGet($field_edit_path);
$this->assertFieldByXPath("//select[@name='cardinality']", FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$this->assertFieldByXPath("//input[@name='cardinality_number']", 1);
// Assert that you can't set the cardinality to a lower number then the
// highest delta of this field but can set it to the same.
$edit = [
'cardinality' => 'number',
'cardinality_number' => 1,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertRaw(t('There are @count entities with @delta or more values in this field.', ['@count' => 2, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.');
$edit = [
'cardinality' => 'number',
'cardinality_number' => 2,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
$this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 3]), 'Correctly failed to set cardinality lower than highest delta.');
$edit = [
'cardinality' => 'number',
'cardinality_number' => 3,
];
$this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
}
/**
* Tests deleting a field from the field edit form.
*/
protected function deleteField() {
// Delete the field.
$field_id = 'node.' . $this->contentType . '.' . $this->fieldName;
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
$this->clickLink(t('Delete'));
$this->assertResponse(200);
}
/**
* Tests that persistent field storage appears in the field UI.
*/
protected function addPersistentFieldStorage() {
$field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
// Persist the field storage even if there are no fields.
$field_storage->set('persist_with_no_fields', TRUE)->save();
// Delete all instances of the field.
foreach ($field_storage->getBundles() as $node_type) {
// Delete all the body field instances.
$this->drupalGet('admin/structure/types/manage/' . $node_type . '/fields/node.' . $node_type . '.' . $this->fieldName);
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, [], t('Delete'));
}
// Check "Re-use existing field" appears.
$this->drupalGet('admin/structure/types/manage/page/fields/add-field');
$this->assertRaw(t('Re-use an existing field'), '"Re-use existing field" was found.');
// Ensure that we test with a label that contains HTML.
$label = $this->randomString(4) . '<br/>' . $this->randomString(4);
// Add a new field for the orphaned storage.
$this->fieldUIAddExistingField("admin/structure/types/manage/page", $this->fieldName, $label);
}
/**
* Asserts field settings are as expected.
*
* @param $bundle
* The bundle name for the field.
* @param $field_name
* The field name for the field.
* @param $string
* The settings text.
* @param $entity_type
* The entity type for the field.
*/
public function assertFieldSettings($bundle, $field_name, $string = 'dummy test string', $entity_type = 'node') {
// Assert field storage settings.
$field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);
$this->assertTrue($field_storage->getSetting('test_field_storage_setting') == $string, 'Field storage settings were found.');
// Assert field settings.
$field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
$this->assertTrue($field->getSetting('test_field_setting') == $string, 'Field settings were found.');
}
/**
* Tests that the 'field_prefix' setting works on Field UI.
*/
public function testFieldPrefix() {
// Change default field prefix.
$field_prefix = strtolower($this->randomMachineName(10));
$this->config('field_ui.settings')->set('field_prefix', $field_prefix)->save();
// Create a field input and label exceeding the new maxlength, which is 22.
$field_exceed_max_length_label = $this->randomString(23);
$field_exceed_max_length_input = $this->randomMachineName(23);
// Try to create the field.
$edit = [
'label' => $field_exceed_max_length_label,
'field_name' => $field_exceed_max_length_input,
];
$this->drupalPostForm('admin/structure/types/manage/' . $this->contentType . '/fields/add-field', $edit, t('Save and continue'));
$this->assertText('Machine-readable name cannot be longer than 22 characters but is currently 23 characters long.');
// Create a valid field.
$this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel);
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_prefix . $this->fieldNameInput);
$this->assertText(format_string('@label settings for @type', ['@label' => $this->fieldLabel, '@type' => $this->contentType]));
}
/**
* Tests that default value is correctly validated and saved.
*/
public function testDefaultValue() {
// Create a test field storage and field.
$field_name = 'test';
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => 'test_field'
])->save();
$field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => $this->contentType,
]);
$field->save();
entity_get_form_display('node', $this->contentType, 'default')
->setComponent($field_name)
->save();
$admin_path = 'admin/structure/types/manage/' . $this->contentType . '/fields/' . $field->id();
$element_id = "edit-default-value-input-$field_name-0-value";
$element_name = "default_value_input[{$field_name}][0][value]";
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '', 'The default value widget was empty.');
// Check that invalid default values are rejected.
$edit = [$element_name => '-1'];
$this->drupalPostForm($admin_path, $edit, t('Save settings'));
$this->assertText("$field_name does not accept the value -1", 'Form validation failed.');
// Check that the default value is saved.
$edit = [$element_name => '1'];
$this->drupalPostForm($admin_path, $edit, t('Save settings'));
$this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');
$field = FieldConfig::loadByName('node', $this->contentType, $field_name);
$this->assertEqual($field->getDefaultValueLiteral(), [['value' => 1]], 'The default value was correctly saved.');
// Check that the default value shows up in the form
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '1', 'The default value widget was displayed with the correct value.');
// Check that the default value can be emptied.
$edit = [$element_name => ''];
$this->drupalPostForm(NULL, $edit, t('Save settings'));
$this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');
$field = FieldConfig::loadByName('node', $this->contentType, $field_name);
$this->assertEqual($field->getDefaultValueLiteral(), NULL, 'The default value was correctly saved.');
// Check that the default value can be empty when the field is marked as
// required and can store unlimited values.
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage->save();
$this->drupalGet($admin_path);
$edit = [
'required' => 1,
];
$this->drupalPostForm(NULL, $edit, t('Save settings'));
$this->drupalGet($admin_path);
$this->drupalPostForm(NULL, [], t('Save settings'));
$this->assertText("Saved $field_name configuration", 'The form was successfully submitted.');
$field = FieldConfig::loadByName('node', $this->contentType, $field_name);
$this->assertEqual($field->getDefaultValueLiteral(), NULL, 'The default value was correctly saved.');
// Check that the default widget is used when the field is hidden.
entity_get_form_display($field->getTargetEntityTypeId(), $field->getTargetBundle(), 'default')
->removeComponent($field_name)->save();
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '', 'The default value widget was displayed when field is hidden.');
}
/**
* Tests that deletion removes field storages and fields as expected.
*/
public function testDeleteField() {
// Create a new field.
$bundle_path1 = 'admin/structure/types/manage/' . $this->contentType;
$this->fieldUIAddNewField($bundle_path1, $this->fieldNameInput, $this->fieldLabel);
// Create an additional node type.
$type_name2 = strtolower($this->randomMachineName(8)) . '_test';
$type2 = $this->drupalCreateContentType(['name' => $type_name2, 'type' => $type_name2]);
$type_name2 = $type2->id();
// Add a field to the second node type.
$bundle_path2 = 'admin/structure/types/manage/' . $type_name2;
$this->fieldUIAddExistingField($bundle_path2, $this->fieldName, $this->fieldLabel);
// Delete the first field.
$this->fieldUIDeleteField($bundle_path1, "node.$this->contentType.$this->fieldName", $this->fieldLabel, $this->contentType);
// Check that the field was deleted.
$this->assertNull(FieldConfig::loadByName('node', $this->contentType, $this->fieldName), 'Field was deleted.');
// Check that the field storage was not deleted
$this->assertNotNull(FieldStorageConfig::loadByName('node', $this->fieldName), 'Field storage was not deleted.');
// Delete the second field.
$this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$this->fieldName", $this->fieldLabel, $type_name2);
// Check that the field was deleted.
$this->assertNull(FieldConfig::loadByName('node', $type_name2, $this->fieldName), 'Field was deleted.');
// Check that the field storage was deleted too.
$this->assertNull(FieldStorageConfig::loadByName('node', $this->fieldName), 'Field storage was deleted.');
}
/**
* Tests that Field UI respects disallowed field names.
*/
public function testDisallowedFieldNames() {
// Reset the field prefix so we can test properly.
$this->config('field_ui.settings')->set('field_prefix', '')->save();
$label = 'Disallowed field';
$edit = [
'label' => $label,
'new_storage_type' => 'test_field',
];
// Try with an entity key.
$edit['field_name'] = 'title';
$bundle_path = 'admin/structure/types/manage/' . $this->contentType;
$this->drupalPostForm("$bundle_path/fields/add-field", $edit, t('Save and continue'));
$this->assertText(t('The machine-readable name is already in use. It must be unique.'));
// Try with a base field.
$edit['field_name'] = 'sticky';
$bundle_path = 'admin/structure/types/manage/' . $this->contentType;
$this->drupalPostForm("$bundle_path/fields/add-field", $edit, t('Save and continue'));
$this->assertText(t('The machine-readable name is already in use. It must be unique.'));
}
/**
* Tests that Field UI respects locked fields.
*/
public function testLockedField() {
// Create a locked field and attach it to a bundle. We need to do this
// programmatically as there's no way to create a locked field through UI.
$field_name = strtolower($this->randomMachineName(8));
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => 'test_field',
'cardinality' => 1,
'locked' => TRUE
]);
$field_storage->save();
FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $this->contentType,
])->save();
entity_get_form_display('node', $this->contentType, 'default')
->setComponent($field_name, [
'type' => 'test_field_widget',
])
->save();
// Check that the links for edit and delete are not present.
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields');
$locked = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]);
$this->assertTrue(in_array('Locked', $locked), 'Field is marked as Locked in the UI');
$edit_link = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]);
$this->assertFalse(in_array('edit', $edit_link), 'Edit option for locked field is not present the UI');
$delete_link = $this->xpath('//tr[@id=:field_name]/td[4]', [':field_name' => $field_name]);
$this->assertFalse(in_array('delete', $delete_link), 'Delete option for locked field is not present the UI');
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_name . '/delete');
$this->assertResponse(403);
}
/**
* Tests that Field UI respects the 'no_ui' flag in the field type definition.
*/
public function testHiddenFields() {
// Check that the field type is not available in the 'add new field' row.
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/add-field');
$this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value="hidden_test_field"]'), "The 'add new field' select respects field types 'no_ui' property.");
$this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value="shape"]'), "The 'add new field' select shows a valid option.");
// Create a field storage and a field programmatically.
$field_name = 'hidden_test_field';
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => $field_name,
])->save();
$field = [
'field_name' => $field_name,
'bundle' => $this->contentType,
'entity_type' => 'node',
'label' => t('Hidden field'),
];
FieldConfig::create($field)->save();
entity_get_form_display('node', $this->contentType, 'default')
->setComponent($field_name)
->save();
$this->assertTrue(FieldConfig::load('node.' . $this->contentType . '.' . $field_name), format_string('A field of the field storage %field was created programmatically.', ['%field' => $field_name]));
// Check that the newly added field appears on the 'Manage Fields'
// screen.
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields');
$this->assertFieldByXPath('//table[@id="field-overview"]//tr[@id="hidden-test-field"]//td[1]', $field['label'], 'Field was created and appears in the overview page.');
// Check that the field does not appear in the 're-use existing field' row
// on other bundles.
$this->drupalGet('admin/structure/types/manage/page/fields/add-field');
$this->assertFalse($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => $field_name]), "The 're-use existing field' select respects field types 'no_ui' property.");
$this->assertTrue($this->xpath('//select[@id="edit-existing-storage-name"]//option[@value=:field_name]', [':field_name' => 'field_tags']), "The 're-use existing field' select shows a valid option.");
// Check that non-configurable fields are not available.
$field_types = \Drupal::service('plugin.manager.field.field_type')->getDefinitions();
foreach ($field_types as $field_type => $definition) {
if (empty($definition['no_ui'])) {
$this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), SafeMarkup::format('Configurable field type @field_type is available.', ['@field_type' => $field_type]));
}
else {
$this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), SafeMarkup::format('Non-configurable field type @field_type is not available.', ['@field_type' => $field_type]));
}
}
}
/**
* Tests that a duplicate field name is caught by validation.
*/
public function testDuplicateFieldName() {
// field_tags already exists, so we're expecting an error when trying to
// create a new field with the same name.
$edit = [
'field_name' => 'tags',
'label' => $this->randomMachineName(),
'new_storage_type' => 'entity_reference',
];
$url = 'admin/structure/types/manage/' . $this->contentType . '/fields/add-field';
$this->drupalPostForm($url, $edit, t('Save and continue'));
$this->assertText(t('The machine-readable name is already in use. It must be unique.'));
$this->assertUrl($url, [], 'Stayed on the same page.');
}
/**
* Tests that external URLs in the 'destinations' query parameter are blocked.
*/
public function testExternalDestinations() {
$options = [
'query' => ['destinations' => ['http://example.com']],
];
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.body/storage', [], 'Save field settings', $options);
// The external redirect should not fire.
$this->assertUrl('admin/structure/types/manage/article/fields/node.article.body/storage', $options);
$this->assertResponse(200);
$this->assertRaw('Attempt to update field <em class="placeholder">Body</em> failed: <em class="placeholder">The internal path component &#039;http://example.com&#039; is external. You are not allowed to specify an external URL together with internal:/.</em>.');
}
/**
* Tests that deletion removes field storages and fields as expected for a term.
*/
public function testDeleteTaxonomyField() {
// Create a new field.
$bundle_path = 'admin/structure/taxonomy/manage/tags/overview';
$this->fieldUIAddNewField($bundle_path, $this->fieldNameInput, $this->fieldLabel);
// Delete the field.
$this->fieldUIDeleteField($bundle_path, "taxonomy_term.tags.$this->fieldName", $this->fieldLabel, 'Tags');
// Check that the field was deleted.
$this->assertNull(FieldConfig::loadByName('taxonomy_term', 'tags', $this->fieldName), 'Field was deleted.');
// Check that the field storage was deleted too.
$this->assertNull(FieldStorageConfig::loadByName('taxonomy_term', $this->fieldName), 'Field storage was deleted.');
}
/**
* Tests that help descriptions render valid HTML.
*/
public function testHelpDescriptions() {
// Create an image field
FieldStorageConfig::create([
'field_name' => 'field_image',
'entity_type' => 'node',
'type' => 'image',
])->save();
FieldConfig::create([
'field_name' => 'field_image',
'entity_type' => 'node',
'label' => 'Image',
'bundle' => 'article',
])->save();
entity_get_form_display('node', 'article', 'default')->setComponent('field_image')->save();
$edit = [
'description' => '<strong>Test with an upload field.',
];
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image', $edit, t('Save settings'));
// Check that hook_field_widget_form_alter() does believe this is the
// default value form.
$this->drupalGet('admin/structure/types/manage/article/fields/node.article.field_tags');
$this->assertText('From hook_field_widget_form_alter(): Default form is true.', 'Default value form in hook_field_widget_form_alter().');
$edit = [
'description' => '<em>Test with a non upload field.',
];
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags', $edit, t('Save settings'));
$this->drupalGet('node/add/article');
$this->assertRaw('<strong>Test with an upload field.</strong>');
$this->assertRaw('<em>Test with a non upload field.</em>');
}
/**
* Tests that the field list administration page operates correctly.
*/
public function fieldListAdminPage() {
$this->drupalGet('admin/reports/fields');
$this->assertText($this->fieldName, 'Field name is displayed in field list.');
$this->assertTrue($this->assertLinkByHref('admin/structure/types/manage/' . $this->contentType . '/fields'), 'Link to content type using field is displayed in field list.');
}
/**
* Tests the "preconfigured field" functionality.
*
* @see \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface
*/
public function testPreconfiguredFields() {
$this->drupalGet('admin/structure/types/manage/article/fields/add-field');
// Check that the preconfigured field option exist alongside the regular
// field type option.
$this->assertOption('edit-new-storage-type', 'field_ui:test_field_with_preconfigured_options:custom_options');
$this->assertOption('edit-new-storage-type', 'test_field_with_preconfigured_options');
// Add a field with every possible preconfigured value.
$this->fieldUIAddNewField(NULL, 'test_custom_options', 'Test label', 'field_ui:test_field_with_preconfigured_options:custom_options');
$field_storage = FieldStorageConfig::loadByName('node', 'field_test_custom_options');
$this->assertEqual($field_storage->getCardinality(), FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$this->assertEqual($field_storage->getSetting('test_field_storage_setting'), 'preconfigured_storage_setting');
$field = FieldConfig::loadByName('node', 'article', 'field_test_custom_options');
$this->assertTrue($field->isRequired());
$this->assertEqual($field->getSetting('test_field_setting'), 'preconfigured_field_setting');
$form_display = entity_get_form_display('node', 'article', 'default');
$this->assertEqual($form_display->getComponent('field_test_custom_options')['type'], 'test_field_widget_multiple');
$view_display = entity_get_display('node', 'article', 'default');
$this->assertEqual($view_display->getComponent('field_test_custom_options')['type'], 'field_test_multiple');
}
/**
* Tests the access to non-existent field URLs.
*/
public function testNonExistentFieldUrls() {
$field_id = 'node.foo.bar';
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id);
$this->assertResponse(404);
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/' . $field_id . '/storage');
$this->assertResponse(404);
}
}