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

@ -17,7 +17,7 @@
}
.field-ui-overview .field-plugin-summary {
float: left; /* LTR */
font-size: .9em;
font-size: 0.9em;
}
[dir="rtl"] .field-ui-overview .field-plugin-summary {
float: right;
@ -25,7 +25,7 @@
.field-ui-overview .field-plugin-summary-cell .warning {
display: block;
float: left; /* LTR */
margin-right: .5em;
margin-right: 0.5em;
}
[dir="rtl"] .field-ui-overview .field-plugin-summary-cell .warning {
float: right;

View file

@ -0,0 +1,366 @@
/**
* @file
* Attaches the behaviors for the Field UI module.
*/
(function($, Drupal, drupalSettings) {
/**
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Adds behaviors to the field storage add form.
*/
Drupal.behaviors.fieldUIFieldStorageAddForm = {
attach(context) {
const $form = $(context)
.find('[data-drupal-selector="field-ui-field-storage-add-form"]')
.once('field_ui_add');
if ($form.length) {
// Add a few 'js-form-required' and 'form-required' css classes here.
// We can not use the Form API '#required' property because both label
// elements for "add new" and "re-use existing" can never be filled and
// submitted at the same time. The actual validation will happen
// server-side.
$form
.find(
'.js-form-item-label label,' +
'.js-form-item-field-name label,' +
'.js-form-item-existing-storage-label label',
)
.addClass('js-form-required form-required');
const $newFieldType = $form.find('select[name="new_storage_type"]');
const $existingStorageName = $form.find(
'select[name="existing_storage_name"]',
);
const $existingStorageLabel = $form.find(
'input[name="existing_storage_label"]',
);
// When the user selects a new field type, clear the "existing field"
// selection.
$newFieldType.on('change', function() {
if ($(this).val() !== '') {
// Reset the "existing storage name" selection.
$existingStorageName.val('').trigger('change');
}
});
// When the user selects an existing storage name, clear the "new field
// type" selection and populate the 'existing_storage_label' element.
$existingStorageName.on('change', function() {
const value = $(this).val();
if (value !== '') {
// Reset the "new field type" selection.
$newFieldType.val('').trigger('change');
// Pre-populate the "existing storage label" element.
if (
typeof drupalSettings.existingFieldLabels[value] !== 'undefined'
) {
$existingStorageLabel.val(
drupalSettings.existingFieldLabels[value],
);
}
}
});
}
},
};
/**
* Attaches the fieldUIOverview behavior.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the fieldUIOverview behavior.
*
* @see Drupal.fieldUIOverview.attach
*/
Drupal.behaviors.fieldUIDisplayOverview = {
attach(context, settings) {
$(context)
.find('table#field-display-overview')
.once('field-display-overview')
.each(function() {
Drupal.fieldUIOverview.attach(
this,
settings.fieldUIRowsData,
Drupal.fieldUIDisplayOverview,
);
});
},
};
/**
* Namespace for the field UI overview.
*
* @namespace
*/
Drupal.fieldUIOverview = {
/**
* Attaches the fieldUIOverview behavior.
*
* @param {HTMLTableElement} table
* The table element for the overview.
* @param {object} rowsData
* The data of the rows in the table.
* @param {object} rowHandlers
* Handlers to be added to the rows.
*/
attach(table, rowsData, rowHandlers) {
const tableDrag = Drupal.tableDrag[table.id];
// Add custom tabledrag callbacks.
tableDrag.onDrop = this.onDrop;
tableDrag.row.prototype.onSwap = this.onSwap;
// Create row handlers.
$(table)
.find('tr.draggable')
.each(function() {
// Extract server-side data for the row.
const row = this;
if (row.id in rowsData) {
const data = rowsData[row.id];
data.tableDrag = tableDrag;
// Create the row handler, make it accessible from the DOM row
// element.
const rowHandler = new rowHandlers[data.rowHandler](row, data);
$(row).data('fieldUIRowHandler', rowHandler);
}
});
},
/**
* Event handler to be attached to form inputs triggering a region change.
*/
onChange() {
const $trigger = $(this);
const $row = $trigger.closest('tr');
const rowHandler = $row.data('fieldUIRowHandler');
const refreshRows = {};
refreshRows[rowHandler.name] = $trigger.get(0);
// Handle region change.
const region = rowHandler.getRegion();
if (region !== rowHandler.region) {
// Remove parenting.
$row.find('select.js-field-parent').val('');
// Let the row handler deal with the region change.
$.extend(refreshRows, rowHandler.regionChange(region));
// Update the row region.
rowHandler.region = region;
}
// Ajax-update the rows.
Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
},
/**
* Lets row handlers react when a row is dropped into a new region.
*/
onDrop() {
const dragObject = this;
const row = dragObject.rowObject.element;
const $row = $(row);
const rowHandler = $row.data('fieldUIRowHandler');
if (typeof rowHandler !== 'undefined') {
const regionRow = $row.prevAll('tr.region-message').get(0);
const region = regionRow.className.replace(
/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/,
'$2',
);
if (region !== rowHandler.region) {
// Let the row handler deal with the region change.
const refreshRows = rowHandler.regionChange(region);
// Update the row region.
rowHandler.region = region;
// Ajax-update the rows.
Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
}
}
},
/**
* Refreshes placeholder rows in empty regions while a row is being dragged.
*
* Copied from block.js.
*
* @param {HTMLElement} draggedRow
* The tableDrag rowObject for the row being dragged.
*/
onSwap(draggedRow) {
const rowObject = this;
$(rowObject.table)
.find('tr.region-message')
.each(function() {
const $this = $(this);
// If the dragged row is in this region, but above the message row, swap
// it down one space.
if (
$this.prev('tr').get(0) ===
rowObject.group[rowObject.group.length - 1]
) {
// Prevent a recursion problem when using the keyboard to move rows
// up.
if (
rowObject.method !== 'keyboard' ||
rowObject.direction === 'down'
) {
rowObject.swap('after', this);
}
}
// This region has become empty.
if (
$this.next('tr').is(':not(.draggable)') ||
$this.next('tr').length === 0
) {
$this.removeClass('region-populated').addClass('region-empty');
}
// This region has become populated.
else if ($this.is('.region-empty')) {
$this.removeClass('region-empty').addClass('region-populated');
}
});
},
/**
* Triggers Ajax refresh of selected rows.
*
* The 'format type' selects can trigger a series of changes in child rows.
* The #ajax behavior is therefore not attached directly to the selects, but
* triggered manually through a hidden #ajax 'Refresh' button.
*
* @param {object} rows
* A hash object, whose keys are the names of the rows to refresh (they
* will receive the 'ajax-new-content' effect on the server side), and
* whose values are the DOM element in the row that should get an Ajax
* throbber.
*/
AJAXRefreshRows(rows) {
// Separate keys and values.
const rowNames = [];
const ajaxElements = [];
Object.keys(rows || {}).forEach(rowName => {
rowNames.push(rowName);
ajaxElements.push(rows[rowName]);
});
if (rowNames.length) {
// Add a throbber next each of the ajaxElements.
$(ajaxElements).after(Drupal.theme.ajaxProgressThrobber());
// Fire the Ajax update.
$('input[name=refresh_rows]').val(rowNames.join(' '));
$('input[data-drupal-selector="edit-refresh"]').trigger('mousedown');
// Disabled elements do not appear in POST ajax data, so we mark the
// elements disabled only after firing the request.
$(ajaxElements).prop('disabled', true);
}
},
};
/**
* Row handlers for the 'Manage display' screen.
*
* @namespace
*/
Drupal.fieldUIDisplayOverview = {};
/**
* Constructor for a 'field' row handler.
*
* This handler is used for both fields and 'extra fields' rows.
*
* @constructor
*
* @param {HTMLTableRowElement} row
* The row DOM element.
* @param {object} data
* Additional data to be populated in the constructed object.
*
* @return {Drupal.fieldUIDisplayOverview.field}
* The field row handler constructed.
*/
Drupal.fieldUIDisplayOverview.field = function(row, data) {
this.row = row;
this.name = data.name;
this.region = data.region;
this.tableDrag = data.tableDrag;
this.defaultPlugin = data.defaultPlugin;
// Attach change listener to the 'plugin type' select.
this.$pluginSelect = $(row).find('.field-plugin-type');
this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange);
// Attach change listener to the 'region' select.
this.$regionSelect = $(row).find('select.field-region');
this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange);
return this;
};
Drupal.fieldUIDisplayOverview.field.prototype = {
/**
* Returns the region corresponding to the current form values of the row.
*
* @return {string}
* Either 'hidden' or 'content'.
*/
getRegion() {
return this.$regionSelect.val();
},
/**
* Reacts to a row being changed regions.
*
* This function is called when the row is moved to a different region, as
* a
* result of either :
* - a drag-and-drop action (the row's form elements then probably need to
* be updated accordingly)
* - user input in one of the form elements watched by the
* {@link Drupal.fieldUIOverview.onChange} change listener.
*
* @param {string} region
* The name of the new region for the row.
*
* @return {object}
* A hash object indicating which rows should be Ajax-updated as a result
* of the change, in the format expected by
* {@link Drupal.fieldUIOverview.AJAXRefreshRows}.
*/
regionChange(region) {
// Replace dashes with underscores.
region = region.replace(/-/g, '_');
// Set the region of the select list.
this.$regionSelect.val(region);
// Restore the formatter back to the default formatter only if it was
// disabled previously. Pseudo-fields do not have default formatters,
// we just return to 'visible' for those.
if (this.region === 'hidden') {
const value =
typeof this.defaultPlugin !== 'undefined'
? this.defaultPlugin
: this.$pluginSelect.find('option').val();
if (typeof value !== 'undefined') {
this.$pluginSelect.val(value);
}
}
const refreshRows = {};
refreshRows[this.name] = this.$pluginSelect.get(0);
return refreshRows;
},
};
})(jQuery, Drupal, drupalSettings);

View file

@ -5,4 +5,4 @@ package: Core
version: VERSION
core: 8.x
dependencies:
- field
- drupal:field

View file

@ -1,55 +1,32 @@
/**
* @file
* Attaches the behaviors for the Field UI module.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Adds behaviors to the field storage add form.
*/
Drupal.behaviors.fieldUIFieldStorageAddForm = {
attach: function (context) {
attach: function attach(context) {
var $form = $(context).find('[data-drupal-selector="field-ui-field-storage-add-form"]').once('field_ui_add');
if ($form.length) {
// Add a few 'js-form-required' and 'form-required' css classes here.
// We can not use the Form API '#required' property because both label
// elements for "add new" and "re-use existing" can never be filled and
// submitted at the same time. The actual validation will happen
// server-side.
$form.find(
'.js-form-item-label label,' +
'.js-form-item-field-name label,' +
'.js-form-item-existing-storage-label label')
.addClass('js-form-required form-required');
$form.find('.js-form-item-label label,' + '.js-form-item-field-name label,' + '.js-form-item-existing-storage-label label').addClass('js-form-required form-required');
var $newFieldType = $form.find('select[name="new_storage_type"]');
var $existingStorageName = $form.find('select[name="existing_storage_name"]');
var $existingStorageLabel = $form.find('input[name="existing_storage_label"]');
// When the user selects a new field type, clear the "existing field"
// selection.
$newFieldType.on('change', function () {
if ($(this).val() !== '') {
// Reset the "existing storage name" selection.
$existingStorageName.val('').trigger('change');
}
});
// When the user selects an existing storage name, clear the "new field
// type" selection and populate the 'existing_storage_label' element.
$existingStorageName.on('change', function () {
var value = $(this).val();
if (value !== '') {
// Reset the "new field type" selection.
$newFieldType.val('').trigger('change');
// Pre-populate the "existing storage label" element.
if (typeof drupalSettings.existingFieldLabels[value] !== 'undefined') {
$existingStorageLabel.val(drupalSettings.existingFieldLabels[value]);
}
@ -59,68 +36,33 @@
}
};
/**
* Attaches the fieldUIOverview behavior.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the fieldUIOverview behavior.
*
* @see Drupal.fieldUIOverview.attach
*/
Drupal.behaviors.fieldUIDisplayOverview = {
attach: function (context, settings) {
attach: function attach(context, settings) {
$(context).find('table#field-display-overview').once('field-display-overview').each(function () {
Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIDisplayOverview);
});
}
};
/**
* Namespace for the field UI overview.
*
* @namespace
*/
Drupal.fieldUIOverview = {
/**
* Attaches the fieldUIOverview behavior.
*
* @param {HTMLTableElement} table
* The table element for the overview.
* @param {object} rowsData
* The data of the rows in the table.
* @param {object} rowHandlers
* Handlers to be added to the rows.
*/
attach: function (table, rowsData, rowHandlers) {
attach: function attach(table, rowsData, rowHandlers) {
var tableDrag = Drupal.tableDrag[table.id];
// Add custom tabledrag callbacks.
tableDrag.onDrop = this.onDrop;
tableDrag.row.prototype.onSwap = this.onSwap;
// Create row handlers.
$(table).find('tr.draggable').each(function () {
// Extract server-side data for the row.
var row = this;
if (row.id in rowsData) {
var data = rowsData[row.id];
data.tableDrag = tableDrag;
// Create the row handler, make it accessible from the DOM row
// element.
var rowHandler = new rowHandlers[data.rowHandler](row, data);
$(row).data('fieldUIRowHandler', rowHandler);
}
});
},
/**
* Event handler to be attached to form inputs triggering a region change.
*/
onChange: function () {
onChange: function onChange() {
var $trigger = $(this);
var $row = $trigger.closest('tr');
var rowHandler = $row.data('fieldUIRowHandler');
@ -128,25 +70,18 @@
var refreshRows = {};
refreshRows[rowHandler.name] = $trigger.get(0);
// Handle region change.
var region = rowHandler.getRegion();
if (region !== rowHandler.region) {
// Remove parenting.
$row.find('select.js-field-parent').val('');
// Let the row handler deal with the region change.
$.extend(refreshRows, rowHandler.regionChange(region));
// Update the row region.
rowHandler.region = region;
}
// Ajax-update the rows.
Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
},
/**
* Lets row handlers react when a row is dropped into a new region.
*/
onDrop: function () {
onDrop: function onDrop() {
var dragObject = this;
var row = dragObject.rowObject.element;
var $row = $(row);
@ -156,110 +91,53 @@
var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
if (region !== rowHandler.region) {
// Let the row handler deal with the region change.
var refreshRows = rowHandler.regionChange(region);
// Update the row region.
rowHandler.region = region;
// Ajax-update the rows.
Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
}
}
},
/**
* Refreshes placeholder rows in empty regions while a row is being dragged.
*
* Copied from block.js.
*
* @param {HTMLElement} draggedRow
* The tableDrag rowObject for the row being dragged.
*/
onSwap: function (draggedRow) {
onSwap: function onSwap(draggedRow) {
var rowObject = this;
$(rowObject.table).find('tr.region-message').each(function () {
var $this = $(this);
// If the dragged row is in this region, but above the message row, swap
// it down one space.
if ($this.prev('tr').get(0) === rowObject.group[rowObject.group.length - 1]) {
// Prevent a recursion problem when using the keyboard to move rows
// up.
if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
if (rowObject.method !== 'keyboard' || rowObject.direction === 'down') {
rowObject.swap('after', this);
}
}
// This region has become empty.
if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
$this.removeClass('region-populated').addClass('region-empty');
}
// This region has become populated.
else if ($this.is('.region-empty')) {
$this.removeClass('region-empty').addClass('region-populated');
}
} else if ($this.is('.region-empty')) {
$this.removeClass('region-empty').addClass('region-populated');
}
});
},
/**
* Triggers Ajax refresh of selected rows.
*
* The 'format type' selects can trigger a series of changes in child rows.
* The #ajax behavior is therefore not attached directly to the selects, but
* triggered manually through a hidden #ajax 'Refresh' button.
*
* @param {object} rows
* A hash object, whose keys are the names of the rows to refresh (they
* will receive the 'ajax-new-content' effect on the server side), and
* whose values are the DOM element in the row that should get an Ajax
* throbber.
*/
AJAXRefreshRows: function (rows) {
// Separate keys and values.
AJAXRefreshRows: function AJAXRefreshRows(rows) {
var rowNames = [];
var ajaxElements = [];
var rowName;
for (rowName in rows) {
if (rows.hasOwnProperty(rowName)) {
rowNames.push(rowName);
ajaxElements.push(rows[rowName]);
}
}
Object.keys(rows || {}).forEach(function (rowName) {
rowNames.push(rowName);
ajaxElements.push(rows[rowName]);
});
if (rowNames.length) {
// Add a throbber next each of the ajaxElements.
$(ajaxElements).after('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
$(ajaxElements).after(Drupal.theme.ajaxProgressThrobber());
// Fire the Ajax update.
$('input[name=refresh_rows]').val(rowNames.join(' '));
$('input[data-drupal-selector="edit-refresh"]').trigger('mousedown');
// Disabled elements do not appear in POST ajax data, so we mark the
// elements disabled only after firing the request.
$(ajaxElements).prop('disabled', true);
}
}
};
/**
* Row handlers for the 'Manage display' screen.
*
* @namespace
*/
Drupal.fieldUIDisplayOverview = {};
/**
* Constructor for a 'field' row handler.
*
* This handler is used for both fields and 'extra fields' rows.
*
* @constructor
*
* @param {HTMLTableRowElement} row
* The row DOM element.
* @param {object} data
* Additional data to be populated in the constructed object.
*
* @return {Drupal.fieldUIDisplayOverview.field}
* The field row handler constructed.
*/
Drupal.fieldUIDisplayOverview.field = function (row, data) {
this.row = row;
this.name = data.name;
@ -267,11 +145,9 @@
this.tableDrag = data.tableDrag;
this.defaultPlugin = data.defaultPlugin;
// Attach change listener to the 'plugin type' select.
this.$pluginSelect = $(row).find('.field-plugin-type');
this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange);
// Attach change listener to the 'region' select.
this.$regionSelect = $(row).find('select.field-region');
this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange);
@ -279,50 +155,20 @@
};
Drupal.fieldUIDisplayOverview.field.prototype = {
/**
* Returns the region corresponding to the current form values of the row.
*
* @return {string}
* Either 'hidden' or 'content'.
*/
getRegion: function () {
getRegion: function getRegion() {
return this.$regionSelect.val();
},
/**
* Reacts to a row being changed regions.
*
* This function is called when the row is moved to a different region, as
* a
* result of either :
* - a drag-and-drop action (the row's form elements then probably need to
* be updated accordingly)
* - user input in one of the form elements watched by the
* {@link Drupal.fieldUIOverview.onChange} change listener.
*
* @param {string} region
* The name of the new region for the row.
*
* @return {object}
* A hash object indicating which rows should be Ajax-updated as a result
* of the change, in the format expected by
* {@link Drupal.fieldUIOverview.AJAXRefreshRows}.
*/
regionChange: function (region) {
// Replace dashes with underscores.
regionChange: function regionChange(region) {
region = region.replace(/-/g, '_');
// Set the region of the select list.
this.$regionSelect.val(region);
// Restore the formatter back to the default formatter. Pseudo-fields
// do not have default formatters, we just return to 'visible' for
// those.
var value = (typeof this.defaultPlugin !== 'undefined') ? this.defaultPlugin : this.$pluginSelect.find('option').val();
if (this.region === 'hidden') {
var value = typeof this.defaultPlugin !== 'undefined' ? this.defaultPlugin : this.$pluginSelect.find('option').val();
if (typeof value !== 'undefined') {
this.$pluginSelect.val(value);
if (typeof value !== 'undefined') {
this.$pluginSelect.val(value);
}
}
var refreshRows = {};
@ -331,5 +177,4 @@
return refreshRows;
}
};
})(jQuery, Drupal, drupalSettings);
})(jQuery, Drupal, drupalSettings);

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

@ -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

@ -2,6 +2,9 @@
namespace Drupal\Tests\field_ui\Functional;
use Drupal\Core\Entity\Entity\EntityFormMode;
use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
@ -16,7 +19,7 @@ class EntityDisplayModeTest extends BrowserTestBase {
*
* @var string[]
*/
public static $modules = ['block', 'entity_test', 'field_ui'];
public static $modules = ['block', 'entity_test', 'field_ui', 'node'];
/**
* {@inheritdoc}
@ -24,6 +27,12 @@ class EntityDisplayModeTest extends BrowserTestBase {
protected function setUp() {
parent::setUp();
// Create a node type.
$this->drupalCreateContentType([
'type' => 'article',
'name' => 'Article',
]);
$this->drupalPlaceBlock('local_actions_block');
$this->drupalPlaceBlock('page_title_block');
}
@ -68,6 +77,14 @@ class EntityDisplayModeTest extends BrowserTestBase {
// Test editing the view mode.
$this->drupalGet('admin/structure/display-modes/view/manage/entity_test.' . $edit['id']);
// Test that the link templates added by field_ui_entity_type_build() are
// generating valid routes.
$view_mode = EntityViewMode::load('entity_test.' . $edit['id']);
$this->assertEquals(Url::fromRoute('entity.entity_view_mode.collection')->toString(), $view_mode->toUrl('collection')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_view_mode.add_form', ['entity_type_id' => $view_mode->getTargetType()])->toString(), $view_mode->toUrl('add-form')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_view_mode.edit_form', ['entity_view_mode' => $view_mode->id()])->toString(), $view_mode->toUrl('edit-form')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_view_mode.delete_form', ['entity_view_mode' => $view_mode->id()])->toString(), $view_mode->toUrl('delete-form')->toString());
// Test deleting the view mode.
$this->clickLink(t('Delete'));
$this->assertRaw(t('Are you sure you want to delete the view mode %label?', ['%label' => $edit['label']]));
@ -114,6 +131,14 @@ class EntityDisplayModeTest extends BrowserTestBase {
// Test editing the form mode.
$this->drupalGet('admin/structure/display-modes/form/manage/entity_test.' . $edit['id']);
// Test that the link templates added by field_ui_entity_type_build() are
// generating valid routes.
$form_mode = EntityFormMode::load('entity_test.' . $edit['id']);
$this->assertEquals(Url::fromRoute('entity.entity_form_mode.collection')->toString(), $form_mode->toUrl('collection')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_form_mode.add_form', ['entity_type_id' => $form_mode->getTargetType()])->toString(), $form_mode->toUrl('add-form')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_form_mode.edit_form', ['entity_form_mode' => $form_mode->id()])->toString(), $form_mode->toUrl('edit-form')->toString());
$this->assertEquals(Url::fromRoute('entity.entity_form_mode.delete_form', ['entity_form_mode' => $form_mode->id()])->toString(), $form_mode->toUrl('delete-form')->toString());
// Test deleting the form mode.
$this->clickLink(t('Delete'));
$this->assertRaw(t('Are you sure you want to delete the form mode %label?', ['%label' => $edit['label']]));
@ -121,4 +146,62 @@ class EntityDisplayModeTest extends BrowserTestBase {
$this->assertRaw(t('The form mode %label has been deleted.', ['%label' => $edit['label']]));
}
/**
* Tests if view modes appear in alphabetical order by visible name.
*
* The machine name should not be used for sorting.
*
* @see https://www.drupal.org/node/2858569
*/
public function testAlphabeticalDisplaySettings() {
$this->drupalLogin($this->drupalCreateUser([
'access administration pages',
'administer content types',
'administer display modes',
'administer nodes',
'administer node fields',
'administer node display',
'administer node form display',
'view the administration theme',
]));
$this->drupalGet('admin/structure/types/manage/article/display');
// Verify that the order of view modes is alphabetical by visible label.
// Since the default view modes all have machine names which coincide with
// the English labels, they should appear in alphabetical order, by default
// if viewing the site in English and if no changes have been made. We will
// verify this first.
$page_text = $this->getTextContent();
$start = strpos($page_text, 'view modes');
$pos = $start;
$list = ['Full content', 'RSS', 'Search index', 'Search result', 'Teaser'];
foreach ($list as $name) {
$new_pos = strpos($page_text, $name, $start);
$this->assertTrue($new_pos > $pos, 'Order of ' . $name . ' is correct on page');
$pos = $new_pos;
}
// Now that we have verified the original display order, we can change the
// label for one of the view modes. If we rename "Teaser" to "Breezer", it
// should appear as the first of the listed view modes:
// Set new values and enable test plugins.
$edit = [
'label' => 'Breezer',
];
$this->drupalPostForm('admin/structure/display-modes/view/manage/node.teaser', $edit, 'Save');
$this->assertSession()->pageTextContains('Saved the Breezer view mode.');
// Re-open the display settings for the article content type and verify
// that changing "Teaser" to "Breezer" makes it appear before "Full
// content".
$this->drupalGet('admin/structure/types/manage/article/display');
$page_text = $this->getTextContent();
$start = strpos($page_text, 'view modes');
$pos = $start;
$list = ['Breezer', 'Full content'];
foreach ($list as $name) {
$new_pos = strpos($page_text, $name, $start);
$this->assertTrue($new_pos > $pos, 'Order of ' . $name . ' is correct on page');
$pos = $new_pos;
}
}
}

View file

@ -1,10 +1,12 @@
<?php
namespace Drupal\field_ui\Tests;
namespace Drupal\Tests\field_ui\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\simpletest\WebTestBase;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
use Drupal\views\Entity\View;
use Drupal\views\Tests\ViewTestData;
/**
@ -12,7 +14,7 @@ use Drupal\views\Tests\ViewTestData;
*
* @group field_ui
*/
class FieldUIDeleteTest extends WebTestBase {
class FieldUIDeleteTest extends BrowserTestBase {
use FieldUiTestTrait;
@ -74,6 +76,13 @@ class FieldUIDeleteTest extends WebTestBase {
\Drupal::service('module_installer')->install(['views']);
ViewTestData::createTestViews(get_class($this), ['field_test_views']);
$view = View::load('test_view_field_delete');
$this->assertNotNull($view);
$this->assertTrue($view->status());
// Test that the View depends on the field.
$dependencies = $view->getDependencies() + ['config' => []];
$this->assertTrue(in_array("field.storage.node.$field_name", $dependencies['config']));
// 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");
@ -91,13 +100,13 @@ class FieldUIDeleteTest extends WebTestBase {
// 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('The listed configuration will be updated.'));
$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.');
// Test that nothing is scheduled for deletion.
$this->assertFalse(isset($xml[0]), 'The field currently being deleted is not shown in the entity deletions.');
// Delete the second field.
$this->fieldUIDeleteField($bundle_path2, "node.$type_name2.$field_name", $field_label, $type_name2);
@ -106,6 +115,14 @@ class FieldUIDeleteTest extends WebTestBase {
$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.');
// Test that the View isn't deleted and has been disabled.
$view = View::load('test_view_field_delete');
$this->assertNotNull($view);
$this->assertFalse($view->status());
// Test that the View no longer depends on the deleted field.
$dependencies = $view->getDependencies() + ['config' => []];
$this->assertFalse(in_array("field.storage.node.$field_name", $dependencies['config']));
}
}

View file

@ -1,22 +1,23 @@
<?php
namespace Drupal\field_ui\Tests;
namespace Drupal\Tests\field_ui\Functional;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
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;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
/**
* Tests the Field UI "Manage fields" screen.
*
* @group field_ui
*/
class ManageFieldsTest extends WebTestBase {
class ManageFieldsFunctionalTest extends BrowserTestBase {
use FieldUiTestTrait;
use EntityReferenceTestTrait;
@ -153,17 +154,17 @@ class ManageFieldsTest extends WebTestBase {
$url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body";
foreach ($operation_links as $link) {
switch ($link['title']) {
switch ($link->getAttribute('title')) {
case 'Edit field settings.':
$this->assertIdentical($url, (string) $link['href']);
$this->assertIdentical($url, $link->getAttribute('href'));
$number_of_links_found++;
break;
case 'Edit storage settings.':
$this->assertIdentical("$url/storage", (string) $link['href']);
$this->assertIdentical("$url/storage", $link->getAttribute('href'));
$number_of_links_found++;
break;
case 'Delete field.':
$this->assertIdentical("$url/delete", (string) $link['href']);
$this->assertIdentical("$url/delete", $link->getAttribute('href'));
$number_of_links_found++;
break;
}
@ -409,7 +410,7 @@ class ManageFieldsTest extends WebTestBase {
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => 'test_field'
'type' => 'test_field',
])->save();
$field = FieldConfig::create([
'field_name' => $field_name,
@ -449,7 +450,7 @@ class ManageFieldsTest extends WebTestBase {
$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.');
$this->assertEqual($field->getDefaultValueLiteral(), [], '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.
@ -467,7 +468,7 @@ class ManageFieldsTest extends WebTestBase {
$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.');
$this->assertEqual($field->getDefaultValueLiteral(), [], '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')
@ -548,7 +549,7 @@ class ManageFieldsTest extends WebTestBase {
'entity_type' => 'node',
'type' => 'test_field',
'cardinality' => 1,
'locked' => TRUE
'locked' => TRUE,
]);
$field_storage->save();
FieldConfig::create([
@ -564,11 +565,7 @@ class ManageFieldsTest extends WebTestBase {
// 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->assertSame('Locked', $locked[0]->getHtml(), 'Field is marked as Locked in the UI');
$this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/node.' . $this->contentType . '.' . $field_name . '/delete');
$this->assertResponse(403);
}
@ -616,10 +613,10 @@ class ManageFieldsTest extends WebTestBase {
$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]));
$this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), new FormattableMarkup('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]));
$this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', [':field_type' => $field_type]), new FormattableMarkup('Non-configurable field type @field_type is not available.', ['@field_type' => $field_type]));
}
}
}
@ -720,7 +717,7 @@ class ManageFieldsTest extends WebTestBase {
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.');
$this->assertLinkByHref('admin/structure/types/manage/' . $this->contentType . '/fields');
}
/**
@ -750,6 +747,7 @@ class ManageFieldsTest extends WebTestBase {
$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');
$this->assertEqual($view_display->getComponent('field_test_custom_options')['settings']['test_formatter_setting_multiple'], 'altered dummy test string');
}
/**

View file

@ -3,14 +3,14 @@
namespace Drupal\Tests\field_ui\FunctionalJavascript;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Tests the UI for entity displays.
*
* @group field_ui
*/
class EntityDisplayTest extends JavascriptTestBase {
class EntityDisplayTest extends WebDriverTestBase {
/**
* {@inheritdoc}
@ -25,9 +25,9 @@ class EntityDisplayTest extends JavascriptTestBase {
$entity = EntityTest::create([
'name' => 'The name for this entity',
'field_test_text' => [[
'value' => 'The field test text value',
]],
'field_test_text' => [
['value' => 'The field test text value'],
],
]);
$entity->save();
$this->drupalLogin($this->drupalCreateUser([
@ -50,7 +50,8 @@ class EntityDisplayTest extends JavascriptTestBase {
$this->drupalGet('entity_test/structure/entity_test/form-display');
$this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'content')->isSelected());
$this->getSession()->getPage()->pressButton('Show row weights');
$this->assertSession()->waitForElementVisible('css', '[name="fields[field_test_text][region]"]');
$this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'hidden');
$this->assertSession()->assertWaitOnAjaxRequest();
$this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'hidden')->isSelected());
@ -72,6 +73,8 @@ class EntityDisplayTest extends JavascriptTestBase {
$this->drupalGet('entity_test/structure/entity_test/display');
$this->assertSession()->elementExists('css', '.region-content-message.region-empty');
$this->getSession()->getPage()->pressButton('Show row weights');
$this->assertSession()->waitForElementVisible('css', '[name="fields[field_test_text][region]"]');
$this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'hidden')->isSelected());
$this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'content');
@ -92,12 +95,16 @@ class EntityDisplayTest extends JavascriptTestBase {
public function testExtraFields() {
entity_test_create_bundle('bundle_with_extra_fields');
$this->drupalGet('entity_test/structure/bundle_with_extra_fields/display');
$this->assertSession()->waitForElement('css', '.tabledrag-handle');
$id = $this->getSession()->getPage()->find('css', '[name="form_build_id"]')->getValue();
$extra_field_row = $this->getSession()->getPage()->find('css', '#display-extra-field');
$disabled_region_row = $this->getSession()->getPage()->find('css', '.region-hidden-title');
$extra_field_row->find('css', '.handle')->dragTo($disabled_region_row);
$this->assertSession()->assertWaitOnAjaxRequest();
$this->assertSession()
->waitForElement('css', "[name='form_build_id']:not([value='$id'])");
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains('Your settings have been saved.');

View file

@ -3,7 +3,6 @@
namespace Drupal\Tests\field_ui\Kernel;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
@ -87,7 +86,7 @@ class EntityDisplayTest extends KernelTestBase {
'link_to_entity' => FALSE,
],
'third_party_settings' => [],
'region' => 'content'
'region' => 'content',
];
$this->assertEqual($display->getComponents(), $expected);
@ -129,7 +128,7 @@ class EntityDisplayTest extends KernelTestBase {
$display->save();
$components = array_keys($display->getComponents());
// The name field is not configurable so will be added automatically.
$expected = [ 0 => 'component_1', 1 => 'component_2', 2 => 'component_3', 'name'];
$expected = [0 => 'component_1', 1 => 'component_2', 2 => 'component_3', 'name'];
$this->assertIdentical($components, $expected);
}
@ -150,7 +149,7 @@ class EntityDisplayTest extends KernelTestBase {
$display = entity_get_display('entity_test', 'entity_test', 'default');
$this->assertFalse($display->isNew());
$this->assertEqual($display->id(), 'entity_test.entity_test.default');
$this->assertEqual($display->getComponent('component_1'), [ 'weight' => 10, 'settings' => [], 'third_party_settings' => [], 'region' => 'content']);
$this->assertEqual($display->getComponent('component_1'), ['weight' => 10, 'settings' => [], 'third_party_settings' => [], 'region' => 'content']);
}
/**
@ -166,7 +165,15 @@ class EntityDisplayTest extends KernelTestBase {
// Check that the default visibility taken into account for extra fields
// unknown in the display.
$this->assertEqual($display->getComponent('display_extra_field'), ['weight' => 5, 'region' => 'content']);
$this->assertEqual(
$display->getComponent('display_extra_field'),
[
'weight' => 5,
'region' => 'content',
'settings' => [],
'third_party_settings' => [],
]
);
$this->assertNull($display->getComponent('display_extra_field_hidden'));
// Check that setting explicit options overrides the defaults.
@ -214,7 +221,7 @@ class EntityDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field'
'type' => 'test_field',
]);
$field_storage->save();
$field = FieldConfig::create([
@ -360,7 +367,7 @@ class EntityDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field'
'type' => 'test_field',
]);
$field_storage->save();
$field = FieldConfig::create([
@ -409,7 +416,7 @@ class EntityDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'text'
'type' => 'text',
]);
$field_storage->save();
$field = FieldConfig::create([
@ -515,14 +522,14 @@ class EntityDisplayTest extends KernelTestBase {
// Create two arbitrary user roles.
for ($i = 0; $i < 2; $i++) {
$roles[$i] = Role::create([
'id' => Unicode::strtolower($this->randomMachineName()),
'id' => mb_strtolower($this->randomMachineName()),
'label' => $this->randomString(),
]);
$roles[$i]->save();
}
// Create a field of type 'test_field' attached to 'entity_test'.
$field_name = Unicode::strtolower($this->randomMachineName());
$field_name = mb_strtolower($this->randomMachineName());
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',

View file

@ -55,7 +55,7 @@ class EntityFormDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field'
'type' => 'test_field',
]);
$field_storage->save();
$field = FieldConfig::create([
@ -185,7 +185,7 @@ class EntityFormDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field'
'type' => 'test_field',
]);
$field_storage->save();
$field = FieldConfig::create([
@ -234,7 +234,7 @@ class EntityFormDisplayTest extends KernelTestBase {
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'text'
'type' => 'text',
]);
$field_storage->save();
$field = FieldConfig::create([

View file

@ -0,0 +1,125 @@
<?php
namespace Drupal\Tests\field_ui\Traits;
/**
* Provides common functionality for the Field UI test classes.
*/
trait FieldUiTestTrait {
/**
* Creates a new field through the Field UI.
*
* @param string $bundle_path
* Admin path of the bundle that the new field is to be attached to.
* @param string $field_name
* The field name of the new field storage.
* @param string $label
* (optional) The label of the new field. Defaults to a random string.
* @param string $field_type
* (optional) The field type of the new field storage. Defaults to
* 'test_field'.
* @param array $storage_edit
* (optional) $edit parameter for drupalPostForm() on the second step
* ('Storage settings' form).
* @param array $field_edit
* (optional) $edit parameter for drupalPostForm() on the third step ('Field
* settings' form).
*/
public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $field_type = 'test_field', array $storage_edit = [], array $field_edit = []) {
$label = $label ?: $this->randomString();
$initial_edit = [
'new_storage_type' => $field_type,
'label' => $label,
'field_name' => $field_name,
];
// Allow the caller to set a NULL path in case they navigated to the right
// page before calling this method.
if ($bundle_path !== NULL) {
$bundle_path = "$bundle_path/fields/add-field";
}
// First step: 'Add field' page.
$this->drupalPostForm($bundle_path, $initial_edit, t('Save and continue'));
$this->assertRaw(t('These settings apply to the %label field everywhere it is used.', ['%label' => $label]), 'Storage settings page was displayed.');
// Test Breadcrumbs.
$this->assertLink($label, 0, 'Field label is correct in the breadcrumb of the storage settings page.');
// Second step: 'Storage settings' form.
$this->drupalPostForm(NULL, $storage_edit, t('Save field settings'));
$this->assertRaw(t('Updated field %label field settings.', ['%label' => $label]), 'Redirected to field settings page.');
// Third step: 'Field settings' form.
$this->drupalPostForm(NULL, $field_edit, t('Save settings'));
$this->assertRaw(t('Saved %label configuration.', ['%label' => $label]), 'Redirected to "Manage fields" page.');
// Check that the field appears in the overview form.
$this->assertFieldByXPath('//table[@id="field-overview"]//tr/td[1]', $label, 'Field was created and appears in the overview page.');
}
/**
* Adds an existing field through the Field UI.
*
* @param string $bundle_path
* Admin path of the bundle that the field is to be attached to.
* @param string $existing_storage_name
* The name of the existing field storage for which we want to add a new
* field.
* @param string $label
* (optional) The label of the new field. Defaults to a random string.
* @param array $field_edit
* (optional) $edit parameter for drupalPostForm() on the second step
* ('Field settings' form).
*/
public function fieldUIAddExistingField($bundle_path, $existing_storage_name, $label = NULL, array $field_edit = []) {
$label = $label ?: $this->randomString();
$initial_edit = [
'existing_storage_name' => $existing_storage_name,
'existing_storage_label' => $label,
];
// First step: 'Re-use existing field' on the 'Add field' page.
$this->drupalPostForm("$bundle_path/fields/add-field", $initial_edit, t('Save and continue'));
// Set the main content to only the content region because the label can
// contain HTML which will be auto-escaped by Twig.
$this->assertRaw('field-config-edit-form', 'The field config edit form is present.');
$this->assertNoRaw('&amp;lt;', 'The page does not have double escaped HTML tags.');
// Second step: 'Field settings' form.
$this->drupalPostForm(NULL, $field_edit, t('Save settings'));
$this->assertRaw(t('Saved %label configuration.', ['%label' => $label]), 'Redirected to "Manage fields" page.');
// Check that the field appears in the overview form.
$this->assertFieldByXPath('//table[@id="field-overview"]//tr/td[1]', $label, 'Field was created and appears in the overview page.');
}
/**
* Deletes a field through the Field UI.
*
* @param string $bundle_path
* Admin path of the bundle that the field is to be deleted from.
* @param string $field_name
* The name of the field.
* @param string $label
* The label of the field.
* @param string $bundle_label
* The label of the bundle.
*/
public function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) {
// Display confirmation form.
$this->drupalGet("$bundle_path/fields/$field_name/delete");
$this->assertRaw(t('Are you sure you want to delete the field %label', ['%label' => $label]), 'Delete confirmation was found.');
// Test Breadcrumbs.
$this->assertLink($label, 0, 'Field label is correct in the breadcrumb of the field delete page.');
// Submit confirmation form.
$this->drupalPostForm(NULL, [], t('Delete'));
$this->assertRaw(t('The field %label has been deleted from the %type content type.', ['%label' => $label, '%type' => $bundle_label]), 'Delete message was found.');
// Check that the field does not appear in the overview form.
$this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, 'Field does not appear in the overview page.');
}
}