Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
|
@ -0,0 +1 @@
|
|||
field_prefix: field_
|
|
@ -0,0 +1,9 @@
|
|||
# Schema for configuration files of the Field UI module.
|
||||
|
||||
field_ui.settings:
|
||||
type: config_object
|
||||
label: 'Field UI settings'
|
||||
mapping:
|
||||
field_prefix:
|
||||
type: string
|
||||
label: 'The prefix for new fields created via Field UI'
|
53
web/core/modules/field_ui/css/field_ui.admin.css
Normal file
53
web/core/modules/field_ui/css/field_ui.admin.css
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @file
|
||||
* Stylesheet for the Field UI module.
|
||||
*/
|
||||
|
||||
/* 'Manage fields' and 'Manage display' overviews */
|
||||
.field-ui-overview .region-title td {
|
||||
font-weight: bold;
|
||||
}
|
||||
.field-ui-overview .region-message td {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 'Manage form display' and 'Manage display' overview */
|
||||
.field-ui-overview .field-plugin-summary-cell {
|
||||
line-height: 1em;
|
||||
}
|
||||
.field-ui-overview .field-plugin-summary {
|
||||
float: left; /* LTR */
|
||||
font-size: .9em;
|
||||
}
|
||||
[dir="rtl"] .field-ui-overview .field-plugin-summary {
|
||||
float: right;
|
||||
}
|
||||
.field-ui-overview .field-plugin-summary-cell .warning {
|
||||
display: block;
|
||||
float: left; /* LTR */
|
||||
margin-right: .5em;
|
||||
}
|
||||
[dir="rtl"] .field-ui-overview .field-plugin-summary-cell .warning {
|
||||
float: right;
|
||||
}
|
||||
.field-ui-overview .field-plugin-settings-edit-wrapper {
|
||||
float: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .field-ui-overview .field-plugin-settings-edit-wrapper {
|
||||
float: left;
|
||||
}
|
||||
.field-ui-overview .field-plugin-settings-edit {
|
||||
float: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .field-ui-overview .field-plugin-settings-edit {
|
||||
float: left;
|
||||
}
|
||||
.field-ui-overview .field-plugin-settings-editing td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.field-ui-overview .field-plugin-settings-editing .field-plugin-type {
|
||||
display: none;
|
||||
}
|
||||
.field-ui-overview .field-plugin-settings-edit-form .plugin-name {
|
||||
font-weight: bold;
|
||||
}
|
127
web/core/modules/field_ui/field_ui.api.php
Normal file
127
web/core/modules/field_ui/field_ui.api.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Field UI module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup field_types
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allow modules to add settings to field formatters provided by other modules.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FormatterInterface $plugin
|
||||
* The instantiated field formatter plugin.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
* @param $view_mode
|
||||
* The entity view mode.
|
||||
* @param array $form
|
||||
* The (entire) configuration form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* Returns the form array to be built.
|
||||
*
|
||||
* @see \Drupal\field_ui\DisplayOverView
|
||||
*/
|
||||
function hook_field_formatter_third_party_settings_form(\Drupal\Core\Field\FormatterInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $view_mode, $form, \Drupal\Core\Form\FormStateInterface $form_state) {
|
||||
$element = array();
|
||||
// Add a 'my_setting' checkbox to the settings form for 'foo_formatter' field
|
||||
// formatters.
|
||||
if ($plugin->getPluginId() == 'foo_formatter') {
|
||||
$element['my_setting'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('My setting'),
|
||||
'#default_value' => $plugin->getThirdPartySetting('my_module', 'my_setting'),
|
||||
);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow modules to add settings to field widgets provided by other modules.
|
||||
*
|
||||
* @param \Drupal\Core\Field\WidgetInterface $plugin
|
||||
* The instantiated field widget plugin.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
* @param $form_mode
|
||||
* The entity form mode.
|
||||
* @param array $form
|
||||
* The (entire) configuration form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* Returns the form array to be built.
|
||||
*
|
||||
* @see \Drupal\field_ui\FormDisplayOverView
|
||||
*/
|
||||
function hook_field_widget_third_party_settings_form(\Drupal\Core\Field\WidgetInterface $plugin, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, $form_mode, $form, \Drupal\Core\Form\FormStateInterface $form_state) {
|
||||
$element = array();
|
||||
// Add a 'my_setting' checkbox to the settings form for 'foo_widget' field
|
||||
// widgets.
|
||||
if ($plugin->getPluginId() == 'foo_widget') {
|
||||
$element['my_setting'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('My setting'),
|
||||
'#default_value' => $plugin->getThirdPartySetting('my_module', 'my_setting'),
|
||||
);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the field formatter settings summary.
|
||||
*
|
||||
* @param array $summary
|
||||
* An array of summary messages.
|
||||
* @param $context
|
||||
* An associative array with the following elements:
|
||||
* - formatter: The formatter object.
|
||||
* - field_definition: The field definition.
|
||||
* - view_mode: The view mode being configured.
|
||||
*
|
||||
* @see \Drupal\field_ui\DisplayOverView
|
||||
*/
|
||||
function hook_field_formatter_settings_summary_alter(&$summary, $context) {
|
||||
// Append a message to the summary when an instance of foo_formatter has
|
||||
// mysetting set to TRUE for the current view mode.
|
||||
if ($context['formatter']->getPluginId() == 'foo_formatter') {
|
||||
if ($context['formatter']->getThirdPartySetting('my_module', 'my_setting')) {
|
||||
$summary[] = t('My setting enabled.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the field widget settings summary.
|
||||
*
|
||||
* @param array $summary
|
||||
* An array of summary messages.
|
||||
* @param array $context
|
||||
* An associative array with the following elements:
|
||||
* - widget: The widget object.
|
||||
* - field_definition: The field definition.
|
||||
* - form_mode: The form mode being configured.
|
||||
*
|
||||
* @see \Drupal\field_ui\FormDisplayOverView
|
||||
*/
|
||||
function hook_field_widget_settings_summary_alter(&$summary, $context) {
|
||||
// Append a message to the summary when an instance of foo_widget has
|
||||
// mysetting set to TRUE for the current view mode.
|
||||
if ($context['widget']->getPluginId() == 'foo_widget') {
|
||||
if ($context['widget']->getThirdPartySetting('my_module', 'my_setting')) {
|
||||
$summary[] = t('My setting enabled.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup field_types".
|
||||
*/
|
8
web/core/modules/field_ui/field_ui.info.yml
Normal file
8
web/core/modules/field_ui/field_ui.info.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
name: 'Field UI'
|
||||
type: module
|
||||
description: 'User interface for the Field API.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- field
|
338
web/core/modules/field_ui/field_ui.js
Normal file
338
web/core/modules/field_ui/field_ui.js
Normal file
|
@ -0,0 +1,338 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches the behaviors for the Field UI module.
|
||||
*/
|
||||
|
||||
(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) {
|
||||
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');
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$(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) {
|
||||
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 () {
|
||||
var $trigger = $(this);
|
||||
var $row = $trigger.closest('tr');
|
||||
var rowHandler = $row.data('fieldUIRowHandler');
|
||||
|
||||
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 () {
|
||||
var dragObject = this;
|
||||
var row = dragObject.rowObject.element;
|
||||
var $row = $(row);
|
||||
var rowHandler = $row.data('fieldUIRowHandler');
|
||||
if (typeof rowHandler !== 'undefined') {
|
||||
var regionRow = $row.prevAll('tr.region-message').get(0);
|
||||
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) {
|
||||
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')) {
|
||||
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: function (rows) {
|
||||
// Separate keys and values.
|
||||
var rowNames = [];
|
||||
var ajaxElements = [];
|
||||
var rowName;
|
||||
for (rowName in rows) {
|
||||
if (rows.hasOwnProperty(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"> </div></div>');
|
||||
|
||||
// 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;
|
||||
|
||||
// Attach change listener to the 'plugin type' select.
|
||||
this.$pluginSelect = $(row).find('select.field-plugin-type');
|
||||
this.$pluginSelect.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: function () {
|
||||
return (this.$pluginSelect.val() === 'hidden') ? 'hidden' : 'content';
|
||||
},
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// When triggered by a row drag, the 'format' select needs to be adjusted
|
||||
// to the new region.
|
||||
var currentValue = this.$pluginSelect.val();
|
||||
var value;
|
||||
// @TODO Check if this couldn't just be like
|
||||
// if (region !== 'hidden') {
|
||||
if (region === 'content') {
|
||||
if (currentValue === 'hidden') {
|
||||
// Restore the formatter back to the default formatter. Pseudo-fields
|
||||
// do not have default formatters, we just return to 'visible' for
|
||||
// those.
|
||||
value = (typeof this.defaultPlugin !== 'undefined') ? this.defaultPlugin : this.$pluginSelect.find('option').val();
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = 'hidden';
|
||||
}
|
||||
|
||||
if (typeof value !== 'undefined') {
|
||||
this.$pluginSelect.val(value);
|
||||
}
|
||||
|
||||
var refreshRows = {};
|
||||
refreshRows[this.name] = this.$pluginSelect.get(0);
|
||||
|
||||
return refreshRows;
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
12
web/core/modules/field_ui/field_ui.libraries.yml
Normal file
12
web/core/modules/field_ui/field_ui.libraries.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
drupal.field_ui:
|
||||
version: VERSION
|
||||
js:
|
||||
field_ui.js: {}
|
||||
css:
|
||||
theme:
|
||||
css/field_ui.admin.css: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/jquery.once
|
17
web/core/modules/field_ui/field_ui.links.action.yml
Normal file
17
web/core/modules/field_ui/field_ui.links.action.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
field_ui.entity_view_mode_add:
|
||||
route_name: field_ui.entity_view_mode_add
|
||||
title: 'Add view mode'
|
||||
weight: 1
|
||||
appears_on:
|
||||
- entity.entity_view_mode.collection
|
||||
|
||||
field_ui.entity_form_mode_add:
|
||||
route_name: field_ui.entity_form_mode_add
|
||||
title: 'Add form mode'
|
||||
weight: 1
|
||||
appears_on:
|
||||
- entity.entity_form_mode.collection
|
||||
|
||||
field_ui.field_storage_config_add:
|
||||
class: \Drupal\Core\Menu\LocalActionDefault
|
||||
deriver: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalAction
|
23
web/core/modules/field_ui/field_ui.links.menu.yml
Normal file
23
web/core/modules/field_ui/field_ui.links.menu.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
entity.field_storage_config.collection:
|
||||
title: 'Field list'
|
||||
description: 'Overview of fields on all entity types.'
|
||||
route_name: entity.field_storage_config.collection
|
||||
parent: system.admin_reports
|
||||
|
||||
field_ui.display_mode:
|
||||
title: 'Display modes'
|
||||
description: 'Configure what displays are available for your content and forms.'
|
||||
route_name: field_ui.display_mode
|
||||
parent: system.admin_structure
|
||||
|
||||
entity.entity_view_mode.collection:
|
||||
title: 'View modes'
|
||||
description: 'Manage custom view modes.'
|
||||
route_name: entity.entity_view_mode.collection
|
||||
parent: field_ui.display_mode
|
||||
|
||||
entity.entity_form_mode.collection:
|
||||
title: 'Form modes'
|
||||
description: 'Manage custom form modes.'
|
||||
route_name: entity.entity_form_mode.collection
|
||||
parent: field_ui.display_mode
|
28
web/core/modules/field_ui/field_ui.links.task.yml
Normal file
28
web/core/modules/field_ui/field_ui.links.task.yml
Normal file
|
@ -0,0 +1,28 @@
|
|||
entity.field_storage_config.collection:
|
||||
title: Entities
|
||||
route_name: entity.field_storage_config.collection
|
||||
base_route: entity.field_storage_config.collection
|
||||
|
||||
field_ui.fields:
|
||||
class: \Drupal\Core\Menu\LocalTaskDefault
|
||||
deriver: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask
|
||||
|
||||
entity.entity_view_mode.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.entity_view_mode.edit_form
|
||||
base_route: entity.entity_view_mode.edit_form
|
||||
|
||||
entity.entity_form_mode.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.entity_form_mode.edit_form
|
||||
base_route: entity.entity_form_mode.edit_form
|
||||
|
||||
entity.entity_view_mode.collection:
|
||||
title: List
|
||||
route_name: entity.entity_view_mode.collection
|
||||
base_route: entity.entity_view_mode.collection
|
||||
|
||||
entity.entity_form_mode.collection:
|
||||
title: List
|
||||
route_name: entity.entity_form_mode.collection
|
||||
base_route: entity.entity_form_mode.collection
|
250
web/core/modules/field_ui/field_ui.module
Normal file
250
web/core/modules/field_ui/field_ui.module
Normal file
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows administrators to attach custom fields to fieldable types.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Entity\EntityViewModeInterface;
|
||||
use Drupal\Core\Entity\EntityFormModeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function field_ui_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.field_ui':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Field UI module provides an administrative user interface (UI) for managing and displaying fields. Fields can be attached to most content entity sub-types. Different field types, widgets, and formatters are provided by the modules enabled on your site, and managed by the Field module. For background information and terminology related to fields and entities, see the <a href=":field">Field module help page</a>. For more information about the Field UI, see the <a href=":field_ui_docs">online documentation for the Field UI module</a>.', array(':field' => \Drupal::url('help.page', array('name' => 'field')), ':field_ui_docs' => 'https://www.drupal.org/documentation/modules/field-ui')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Creating a field') . '</dt>';
|
||||
$output .= '<dd>' . t('On the <em>Manage fields</em> page for your entity type or sub-type, you can add, configure, and delete fields for that entity type or sub-type. Each field has a <em>machine name</em>, which is used internally to identify the field and must be unique across an entity type; once a field is created, you cannot change the machine name. Most fields have two types of settings. The field-level settings depend on the field type, and affect how the data in the field is stored. Once they are set, they can no longer be changed; examples include how many data values are allowed for the field and where files are stored. The sub-type-level settings are specific to each entity sub-type the field is used on, and they can be changed later; examples include the field label, help text, default value, and whether the field is required or not. You can return to these settings by choosing the <em>Edit</em> link for the field from the <em>Manage fields</em> page.');
|
||||
$output .= '<dt>' . t('Re-using fields') . '</dt>';
|
||||
$output .= '<dd>' . t('Once you have created a field, you can use it again in other sub-types of the same entity type. For instance, if you create a field for the article content type, you can also use it for the page content type, but you cannot use it for custom blocks or taxonomy terms. If there are fields available for re-use, after clicking <em>Add field</em> from the <em>Manage fields</em> page, you will see a list of available fields for re-use. After selecting a field for re-use, you can configure the sub-type-level settings.') . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring field editing') . '</dt>';
|
||||
$output .= '<dd>' . t('On the <em>Manage form display</em> page of your entity type or sub-type, you can configure how the field data is edited by default and in each form mode. If your entity type has multiple form modes (on most sites, most entities do not), you can toggle between the form modes at the top of the page, and you can toggle whether each form mode uses the default settings or custom settings in the <em>Custom display settings</em> section. For each field in each form mode, you can select the widget to use for editing; some widgets have additional configuration options, such as the size for a text field, and these can be edited using the Edit button (which looks like a wheel). You can also change the order of the fields on the form. You can exclude a field from a form by choosing <em>Hidden</em> from the widget drop-down list, or by dragging it into the <em>Disabled</em> section.') . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring field display') . '</dt>';
|
||||
$output .= '<dd>' . t('On the <em>Manage display</em> page of your entity type or sub-type, you can configure how each field is displayed by default and in each view mode. If your entity type has multiple view modes, you can toggle between the view modes at the top of the page, and you can toggle whether each view mode uses the default settings or custom settings in the <em>Custom display settings</em> section. For each field in each view mode, you can choose whether and how to display the label of the field from the <em>Label</em> drop-down list. You can also select the formatter to use for display; some formatters have configuration options, which you can edit using the Edit button (which looks like a wheel). You can also change the display order of fields. You can exclude a field from a specific view mode by choosing <em>Hidden</em> from the formatter drop-down list, or by dragging it into the <em>Disabled</em> section.') . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring view and form modes') . '</dt>';
|
||||
$output .= '<dd>' . t('You can add, edit, and delete view modes for entities on the <a href=":view_modes">View modes page</a>, and you can add, edit, and delete form modes for entities on the <a href=":form_modes">Form modes page</a>. Once you have defined a view mode or form mode for an entity type, it will be available on the Manage display or Manage form display page for each sub-type of that entity.', array(':view_modes' => \Drupal::url('entity.entity_view_mode.collection'), ':form_modes' => \Drupal::url('entity.entity_form_mode.collection'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Listing fields') . '</dt>';
|
||||
$output .= '<dd>' . t('There are two reports available that list the fields defined on your site. The <a href=":entity-list" title="Entities field list report">Entities</a> report lists all your fields, showing the field machine names, types, and the entity types or sub-types they are used on (each sub-type links to the Manage fields page). If the <a href=":views">Views</a> and <a href=":views-ui">Views UI</a> modules are enabled, the <a href=":views-list" title="Used in views field list report">Used in views</a> report lists each field that is used in a view, with a link to edit that view.', array(':entity-list' => \Drupal::url('entity.field_storage_config.collection'), ':views-list' => (\Drupal::moduleHandler()->moduleExists('views_ui')) ? \Drupal::url('views_ui.reports_fields') : '#', ':views' => (\Drupal::moduleHandler()->moduleExists('views')) ? \Drupal::url('help.page', array('name' => 'views')) : '#', ':views-ui' => (\Drupal::moduleHandler()->moduleExists('views_ui')) ? \Drupal::url('help.page', array('name' => 'views_ui')) : '#')) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'entity.field_storage_config.collection':
|
||||
return '<p>' . t('This list shows all fields currently in use for easy reference.') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function field_ui_theme() {
|
||||
return array(
|
||||
'field_ui_table' => array(
|
||||
'variables' => array(
|
||||
'header' => NULL,
|
||||
'rows' => NULL,
|
||||
'footer' => NULL,
|
||||
'attributes' => array(),
|
||||
'caption' => NULL,
|
||||
'colgroups' => array(),
|
||||
'sticky' => FALSE,
|
||||
'responsive' => TRUE,
|
||||
'empty' => '',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function field_ui_entity_type_build(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
$entity_types['field_config']->setFormClass('edit', 'Drupal\field_ui\Form\FieldConfigEditForm');
|
||||
$entity_types['field_config']->setFormClass('delete', 'Drupal\field_ui\Form\FieldConfigDeleteForm');
|
||||
$entity_types['field_config']->setListBuilderClass('Drupal\field_ui\FieldConfigListBuilder');
|
||||
|
||||
$entity_types['field_storage_config']->setFormClass('edit', 'Drupal\field_ui\Form\FieldStorageConfigEditForm');
|
||||
$entity_types['field_storage_config']->setListBuilderClass('Drupal\field_ui\FieldStorageConfigListBuilder');
|
||||
$entity_types['field_storage_config']->setLinkTemplate('collection', '/admin/reports/fields');
|
||||
|
||||
$entity_types['entity_form_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityFormDisplayEditForm');
|
||||
$entity_types['entity_view_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityViewDisplayEditForm');
|
||||
|
||||
$form_mode = $entity_types['entity_form_mode'];
|
||||
$form_mode->setListBuilderClass('Drupal\field_ui\EntityFormModeListBuilder');
|
||||
$form_mode->setFormClass('add', 'Drupal\field_ui\Form\EntityFormModeAddForm');
|
||||
$form_mode->setFormClass('edit', 'Drupal\field_ui\Form\EntityDisplayModeEditForm');
|
||||
$form_mode->setFormClass('delete', 'Drupal\field_ui\Form\EntityDisplayModeDeleteForm');
|
||||
$form_mode->set('admin_permission', 'administer display modes');
|
||||
$form_mode->setLinkTemplate('delete-form', '/admin/structure/display-modes/form/manage/{entity_form_mode}/delete');
|
||||
$form_mode->setLinkTemplate('edit-form', '/admin/structure/display-modes/form/manage/{entity_form_mode}');
|
||||
$form_mode->setLinkTemplate('add-form', '/admin/structure/display-modes/form/add/{entity_type_id}');
|
||||
$form_mode->setLinkTemplate('collection', '/admin/structure/display-modes/form');
|
||||
|
||||
$view_mode = $entity_types['entity_view_mode'];
|
||||
$view_mode->setListBuilderClass('Drupal\field_ui\EntityDisplayModeListBuilder');
|
||||
$view_mode->setFormClass('add', 'Drupal\field_ui\Form\EntityDisplayModeAddForm');
|
||||
$view_mode->setFormClass('edit', 'Drupal\field_ui\Form\EntityDisplayModeEditForm');
|
||||
$view_mode->setFormClass('delete', 'Drupal\field_ui\Form\EntityDisplayModeDeleteForm');
|
||||
$view_mode->set('admin_permission', 'administer display modes');
|
||||
$view_mode->setLinkTemplate('delete-form', '/admin/structure/display-modes/view/manage/{entity_view_mode}/delete');
|
||||
$view_mode->setLinkTemplate('edit-form', '/admin/structure/display-modes/view/manage/{entity_view_mode}');
|
||||
$view_mode->setLinkTemplate('add-form', '/admin/structure/display-modes/view/add/{entity_type_id}');
|
||||
$view_mode->setLinkTemplate('collection', '/admin/structure/display-modes/view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_create().
|
||||
*/
|
||||
function field_ui_entity_bundle_create($entity_type, $bundle) {
|
||||
// When a new bundle is created, the menu needs to be rebuilt to add our
|
||||
// menu item tabs.
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
* Adds a button 'Save and manage fields' to the 'Create content type' form.
|
||||
*
|
||||
* @see node_type_form()
|
||||
* @see field_ui_form_node_type_form_submit()
|
||||
*/
|
||||
function field_ui_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
|
||||
// We want to display the button only on add page.
|
||||
if ($form_state->getFormObject()->getEntity()->isNew()) {
|
||||
$form['actions']['save_continue'] = $form['actions']['submit'];
|
||||
$form['actions']['save_continue']['#value'] = t('Save and manage fields');
|
||||
$form['actions']['save_continue']['#weight'] = $form['actions']['save_continue']['#weight'] + 5;
|
||||
$form['actions']['save_continue']['#submit'][] = 'field_ui_form_node_type_form_submit';
|
||||
// Hide the 'Save content type' button.
|
||||
$form['actions']['submit']['#access'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_operation().
|
||||
*/
|
||||
function field_ui_entity_operation(EntityInterface $entity) {
|
||||
$operations = array();
|
||||
$info = $entity->getEntityType();
|
||||
// Add manage fields and display links if this entity type is the bundle
|
||||
// of another and that type has field UI enabled.
|
||||
if (($bundle_of = $info->getBundleOf()) && \Drupal::entityManager()->getDefinition($bundle_of)->get('field_ui_base_route')) {
|
||||
$account = \Drupal::currentUser();
|
||||
if ($account->hasPermission('administer ' . $bundle_of . ' fields')) {
|
||||
$operations['manage-fields'] = array(
|
||||
'title' => t('Manage fields'),
|
||||
'weight' => 15,
|
||||
'url' => Url::fromRoute("entity.{$bundle_of}.field_ui_fields", array(
|
||||
$entity->getEntityTypeId() => $entity->id(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
if ($account->hasPermission('administer ' . $bundle_of . ' form display')) {
|
||||
$operations['manage-form-display'] = array(
|
||||
'title' => t('Manage form display'),
|
||||
'weight' => 20,
|
||||
'url' => Url::fromRoute("entity.entity_form_display.{$bundle_of}.default", array(
|
||||
$entity->getEntityTypeId() => $entity->id(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
if ($account->hasPermission('administer ' . $bundle_of . ' display')) {
|
||||
$operations['manage-display'] = array(
|
||||
'title' => t('Manage display'),
|
||||
'weight' => 25,
|
||||
'url' => Url::fromRoute("entity.entity_view_display.{$bundle_of}.default", array(
|
||||
$entity->getEntityTypeId() => $entity->id(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'Save and manage fields' button.
|
||||
*
|
||||
* @see field_ui_form_node_type_form_alter()
|
||||
*/
|
||||
function field_ui_form_node_type_form_submit($form, FormStateInterface $form_state) {
|
||||
if ($form_state->getTriggeringElement()['#parents'][0] === 'save_continue' && $route_info = FieldUI::getOverviewRouteInfo('node', $form_state->getValue('type'))) {
|
||||
$form_state->setRedirectUrl($route_info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_view_mode_presave().
|
||||
*/
|
||||
function field_ui_entity_view_mode_presave(EntityViewModeInterface $view_mode) {
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_form_mode_presave().
|
||||
*/
|
||||
function field_ui_entity_form_mode_presave(EntityFormModeInterface $form_mode) {
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_view_mode_delete().
|
||||
*/
|
||||
function field_ui_entity_view_mode_delete(EntityViewModeInterface $view_mode) {
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_form_mode_delete().
|
||||
*/
|
||||
function field_ui_entity_form_mode_delete(EntityFormModeInterface $form_mode) {
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares variables for field UI overview table templates.
|
||||
*
|
||||
* Default template: field-ui-table.html.twig.
|
||||
*
|
||||
* @param array $variables
|
||||
* An associative array containing:
|
||||
* - elements: An associative array containing a Form API structure to be
|
||||
* rendered as a table.
|
||||
*/
|
||||
function template_preprocess_field_ui_table(&$variables) {
|
||||
template_preprocess_table($variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_local_tasks_alter().
|
||||
*/
|
||||
function field_ui_local_tasks_alter(&$local_tasks) {
|
||||
$container = \Drupal::getContainer();
|
||||
$local_task = FieldUiLocalTask::create($container, 'field_ui.fields');
|
||||
$local_task->alterLocalTasks($local_tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for 'field_ui_field_storage_add_form'.
|
||||
*/
|
||||
function field_ui_form_field_ui_field_storage_add_form_alter(array &$form) {
|
||||
$optgroup = (string) t('Reference');
|
||||
// Move the "Entity reference" option to the end of the list and rename it to
|
||||
// "Other".
|
||||
unset($form['add']['new_storage_type']['#options'][$optgroup]['entity_reference']);
|
||||
$form['add']['new_storage_type']['#options'][$optgroup]['entity_reference'] = t('Other…');
|
||||
}
|
5
web/core/modules/field_ui/field_ui.permissions.yml
Normal file
5
web/core/modules/field_ui/field_ui.permissions.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
administer display modes:
|
||||
title: 'Add, edit, and delete custom display modes.'
|
||||
|
||||
permission_callbacks:
|
||||
- Drupal\field_ui\FieldUiPermissions::fieldPermissions
|
95
web/core/modules/field_ui/field_ui.routing.yml
Normal file
95
web/core/modules/field_ui/field_ui.routing.yml
Normal file
|
@ -0,0 +1,95 @@
|
|||
entity.field_storage_config.collection:
|
||||
path: '/admin/reports/fields'
|
||||
defaults:
|
||||
_entity_list: 'field_storage_config'
|
||||
_title: 'Field list'
|
||||
requirements:
|
||||
_permission: 'administer content types'
|
||||
|
||||
field_ui.display_mode:
|
||||
path: '/admin/structure/display-modes'
|
||||
defaults:
|
||||
_controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
|
||||
_title: 'Display modes'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
entity.entity_view_mode.collection:
|
||||
path: '/admin/structure/display-modes/view'
|
||||
defaults:
|
||||
_entity_list: 'entity_view_mode'
|
||||
_title: 'View modes'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
field_ui.entity_view_mode_add:
|
||||
path: '/admin/structure/display-modes/view/add'
|
||||
defaults:
|
||||
_controller: '\Drupal\field_ui\Controller\EntityDisplayModeController::viewModeTypeSelection'
|
||||
_title: 'Choose view mode entity type'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
entity.entity_view_mode.add_form:
|
||||
path: '/admin/structure/display-modes/view/add/{entity_type_id}'
|
||||
defaults:
|
||||
_entity_form: 'entity_view_mode.add'
|
||||
_title: 'Add view mode'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
entity.entity_view_mode.edit_form:
|
||||
path: '/admin/structure/display-modes/view/manage/{entity_view_mode}'
|
||||
defaults:
|
||||
_entity_form: 'entity_view_mode.edit'
|
||||
_title: 'Edit view mode'
|
||||
requirements:
|
||||
_entity_access: 'entity_view_mode.update'
|
||||
|
||||
entity.entity_view_mode.delete_form:
|
||||
path: '/admin/structure/display-modes/view/manage/{entity_view_mode}/delete'
|
||||
defaults:
|
||||
_entity_form: 'entity_view_mode.delete'
|
||||
_title: 'Delete view mode'
|
||||
requirements:
|
||||
_entity_access: 'entity_view_mode.delete'
|
||||
|
||||
entity.entity_form_mode.collection:
|
||||
path: '/admin/structure/display-modes/form'
|
||||
defaults:
|
||||
_entity_list: 'entity_form_mode'
|
||||
_title: 'Form modes'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
field_ui.entity_form_mode_add:
|
||||
path: '/admin/structure/display-modes/form/add'
|
||||
defaults:
|
||||
_controller: '\Drupal\field_ui\Controller\EntityDisplayModeController::formModeTypeSelection'
|
||||
_title: 'Choose form mode entity type'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
entity.entity_form_mode.add_form:
|
||||
path: '/admin/structure/display-modes/form/add/{entity_type_id}'
|
||||
defaults:
|
||||
_entity_form: 'entity_form_mode.add'
|
||||
_title: 'Add form mode'
|
||||
requirements:
|
||||
_permission: 'administer display modes'
|
||||
|
||||
entity.entity_form_mode.edit_form:
|
||||
path: '/admin/structure/display-modes/form/manage/{entity_form_mode}'
|
||||
defaults:
|
||||
_entity_form: 'entity_form_mode.edit'
|
||||
_title: 'Edit form mode'
|
||||
requirements:
|
||||
_entity_access: 'entity_form_mode.update'
|
||||
|
||||
entity.entity_form_mode.delete_form:
|
||||
path: '/admin/structure/display-modes/form/manage/{entity_form_mode}/delete'
|
||||
defaults:
|
||||
_entity_form: 'entity_form_mode.delete'
|
||||
_title: 'Delete form mode'
|
||||
requirements:
|
||||
_entity_access: 'entity_form_mode.delete'
|
21
web/core/modules/field_ui/field_ui.services.yml
Normal file
21
web/core/modules/field_ui/field_ui.services.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
services:
|
||||
field_ui.subscriber:
|
||||
class: Drupal\field_ui\Routing\RouteSubscriber
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
field_ui.route_enhancer:
|
||||
class: Drupal\field_ui\Routing\FieldUiRouteEnhancer
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: route_enhancer }
|
||||
access_check.field_ui.view_mode:
|
||||
class: Drupal\field_ui\Access\ViewModeAccessCheck
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _field_ui_view_mode_access }
|
||||
access_check.field_ui.form_mode:
|
||||
class: Drupal\field_ui\Access\FormModeAccessCheck
|
||||
arguments: ['@entity.manager']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _field_ui_form_mode_access }
|
87
web/core/modules/field_ui/src/Access/FormModeAccessCheck.php
Normal file
87
web/core/modules/field_ui/src/Access/FormModeAccessCheck.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Defines an access check for entity form mode routes.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Entity\EntityFormMode
|
||||
*/
|
||||
class FormModeAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Creates a new FormModeAccessCheck.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the form mode.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route to check against.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The parametrized route.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
* @param string $form_mode_name
|
||||
* (optional) The form mode. Defaults to 'default'.
|
||||
* @param string $bundle
|
||||
* (optional) The bundle. Different entity types can have different names
|
||||
* for their bundle key, so if not specified on the route via a {bundle}
|
||||
* parameter, the access checker determines the appropriate key name, and
|
||||
* gets the value from the corresponding request attribute. For example,
|
||||
* for nodes, the bundle key is "node_type", so the value would be
|
||||
* available via the {node_type} parameter rather than a {bundle}
|
||||
* parameter.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $form_mode_name = 'default', $bundle = NULL) {
|
||||
$access = AccessResult::neutral();
|
||||
if ($entity_type_id = $route->getDefault('entity_type_id')) {
|
||||
if (empty($bundle)) {
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$bundle = $route_match->getRawParameter($entity_type->getBundleEntityType());
|
||||
}
|
||||
|
||||
$visibility = FALSE;
|
||||
if ($form_mode_name == 'default') {
|
||||
$visibility = TRUE;
|
||||
}
|
||||
elseif ($entity_display = $this->entityManager->getStorage('entity_form_display')->load($entity_type_id . '.' . $bundle . '.' . $form_mode_name)) {
|
||||
$visibility = $entity_display->status();
|
||||
}
|
||||
|
||||
if ($form_mode_name != 'default' && $entity_display) {
|
||||
$access->addCacheableDependency($entity_display);
|
||||
}
|
||||
|
||||
if ($visibility) {
|
||||
$permission = $route->getRequirement('_field_ui_form_mode_access');
|
||||
$access = $access->orIf(AccessResult::allowedIfHasPermission($account, $permission));
|
||||
}
|
||||
}
|
||||
return $access;
|
||||
}
|
||||
|
||||
}
|
87
web/core/modules/field_ui/src/Access/ViewModeAccessCheck.php
Normal file
87
web/core/modules/field_ui/src/Access/ViewModeAccessCheck.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Defines an access check for entity view mode routes.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Entity\EntityViewMode
|
||||
*/
|
||||
class ViewModeAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Creates a new ViewModeAccessCheck.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the view mode.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route to check against.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The parametrized route.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
* @param string $view_mode_name
|
||||
* (optional) The view mode. Defaults to 'default'.
|
||||
* @param string $bundle
|
||||
* (optional) The bundle. Different entity types can have different names
|
||||
* for their bundle key, so if not specified on the route via a {bundle}
|
||||
* parameter, the access checker determines the appropriate key name, and
|
||||
* gets the value from the corresponding request attribute. For example,
|
||||
* for nodes, the bundle key is "node_type", so the value would be
|
||||
* available via the {node_type} parameter rather than a {bundle}
|
||||
* parameter.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $view_mode_name = 'default', $bundle = NULL) {
|
||||
$access = AccessResult::neutral();
|
||||
if ($entity_type_id = $route->getDefault('entity_type_id')) {
|
||||
if (empty($bundle)) {
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$bundle = $route_match->getRawParameter($entity_type->getBundleEntityType());
|
||||
}
|
||||
|
||||
$visibility = FALSE;
|
||||
if ($view_mode_name == 'default') {
|
||||
$visibility = TRUE;
|
||||
}
|
||||
elseif ($entity_display = $this->entityManager->getStorage('entity_view_display')->load($entity_type_id . '.' . $bundle . '.' . $view_mode_name)) {
|
||||
$visibility = $entity_display->status();
|
||||
}
|
||||
|
||||
if ($view_mode_name != 'default' && $entity_display) {
|
||||
$access->addCacheableDependency($entity_display);
|
||||
}
|
||||
|
||||
if ($visibility) {
|
||||
$permission = $route->getRequirement('_field_ui_view_mode_access');
|
||||
$access = $access->orIf(AccessResult::allowedIfHasPermission($account, $permission));
|
||||
}
|
||||
}
|
||||
return $access;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides methods for entity display mode routes.
|
||||
*/
|
||||
class EntityDisplayModeController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Provides a list of eligible entity types for adding view modes.
|
||||
*
|
||||
* @return array
|
||||
* A list of entity types to add a view mode for.
|
||||
*/
|
||||
public function viewModeTypeSelection() {
|
||||
$entity_types = array();
|
||||
foreach ($this->entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->get('field_ui_base_route') && $entity_type->hasViewBuilderClass()) {
|
||||
$entity_types[$entity_type_id] = array(
|
||||
'title' => $entity_type->getLabel(),
|
||||
'url' => Url::fromRoute('entity.entity_view_mode.add_form', array('entity_type_id' => $entity_type_id)),
|
||||
'localized_options' => array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
return array(
|
||||
'#theme' => 'admin_block_content',
|
||||
'#content' => $entity_types,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of eligible entity types for adding form modes.
|
||||
*
|
||||
* @return array
|
||||
* A list of entity types to add a form mode for.
|
||||
*/
|
||||
public function formModeTypeSelection() {
|
||||
$entity_types = array();
|
||||
foreach ($this->entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->get('field_ui_base_route') && $entity_type->hasFormClasses()) {
|
||||
$entity_types[$entity_type_id] = array(
|
||||
'title' => $entity_type->getLabel(),
|
||||
'url' => Url::fromRoute('entity.entity_form_mode.add_form', array('entity_type_id' => $entity_type_id)),
|
||||
'localized_options' => array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
return array(
|
||||
'#theme' => 'admin_block_content',
|
||||
'#content' => $entity_types,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Controller;
|
||||
|
||||
use Drupal\Core\Entity\Controller\EntityListController;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Defines a controller to list field instances.
|
||||
*/
|
||||
class FieldConfigListController extends EntityListController {
|
||||
|
||||
/**
|
||||
* Shows the 'Manage fields' page.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type.
|
||||
* @param string $bundle
|
||||
* The entity bundle.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The current route match.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_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);
|
||||
}
|
||||
|
||||
}
|
239
web/core/modules/field_ui/src/Element/FieldUiTable.php
Normal file
239
web/core/modules/field_ui/src/Element/FieldUiTable.php
Normal file
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Element;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\Table;
|
||||
|
||||
/**
|
||||
* Provides a field_ui table element.
|
||||
*
|
||||
* @RenderElement("field_ui_table")
|
||||
*/
|
||||
class FieldUiTable extends Table {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
$info['#regions'] = ['' => []];
|
||||
$info['#theme'] = 'field_ui_table';
|
||||
// Prepend FieldUiTable's prerender callbacks.
|
||||
array_unshift($info['#pre_render'], [$this, 'tablePreRender'], [$this, 'preRenderRegionRows']);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-render tasks on field_ui_table elements.
|
||||
*
|
||||
* @param array $elements
|
||||
* A structured array containing two sub-levels of elements. Properties
|
||||
* used:
|
||||
* - #tabledrag: The value is a list of $options arrays that are passed to
|
||||
* drupal_attach_tabledrag(). The HTML ID of the table is added to each
|
||||
* $options array.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for field-ui-table.html.twig.
|
||||
*
|
||||
* @see drupal_render()
|
||||
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
||||
*/
|
||||
public static function tablePreRender($elements) {
|
||||
$js_settings = array();
|
||||
|
||||
// For each region, build the tree structure from the weight and parenting
|
||||
// data contained in the flat form structure, to determine row order and
|
||||
// indentation.
|
||||
$regions = $elements['#regions'];
|
||||
$tree = ['' => ['name' => '', 'children' => []]];
|
||||
$trees = array_fill_keys(array_keys($regions), $tree);
|
||||
|
||||
$parents = [];
|
||||
$children = Element::children($elements);
|
||||
$list = array_combine($children, $children);
|
||||
|
||||
// Iterate on rows until we can build a known tree path for all of them.
|
||||
while ($list) {
|
||||
foreach ($list as $name) {
|
||||
$row = &$elements[$name];
|
||||
$parent = $row['parent_wrapper']['parent']['#value'];
|
||||
// Proceed if parent is known.
|
||||
if (empty($parent) || isset($parents[$parent])) {
|
||||
// Grab parent, and remove the row from the next iteration.
|
||||
$parents[$name] = $parent ? array_merge($parents[$parent], [$parent]) : [];
|
||||
unset($list[$name]);
|
||||
|
||||
// Determine the region for the row.
|
||||
$region_name = call_user_func($row['#region_callback'], $row);
|
||||
|
||||
// Add the element in the tree.
|
||||
$target = &$trees[$region_name][''];
|
||||
foreach ($parents[$name] as $key) {
|
||||
$target = &$target['children'][$key];
|
||||
}
|
||||
$target['children'][$name] = ['name' => $name, 'weight' => $row['weight']['#value']];
|
||||
|
||||
// Add tabledrag indentation to the first row cell.
|
||||
if ($depth = count($parents[$name])) {
|
||||
$children = Element::children($row);
|
||||
$cell = current($children);
|
||||
$indentation = [
|
||||
'#theme' => 'indentation',
|
||||
'#size' => $depth,
|
||||
'#suffix' => isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '',
|
||||
];
|
||||
$row[$cell]['#prefix'] = \Drupal::service('renderer')->render($indentation);
|
||||
}
|
||||
|
||||
// Add row id and associate JS settings.
|
||||
$id = Html::getClass($name);
|
||||
$row['#attributes']['id'] = $id;
|
||||
if (isset($row['#js_settings'])) {
|
||||
$row['#js_settings'] += [
|
||||
'rowHandler' => $row['#row_type'],
|
||||
'name' => $name,
|
||||
'region' => $region_name,
|
||||
];
|
||||
$js_settings[$id] = $row['#js_settings'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine rendering order from the tree structure.
|
||||
foreach ($regions as $region_name => $region) {
|
||||
$elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], [static::class, 'reduceOrder']);
|
||||
}
|
||||
|
||||
$elements['#attached']['drupalSettings']['fieldUIRowsData'] = $js_settings;
|
||||
|
||||
// If the custom #tabledrag is set and there is a HTML ID, add the table's
|
||||
// HTML ID to the options and attach the behavior.
|
||||
// @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
||||
if (!empty($elements['#tabledrag']) && isset($elements['#attributes']['id'])) {
|
||||
foreach ($elements['#tabledrag'] as $options) {
|
||||
$options['table_id'] = $elements['#attributes']['id'];
|
||||
drupal_attach_tabledrag($elements, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-render to move #regions to rows.
|
||||
*
|
||||
* @param array $elements
|
||||
* A structured array containing two sub-levels of elements. Properties
|
||||
* used:
|
||||
* - #tabledrag: The value is a list of $options arrays that are passed to
|
||||
* drupal_attach_tabledrag(). The HTML ID of the table is added to each
|
||||
* $options array.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for field-ui-table.html.twig.
|
||||
*/
|
||||
public static function preRenderRegionRows($elements) {
|
||||
// Determine the colspan to use for region rows, by checking the number of
|
||||
// columns in the headers.
|
||||
$columns_count = 0;
|
||||
foreach ($elements['#header'] as $header) {
|
||||
$columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach (Element::children($elements) as $key) {
|
||||
$rows[$key] = $elements[$key];
|
||||
unset($elements[$key]);
|
||||
}
|
||||
|
||||
// Render rows, region by region.
|
||||
foreach ($elements['#regions'] as $region_name => $region) {
|
||||
$region_name_class = Html::getClass($region_name);
|
||||
|
||||
// Add region rows.
|
||||
if (isset($region['title']) && empty($region['invisible'])) {
|
||||
$elements['#rows'][] = [
|
||||
'class' => [
|
||||
'region-title',
|
||||
'region-' . $region_name_class . '-title'
|
||||
],
|
||||
'no_striping' => TRUE,
|
||||
'data' => [
|
||||
['data' => $region['title'], 'colspan' => $columns_count],
|
||||
],
|
||||
];
|
||||
}
|
||||
if (isset($region['message'])) {
|
||||
$class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated');
|
||||
$elements['#rows'][] = [
|
||||
'class' => [
|
||||
'region-message',
|
||||
'region-' . $region_name_class . '-message', $class,
|
||||
],
|
||||
'no_striping' => TRUE,
|
||||
'data' => [
|
||||
['data' => $region['message'], 'colspan' => $columns_count],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// Add form rows, in the order determined at pre-render time.
|
||||
foreach ($region['rows_order'] as $name) {
|
||||
$element = $rows[$name];
|
||||
|
||||
$row = ['data' => []];
|
||||
if (isset($element['#attributes'])) {
|
||||
$row += $element['#attributes'];
|
||||
}
|
||||
|
||||
// Render children as table cells.
|
||||
foreach (Element::children($element) as $cell_key) {
|
||||
$child = $element[$cell_key];
|
||||
// Do not render a cell for children of #type 'value'.
|
||||
if (!(isset($child['#type']) && $child['#type'] == 'value')) {
|
||||
$cell = ['data' => $child];
|
||||
if (isset($child['#cell_attributes'])) {
|
||||
$cell += $child['#cell_attributes'];
|
||||
}
|
||||
$row['data'][] = $cell;
|
||||
}
|
||||
}
|
||||
$elements['#rows'][] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the rendering order of an array representing a tree.
|
||||
*
|
||||
* Callback for array_reduce() within ::tablePreRender().
|
||||
*
|
||||
* @param mixed $array
|
||||
* Holds the return value of the previous iteration; in the case of the
|
||||
* first iteration it instead holds the value of the initial array.
|
||||
* @param mixed $a
|
||||
* Holds the value of the current iteration.
|
||||
*
|
||||
* @return array
|
||||
* Array where rendering order has been determined.
|
||||
*/
|
||||
public static function reduceOrder($array, $a) {
|
||||
$array = !$array ? [] : $array;
|
||||
if ($a['name']) {
|
||||
$array[] = $a['name'];
|
||||
}
|
||||
if (!empty($a['children'])) {
|
||||
uasort($a['children'], ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
|
||||
$array = array_merge($array, array_reduce($a['children'], [static::class, 'reduceOrder']));
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
141
web/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
Normal file
141
web/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of view mode entities.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Entity\EntityViewMode
|
||||
*/
|
||||
class EntityDisplayModeListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* All entity types.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface[]
|
||||
*/
|
||||
protected $entityTypes;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityDisplayModeListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
|
||||
* List of all entity types.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, array $entity_types) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->entityTypes = $entity_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
$entity_manager = $container->get('entity.manager');
|
||||
return new static(
|
||||
$entity_type,
|
||||
$entity_manager->getStorage($entity_type->id()),
|
||||
$entity_manager->getDefinitions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Name');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = $entity->label();
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
$entities = array();
|
||||
foreach (parent::load() as $entity) {
|
||||
$entities[$entity->getTargetType()][] = $entity;
|
||||
}
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build = array();
|
||||
foreach ($this->load() as $entity_type => $entities) {
|
||||
if (!isset($this->entityTypes[$entity_type])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter entities.
|
||||
if (!$this->isValidEntity($entity_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$table = array(
|
||||
'#prefix' => '<h2>' . $this->entityTypes[$entity_type]->getLabel() . '</h2>',
|
||||
'#type' => 'table',
|
||||
'#header' => $this->buildHeader(),
|
||||
'#rows' => array(),
|
||||
);
|
||||
foreach ($entities as $entity) {
|
||||
if ($row = $this->buildRow($entity)) {
|
||||
$table['#rows'][$entity->id()] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
// Move content at the top.
|
||||
if ($entity_type == 'node') {
|
||||
$table['#weight'] = -10;
|
||||
}
|
||||
|
||||
$short_type = str_replace(array('entity_', '_mode'), '', $this->entityTypeId);
|
||||
$table['#rows']['_add_new'][] = array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#url' => Url::fromRoute($short_type == 'view' ? 'entity.entity_view_mode.add_form' : 'entity.entity_form_mode.add_form', ['entity_type_id' => $entity_type]),
|
||||
'#title' => $this->t('Add new %label @entity-type', array('%label' => $this->entityTypes[$entity_type]->getLabel(), '@entity-type' => $this->entityType->getLowercaseLabel())),
|
||||
),
|
||||
'colspan' => count($table['#header']),
|
||||
);
|
||||
$build[$entity_type] = $table;
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entities based on their view builder handlers.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type of the entity that needs to be validated.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity has the correct view builder handler, FALSE if the
|
||||
* entity doesn't have the correct view builder handler.
|
||||
*/
|
||||
protected function isValidEntity($entity_type) {
|
||||
return $this->entityTypes[$entity_type]->get('field_ui_base_route') && $this->entityTypes[$entity_type]->hasViewBuilderClass();
|
||||
}
|
||||
|
||||
}
|
25
web/core/modules/field_ui/src/EntityFormModeListBuilder.php
Normal file
25
web/core/modules/field_ui/src/EntityFormModeListBuilder.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of form mode entities.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Entity\EntityFormMode
|
||||
*/
|
||||
class EntityFormModeListBuilder extends EntityDisplayModeListBuilder {
|
||||
|
||||
/**
|
||||
* Filters entities based on their form mode handlers.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type of the entity that needs to be validated.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity has any forms, FALSE otherwise.
|
||||
*/
|
||||
protected function isValidEntity($entity_type) {
|
||||
return $this->entityTypes[$entity_type]->hasFormClasses();
|
||||
}
|
||||
|
||||
}
|
186
web/core/modules/field_ui/src/FieldConfigListBuilder.php
Normal file
186
web/core/modules/field_ui/src/FieldConfigListBuilder.php
Normal file
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides lists of field config entities.
|
||||
*/
|
||||
class FieldConfigListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The name of the entity type the listed fields are attached to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetEntityTypeId;
|
||||
|
||||
/**
|
||||
* The name of the bundle the listed fields are attached to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetBundle;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new class instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type manager
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_type, $entity_manager->getStorage($entity_type->id()));
|
||||
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static($entity_type, $container->get('entity.manager'), $container->get('plugin.manager.field.field_type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($target_entity_type_id = NULL, $target_bundle = NULL) {
|
||||
$this->targetEntityTypeId = $target_entity_type_id;
|
||||
$this->targetBundle = $target_bundle;
|
||||
|
||||
$build = parent::render();
|
||||
$build['table']['#attributes']['id'] = 'field-overview';
|
||||
$build['table']['#empty'] = $this->t('No fields are present yet.');
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
$entities = array_filter($this->entityManager->getFieldDefinitions($this->targetEntityTypeId, $this->targetBundle), function ($field_definition) {
|
||||
return $field_definition instanceof FieldConfigInterface;
|
||||
});
|
||||
|
||||
// Sort the entities using the entity class's sort() method.
|
||||
// See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
|
||||
uasort($entities, array($this->entityType->getClass(), 'sort'));
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header = array(
|
||||
'label' => $this->t('Label'),
|
||||
'field_name' => array(
|
||||
'data' => $this->t('Machine name'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
),
|
||||
'field_type' => $this->t('Field type'),
|
||||
);
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $field_config) {
|
||||
/** @var \Drupal\field\FieldConfigInterface $field_config */
|
||||
$field_storage = $field_config->getFieldStorageDefinition();
|
||||
$route_parameters = array(
|
||||
'field_config' => $field_config->id(),
|
||||
) + FieldUI::getRouteBundleParameter($this->entityManager->getDefinition($this->targetEntityTypeId), $this->targetBundle);
|
||||
|
||||
$row = array(
|
||||
'id' => Html::getClass($field_config->getName()),
|
||||
'data' => array(
|
||||
'label' => $field_config->getLabel(),
|
||||
'field_name' => $field_config->getName(),
|
||||
'field_type' => array(
|
||||
'data' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->fieldTypeManager->getDefinitions()[$field_storage->getType()]['label'],
|
||||
'#url' => Url::fromRoute("entity.field_config.{$this->targetEntityTypeId}_storage_edit_form", $route_parameters),
|
||||
'#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Add the operations.
|
||||
$row['data'] = $row['data'] + parent::buildRow($field_config);
|
||||
|
||||
if ($field_storage->isLocked()) {
|
||||
$row['data']['operations'] = array('data' => array('#markup' => $this->t('Locked')));
|
||||
$row['class'][] = 'menu-disabled';
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
/** @var \Drupal\field\FieldConfigInterface $entity */
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if ($entity->access('update') && $entity->hasLinkTemplate("{$entity->getTargetEntityTypeId()}-field-edit-form")) {
|
||||
$operations['edit'] = array(
|
||||
'title' => $this->t('Edit'),
|
||||
'weight' => 10,
|
||||
'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-edit-form"),
|
||||
);
|
||||
}
|
||||
if ($entity->access('delete') && $entity->hasLinkTemplate("{$entity->getTargetEntityTypeId()}-field-delete-form")) {
|
||||
$operations['delete'] = array(
|
||||
'title' => $this->t('Delete'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-field-delete-form"),
|
||||
);
|
||||
}
|
||||
|
||||
$operations['storage-settings'] = array(
|
||||
'title' => $this->t('Storage settings'),
|
||||
'weight' => 20,
|
||||
'attributes' => array('title' => $this->t('Edit storage settings.')),
|
||||
'url' => $entity->urlInfo("{$entity->getTargetEntityTypeId()}-storage-edit-form"),
|
||||
);
|
||||
$operations['edit']['attributes']['title'] = $this->t('Edit field settings.');
|
||||
$operations['delete']['attributes']['title'] = $this->t('Delete field.');
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
126
web/core/modules/field_ui/src/FieldStorageConfigListBuilder.php
Normal file
126
web/core/modules/field_ui/src/FieldStorageConfigListBuilder.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of fields.
|
||||
*
|
||||
* @see \Drupal\field\Entity\Field
|
||||
* @see field_ui_entity_info()
|
||||
*/
|
||||
class FieldStorageConfigListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* An array of information about field types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldTypes;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* An array of entity bundle information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundles;
|
||||
|
||||
/**
|
||||
* The field type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldStorageConfigListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The 'field type' plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, EntityTypeBundleInfoInterface $bundle_info_service) {
|
||||
parent::__construct($entity_type, $entity_manager->getStorage($entity_type->id()));
|
||||
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->bundles = $bundle_info_service->getAllBundleInfo();
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
$this->fieldTypes = $this->fieldTypeManager->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.field.field_type'),
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['id'] = $this->t('Field name');
|
||||
$header['type'] = array(
|
||||
'data' => $this->t('Field type'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
);
|
||||
$header['usage'] = $this->t('Used in');
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $field_storage) {
|
||||
if ($field_storage->isLocked()) {
|
||||
$row['class'] = array('menu-disabled');
|
||||
$row['data']['id'] = $this->t('@field_name (Locked)', array('@field_name' => $field_storage->getName()));
|
||||
}
|
||||
else {
|
||||
$row['data']['id'] = $field_storage->getName();
|
||||
}
|
||||
|
||||
$field_type = $this->fieldTypes[$field_storage->getType()];
|
||||
$row['data']['type'] = $this->t('@type (module: @module)', array('@type' => $field_type['label'], '@module' => $field_type['provider']));
|
||||
|
||||
$usage = array();
|
||||
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);
|
||||
}
|
||||
else {
|
||||
$usage[] = $this->bundles[$entity_type_id][$bundle]['label'];
|
||||
}
|
||||
}
|
||||
$row['data']['usage']['data'] = [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $usage,
|
||||
'#context' => ['list_style' => 'comma-list'],
|
||||
];
|
||||
return $row;
|
||||
}
|
||||
|
||||
}
|
83
web/core/modules/field_ui/src/FieldUI.php
Normal file
83
web/core/modules/field_ui/src/FieldUI.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Static service container wrapper for Field UI.
|
||||
*/
|
||||
class FieldUI {
|
||||
|
||||
/**
|
||||
* Returns the route info for the field overview of a given entity bundle.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* An entity type.
|
||||
* @param string $bundle
|
||||
* The entity bundle.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* A URL object.
|
||||
*/
|
||||
public static function getOverviewRouteInfo($entity_type_id, $bundle) {
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
|
||||
if ($entity_type->get('field_ui_base_route')) {
|
||||
return new Url("entity.{$entity_type_id}.field_ui_fields", static::getRouteBundleParameter($entity_type, $bundle));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next redirect path in a multipage sequence.
|
||||
*
|
||||
* @param array $destinations
|
||||
* An array of destinations to redirect to.
|
||||
*
|
||||
* @return \Drupal\Core\Url|null
|
||||
* The next destination to redirect to.
|
||||
*/
|
||||
public static function getNextDestination(array $destinations) {
|
||||
// If there are no valid destinations left, return here.
|
||||
if (empty($destinations)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$next_destination = array_shift($destinations);
|
||||
if (is_array($next_destination)) {
|
||||
$next_destination['options']['query']['destinations'] = $destinations;
|
||||
$next_destination += array(
|
||||
'route_parameters' => array(),
|
||||
);
|
||||
$next_destination = Url::fromRoute($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
|
||||
}
|
||||
else {
|
||||
$options = UrlHelper::parse($next_destination);
|
||||
if ($destinations) {
|
||||
$options['query']['destinations'] = $destinations;
|
||||
}
|
||||
// Redirect to any given path within the same domain.
|
||||
// @todo Revisit this in https://www.drupal.org/node/2418219.
|
||||
$next_destination = Url::fromUserInput('/' . $options['path'], $options);
|
||||
}
|
||||
return $next_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the route parameter that should be used for Field UI routes.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The actual entity type, not the bundle (e.g. the content entity type).
|
||||
* @param string $bundle
|
||||
* The bundle name.
|
||||
*
|
||||
* @return array
|
||||
* An array that can be used a route parameter.
|
||||
*/
|
||||
public static function getRouteBundleParameter(EntityTypeInterface $entity_type, $bundle) {
|
||||
$bundle_parameter_key = $entity_type->getBundleEntityType() ?: 'bundle';
|
||||
return array($bundle_parameter_key => $bundle);
|
||||
}
|
||||
|
||||
}
|
69
web/core/modules/field_ui/src/FieldUiPermissions.php
Normal file
69
web/core/modules/field_ui/src/FieldUiPermissions.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic permissions of the field_ui module.
|
||||
*/
|
||||
class FieldUiPermissions implements ContainerInjectionInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldUiPermissions instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of field UI permissions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fieldPermissions() {
|
||||
$permissions = [];
|
||||
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->get('field_ui_base_route')) {
|
||||
// Create a permission for each fieldable entity to manage
|
||||
// the fields and the display.
|
||||
$permissions['administer ' . $entity_type_id . ' fields'] = [
|
||||
'title' => $this->t('%entity_label: Administer fields', ['%entity_label' => $entity_type->getLabel()]),
|
||||
'restrict access' => TRUE,
|
||||
];
|
||||
$permissions['administer ' . $entity_type_id . ' form display'] = [
|
||||
'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()])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
}
|
936
web/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
Normal file
936
web/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
Normal file
|
@ -0,0 +1,936 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Field\PluginSettingsInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\field_ui\Element\FieldUiTable;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
|
||||
/**
|
||||
* Base class for EntityDisplay edit forms.
|
||||
*/
|
||||
abstract class EntityDisplayFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The display context. Either 'view' or 'form'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $displayContext;
|
||||
|
||||
/**
|
||||
* The widget or formatter plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerBase
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* A list of field types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldTypes;
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Display\EntityDisplayInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityDisplayFormBase.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type manager.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerBase $plugin_manager
|
||||
* The widget or formatter plugin manager.
|
||||
*/
|
||||
public function __construct(FieldTypePluginManagerInterface $field_type_manager, PluginManagerBase $plugin_manager) {
|
||||
$this->fieldTypes = $field_type_manager->getDefinitions();
|
||||
$this->pluginManager = $plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
|
||||
$route_parameters = $route_match->getParameters()->all();
|
||||
|
||||
return $this->getEntityDisplay($route_parameters['entity_type_id'], $route_parameters['bundle'], $route_parameters[$this->displayContext . '_mode_name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regions needed to create the overview form.
|
||||
*
|
||||
* @return array
|
||||
* Example usage:
|
||||
* @code
|
||||
* return array(
|
||||
* 'content' => array(
|
||||
* // label for the region.
|
||||
* 'title' => $this->t('Content'),
|
||||
* // Indicates if the region is visible in the UI.
|
||||
* 'invisible' => TRUE,
|
||||
* // A message to indicate that there is nothing to be displayed in
|
||||
* // the region.
|
||||
* 'message' => $this->t('No field is displayed.'),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*/
|
||||
public function getRegions() {
|
||||
return array(
|
||||
'content' => array(
|
||||
'title' => $this->t('Content'),
|
||||
'invisible' => TRUE,
|
||||
'message' => $this->t('No field is displayed.')
|
||||
),
|
||||
'hidden' => array(
|
||||
'title' => $this->t('Disabled', array(), array('context' => 'Plural')),
|
||||
'message' => $this->t('No field is hidden.')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of all regions.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the region options.
|
||||
*/
|
||||
public function getRegionOptions() {
|
||||
$options = array();
|
||||
foreach ($this->getRegions() as $region => $data) {
|
||||
$options[$region] = $data['title'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the definitions of fields whose display is configurable.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
|
||||
* The array of field definitions
|
||||
*/
|
||||
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 $field_definition->isDisplayConfigurable($context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$field_definitions = $this->getFieldDefinitions();
|
||||
$extra_fields = $this->getExtraFields();
|
||||
|
||||
$form += array(
|
||||
'#entity_type' => $this->entity->getTargetEntityTypeId(),
|
||||
'#bundle' => $this->entity->getTargetBundle(),
|
||||
'#fields' => array_keys($field_definitions),
|
||||
'#extra' => array_keys($extra_fields),
|
||||
);
|
||||
|
||||
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.', array(':link' => $route_info->toString())), 'warning');
|
||||
return $form;
|
||||
}
|
||||
|
||||
$table = array(
|
||||
'#type' => 'field_ui_table',
|
||||
'#header' => $this->getTableHeader(),
|
||||
'#regions' => $this->getRegions(),
|
||||
'#attributes' => array(
|
||||
'class' => array('field-ui-overview'),
|
||||
'id' => 'field-display-overview',
|
||||
),
|
||||
'#tabledrag' => array(
|
||||
array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'field-weight',
|
||||
),
|
||||
array(
|
||||
'action' => 'match',
|
||||
'relationship' => 'parent',
|
||||
'group' => 'field-parent',
|
||||
'subgroup' => 'field-parent',
|
||||
'source' => 'field-name',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Field rows.
|
||||
foreach ($field_definitions as $field_name => $field_definition) {
|
||||
$table[$field_name] = $this->buildFieldRow($field_definition, $form, $form_state);
|
||||
}
|
||||
|
||||
// Non-field elements.
|
||||
foreach ($extra_fields as $field_id => $extra_field) {
|
||||
$table[$field_id] = $this->buildExtraFieldRow($field_id, $extra_field);
|
||||
}
|
||||
|
||||
$form['fields'] = $table;
|
||||
|
||||
// Custom display settings.
|
||||
if ($this->entity->getMode() == 'default') {
|
||||
// Only show the settings if there is at least one custom display mode.
|
||||
$display_mode_options = $this->getDisplayModeOptions();
|
||||
// Unset default option.
|
||||
unset($display_mode_options['default']);
|
||||
if ($display_mode_options) {
|
||||
$form['modes'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Custom display settings'),
|
||||
);
|
||||
// Prepare default values for the 'Custom display settings' checkboxes.
|
||||
$default = array();
|
||||
if ($enabled_displays = array_filter($this->getDisplayStatuses())) {
|
||||
$default = array_keys(array_intersect_key($display_mode_options, $enabled_displays));
|
||||
}
|
||||
$form['modes']['display_modes_custom'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Use custom display settings for the following @display_context modes', ['@display_context' => $this->displayContext]),
|
||||
'#options' => $display_mode_options,
|
||||
'#default_value' => $default,
|
||||
);
|
||||
// Provide link to manage display modes.
|
||||
$form['modes']['display_modes_link'] = $this->getDisplayModesLink();
|
||||
}
|
||||
}
|
||||
|
||||
// In overviews involving nested rows from contributed modules (i.e
|
||||
// field_group), the 'plugin type' selects can trigger a series of changes
|
||||
// in child rows. The #ajax behavior is therefore not attached directly to
|
||||
// the selects, but triggered by the client-side script through a hidden
|
||||
// #ajax 'Refresh' button. A hidden 'refresh_rows' input tracks the name of
|
||||
// affected rows.
|
||||
$form['refresh_rows'] = array('#type' => 'hidden');
|
||||
$form['refresh'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Refresh'),
|
||||
'#op' => 'refresh_table',
|
||||
'#submit' => array('::multistepSubmit'),
|
||||
'#ajax' => array(
|
||||
'callback' => '::multistepAjax',
|
||||
'wrapper' => 'field-display-overview-wrapper',
|
||||
'effect' => 'fade',
|
||||
// The button stays hidden, so we hide the Ajax spinner too. Ad-hoc
|
||||
// spinners will be added manually by the client-side script.
|
||||
'progress' => 'none',
|
||||
),
|
||||
'#attributes' => array('class' => array('visually-hidden'))
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#button_type' => 'primary',
|
||||
'#value' => $this->t('Save'),
|
||||
);
|
||||
|
||||
$form['#attached']['library'][] = 'field_ui/drupal.field_ui';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the table row structure for a single field.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
* A table row array.
|
||||
*/
|
||||
protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
|
||||
$field_name = $field_definition->getName();
|
||||
$display_options = $this->entity->getComponent($field_name);
|
||||
$label = $field_definition->getLabel();
|
||||
|
||||
// Disable fields without any applicable plugins.
|
||||
if (empty($this->getApplicablePluginOptions($field_definition))) {
|
||||
$this->entity->removeComponent($field_name)->save();
|
||||
$display_options = $this->entity->getComponent($field_name);
|
||||
}
|
||||
|
||||
$regions = array_keys($this->getRegions());
|
||||
$field_row = array(
|
||||
'#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
|
||||
'#row_type' => 'field',
|
||||
'#region_callback' => array($this, 'getRowRegion'),
|
||||
'#js_settings' => array(
|
||||
'rowHandler' => 'field',
|
||||
'defaultPlugin' => $this->getDefaultPlugin($field_definition->getType()),
|
||||
),
|
||||
'human_name' => array(
|
||||
'#plain_text' => $label,
|
||||
),
|
||||
'weight' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Weight for @title', array('@title' => $label)),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $display_options ? $display_options['weight'] : '0',
|
||||
'#size' => 3,
|
||||
'#attributes' => array('class' => array('field-weight')),
|
||||
),
|
||||
'parent_wrapper' => array(
|
||||
'parent' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Label display for @title', array('@title' => $label)),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => array_combine($regions, $regions),
|
||||
'#empty_value' => '',
|
||||
'#attributes' => array('class' => array('js-field-parent', 'field-parent')),
|
||||
'#parents' => array('fields', $field_name, 'parent'),
|
||||
),
|
||||
'hidden_name' => array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $field_name,
|
||||
'#attributes' => array('class' => array('field-name')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$field_row['plugin'] = array(
|
||||
'type' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Plugin for @title', array('@title' => $label)),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $this->getPluginOptions($field_definition),
|
||||
'#default_value' => $display_options ? $display_options['type'] : 'hidden',
|
||||
'#parents' => array('fields', $field_name, 'type'),
|
||||
'#attributes' => array('class' => array('field-plugin-type')),
|
||||
),
|
||||
'settings_edit_form' => array(),
|
||||
);
|
||||
|
||||
// Get the corresponding plugin object.
|
||||
$plugin = $this->entity->getRenderer($field_name);
|
||||
|
||||
// Base button element for the various plugin settings actions.
|
||||
$base_button = array(
|
||||
'#submit' => array('::multistepSubmit'),
|
||||
'#ajax' => array(
|
||||
'callback' => '::multistepAjax',
|
||||
'wrapper' => 'field-display-overview-wrapper',
|
||||
'effect' => 'fade',
|
||||
),
|
||||
'#field_name' => $field_name,
|
||||
);
|
||||
|
||||
if ($form_state->get('plugin_settings_edit') == $field_name) {
|
||||
// We are currently editing this field's plugin settings. Display the
|
||||
// settings form and submit buttons.
|
||||
$field_row['plugin']['settings_edit_form'] = array();
|
||||
|
||||
if ($plugin) {
|
||||
// Generate the settings form and allow other modules to alter it.
|
||||
$settings_form = $plugin->settingsForm($form, $form_state);
|
||||
$third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state);
|
||||
|
||||
if ($settings_form || $third_party_settings_form) {
|
||||
$field_row['plugin']['#cell_attributes'] = array('colspan' => 3);
|
||||
$field_row['plugin']['settings_edit_form'] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array('class' => array('field-plugin-settings-edit-form')),
|
||||
'#parents' => array('fields', $field_name, 'settings_edit_form'),
|
||||
'label' => array(
|
||||
'#markup' => $this->t('Plugin settings'),
|
||||
),
|
||||
'settings' => $settings_form,
|
||||
'third_party_settings' => $third_party_settings_form,
|
||||
'actions' => array(
|
||||
'#type' => 'actions',
|
||||
'save_settings' => $base_button + array(
|
||||
'#type' => 'submit',
|
||||
'#button_type' => 'primary',
|
||||
'#name' => $field_name . '_plugin_settings_update',
|
||||
'#value' => $this->t('Update'),
|
||||
'#op' => 'update',
|
||||
),
|
||||
'cancel_settings' => $base_button + array(
|
||||
'#type' => 'submit',
|
||||
'#name' => $field_name . '_plugin_settings_cancel',
|
||||
'#value' => $this->t('Cancel'),
|
||||
'#op' => 'cancel',
|
||||
// Do not check errors for the 'Cancel' button, but make sure we
|
||||
// get the value of the 'plugin type' select.
|
||||
'#limit_validation_errors' => array(array('fields', $field_name, 'type')),
|
||||
),
|
||||
),
|
||||
);
|
||||
$field_row['#attributes']['class'][] = 'field-plugin-settings-editing';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$field_row['settings_summary'] = array();
|
||||
$field_row['settings_edit'] = array();
|
||||
|
||||
if ($plugin) {
|
||||
// Display a summary of the current plugin settings, and (if the
|
||||
// summary is not empty) a button to edit them.
|
||||
$summary = $plugin->settingsSummary();
|
||||
|
||||
// Allow other modules to alter the summary.
|
||||
$this->alterSettingsSummary($summary, $plugin, $field_definition);
|
||||
|
||||
if (!empty($summary)) {
|
||||
$field_row['settings_summary'] = array(
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="field-plugin-summary">{{ summary|safe_join("<br />") }}</div>',
|
||||
'#context' => array('summary' => $summary),
|
||||
'#cell_attributes' => array('class' => array('field-plugin-summary-cell')),
|
||||
);
|
||||
}
|
||||
|
||||
// Check selected plugin settings to display edit link or not.
|
||||
$settings_form = $plugin->settingsForm($form, $form_state);
|
||||
$third_party_settings_form = $this->thirdPartySettingsForm($plugin, $field_definition, $form, $form_state);
|
||||
if (!empty($settings_form) || !empty($third_party_settings_form)) {
|
||||
$field_row['settings_edit'] = $base_button + array(
|
||||
'#type' => 'image_button',
|
||||
'#name' => $field_name . '_settings_edit',
|
||||
'#src' => 'core/misc/icons/787878/cog.svg',
|
||||
'#attributes' => array('class' => array('field-plugin-settings-edit'), 'alt' => $this->t('Edit')),
|
||||
'#op' => 'edit',
|
||||
// Do not check errors for the 'Edit' button, but make sure we get
|
||||
// the value of the 'plugin type' select.
|
||||
'#limit_validation_errors' => array(array('fields', $field_name, 'type')),
|
||||
'#prefix' => '<div class="field-plugin-settings-edit-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $field_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the table row structure for a single extra field.
|
||||
*
|
||||
* @param string $field_id
|
||||
* The field ID.
|
||||
* @param array $extra_field
|
||||
* The pseudo-field element.
|
||||
*
|
||||
* @return array
|
||||
* A table row array.
|
||||
*/
|
||||
protected function buildExtraFieldRow($field_id, $extra_field) {
|
||||
$display_options = $this->entity->getComponent($field_id);
|
||||
|
||||
$regions = array_keys($this->getRegions());
|
||||
$extra_field_row = array(
|
||||
'#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
|
||||
'#row_type' => 'extra_field',
|
||||
'#region_callback' => array($this, 'getRowRegion'),
|
||||
'#js_settings' => array('rowHandler' => 'field'),
|
||||
'human_name' => array(
|
||||
'#markup' => $extra_field['label'],
|
||||
),
|
||||
'weight' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Weight for @title', array('@title' => $extra_field['label'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $display_options ? $display_options['weight'] : 0,
|
||||
'#size' => 3,
|
||||
'#attributes' => array('class' => array('field-weight')),
|
||||
),
|
||||
'parent_wrapper' => array(
|
||||
'parent' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Parents for @title', array('@title' => $extra_field['label'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => array_combine($regions, $regions),
|
||||
'#empty_value' => '',
|
||||
'#attributes' => array('class' => array('js-field-parent', 'field-parent')),
|
||||
'#parents' => array('fields', $field_id, 'parent'),
|
||||
),
|
||||
'hidden_name' => array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $field_id,
|
||||
'#attributes' => array('class' => array('field-name')),
|
||||
),
|
||||
),
|
||||
'plugin' => array(
|
||||
'type' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Visibility for @title', array('@title' => $extra_field['label'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $this->getExtraFieldVisibilityOptions(),
|
||||
'#default_value' => $display_options ? 'visible' : 'hidden',
|
||||
'#parents' => array('fields', $field_id, 'type'),
|
||||
'#attributes' => array('class' => array('field-plugin-type')),
|
||||
),
|
||||
),
|
||||
'settings_summary' => array(),
|
||||
'settings_edit' => array(),
|
||||
);
|
||||
|
||||
return $extra_field_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// If the main "Save" button was submitted while a field settings subform
|
||||
// was being edited, update the new incoming settings when rebuilding the
|
||||
// entity, just as if the subform's "Update" button had been submitted.
|
||||
if ($edit_field = $form_state->get('plugin_settings_edit')) {
|
||||
$form_state->set('plugin_settings_update', $edit_field);
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
$form_values = $form_state->getValues();
|
||||
|
||||
// Handle the 'display modes' checkboxes if present.
|
||||
if ($this->entity->getMode() == 'default' && !empty($form_values['display_modes_custom'])) {
|
||||
$display_modes = $this->getDisplayModes();
|
||||
$current_statuses = $this->getDisplayStatuses();
|
||||
|
||||
$statuses = array();
|
||||
foreach ($form_values['display_modes_custom'] as $mode => $value) {
|
||||
if (!empty($value) && empty($current_statuses[$mode])) {
|
||||
// If no display exists for the newly enabled view mode, initialize
|
||||
// it with those from the 'default' view mode, which were used so
|
||||
// far.
|
||||
if (!$this->entityManager->getStorage($this->entity->getEntityTypeId())->load($this->entity->getTargetEntityTypeId() . '.' . $this->entity->getTargetBundle() . '.' . $mode)) {
|
||||
$display = $this->getEntityDisplay($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle(), 'default')->createCopy($mode);
|
||||
$display->save();
|
||||
}
|
||||
|
||||
$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()]));
|
||||
}
|
||||
$statuses[$mode] = !empty($value);
|
||||
}
|
||||
|
||||
$this->saveDisplayStatuses($statuses);
|
||||
}
|
||||
|
||||
drupal_set_message($this->t('Your settings have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
|
||||
$form_values = $form_state->getValues();
|
||||
|
||||
if ($this->entity instanceof EntityWithPluginCollectionInterface) {
|
||||
// Do not manually update values represented by plugin collections.
|
||||
$form_values = array_diff_key($form_values, $this->entity->getPluginCollections());
|
||||
}
|
||||
|
||||
// Collect data for 'regular' fields.
|
||||
foreach ($form['#fields'] as $field_name) {
|
||||
$values = $form_values['fields'][$field_name];
|
||||
|
||||
if ($values['type'] == 'hidden') {
|
||||
$entity->removeComponent($field_name);
|
||||
}
|
||||
else {
|
||||
$options = $entity->getComponent($field_name);
|
||||
|
||||
// Update field settings only if the submit handler told us to.
|
||||
if ($form_state->get('plugin_settings_update') === $field_name) {
|
||||
// Only store settings actually used by the selected plugin.
|
||||
$default_settings = $this->pluginManager->getDefaultSettings($options['type']);
|
||||
$options['settings'] = isset($values['settings_edit_form']['settings']) ? array_intersect_key($values['settings_edit_form']['settings'], $default_settings) : [];
|
||||
$options['third_party_settings'] = isset($values['settings_edit_form']['third_party_settings']) ? $values['settings_edit_form']['third_party_settings'] : [];
|
||||
$form_state->set('plugin_settings_update', NULL);
|
||||
}
|
||||
|
||||
$options['type'] = $values['type'];
|
||||
$options['weight'] = $values['weight'];
|
||||
// Only formatters have configurable label visibility.
|
||||
if (isset($values['label'])) {
|
||||
$options['label'] = $values['label'];
|
||||
}
|
||||
$entity->setComponent($field_name, $options);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect data for 'extra' fields.
|
||||
foreach ($form['#extra'] as $name) {
|
||||
if ($form_values['fields'][$name]['type'] == 'hidden') {
|
||||
$entity->removeComponent($name);
|
||||
}
|
||||
else {
|
||||
$entity->setComponent($name, array(
|
||||
'weight' => $form_values['fields'][$name]['weight'],
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for multistep buttons.
|
||||
*/
|
||||
public function multistepSubmit($form, FormStateInterface $form_state) {
|
||||
$trigger = $form_state->getTriggeringElement();
|
||||
$op = $trigger['#op'];
|
||||
|
||||
switch ($op) {
|
||||
case 'edit':
|
||||
// Store the field whose settings are currently being edited.
|
||||
$field_name = $trigger['#field_name'];
|
||||
$form_state->set('plugin_settings_edit', $field_name);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
// Set the field back to 'non edit' mode, and update $this->entity with
|
||||
// the new settings fro the next rebuild.
|
||||
$field_name = $trigger['#field_name'];
|
||||
$form_state->set('plugin_settings_edit', NULL);
|
||||
$form_state->set('plugin_settings_update', $field_name);
|
||||
$this->entity = $this->buildEntity($form, $form_state);
|
||||
break;
|
||||
|
||||
case 'cancel':
|
||||
// Set the field back to 'non edit' mode.
|
||||
$form_state->set('plugin_settings_edit', NULL);
|
||||
break;
|
||||
|
||||
case 'refresh_table':
|
||||
// If the currently edited field is one of the rows to be refreshed, set
|
||||
// it back to 'non edit' mode.
|
||||
$updated_rows = explode(' ', $form_state->getValue('refresh_rows'));
|
||||
$plugin_settings_edit = $form_state->get('plugin_settings_edit');
|
||||
if ($plugin_settings_edit && in_array($plugin_settings_edit, $updated_rows)) {
|
||||
$form_state->set('plugin_settings_edit', NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax handler for multistep buttons.
|
||||
*/
|
||||
public function multistepAjax($form, FormStateInterface $form_state) {
|
||||
$trigger = $form_state->getTriggeringElement();
|
||||
$op = $trigger['#op'];
|
||||
|
||||
// Pick the elements that need to receive the ajax-new-content effect.
|
||||
switch ($op) {
|
||||
case 'edit':
|
||||
$updated_rows = array($trigger['#field_name']);
|
||||
$updated_columns = array('plugin');
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
case 'cancel':
|
||||
$updated_rows = array($trigger['#field_name']);
|
||||
$updated_columns = array('plugin', 'settings_summary', 'settings_edit');
|
||||
break;
|
||||
|
||||
case 'refresh_table':
|
||||
$updated_rows = array_values(explode(' ', $form_state->getValue('refresh_rows')));
|
||||
$updated_columns = array('settings_summary', 'settings_edit');
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($updated_rows as $name) {
|
||||
foreach ($updated_columns as $key) {
|
||||
$element = &$form['fields'][$name][$key];
|
||||
$element['#prefix'] = '<div class="ajax-new-content">' . (isset($element['#prefix']) ? $element['#prefix'] : '');
|
||||
$element['#suffix'] = (isset($element['#suffix']) ? $element['#suffix'] : '') . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// Return the whole table.
|
||||
return $form['fields'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-render tasks on field_ui_table elements.
|
||||
*
|
||||
* @param array $elements
|
||||
* A structured array containing two sub-levels of elements. Properties
|
||||
* used:
|
||||
* - #tabledrag: The value is a list of $options arrays that are passed to
|
||||
* drupal_attach_tabledrag(). The HTML ID of the table is added to each
|
||||
* $options array.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see drupal_render()
|
||||
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
|
||||
*/
|
||||
public function tablePreRender($elements) {
|
||||
return FieldUiTable::tablePreRender($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the rendering order of an array representing a tree.
|
||||
*
|
||||
* Callback for array_reduce() within
|
||||
* \Drupal\field_ui\Form\EntityDisplayFormBase::tablePreRender().
|
||||
*
|
||||
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
|
||||
*/
|
||||
public function reduceOrder($array, $a) {
|
||||
return FieldUiTable::reduceOrder($array, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra fields of the entity type and bundle used by this form.
|
||||
*
|
||||
* @return array
|
||||
* An array of extra field info.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityManagerInterface::getExtraFields()
|
||||
*/
|
||||
protected function getExtraFields() {
|
||||
$context = $this->displayContext == 'view' ? 'display' : $this->displayContext;
|
||||
$extra_fields = $this->entityManager->getExtraFields($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle());
|
||||
return isset($extra_fields[$context]) ? $extra_fields[$context] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entity display object to be used by this form.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The target entity type ID of the entity display.
|
||||
* @param string $bundle
|
||||
* The target bundle of the entity display.
|
||||
* @param string $mode
|
||||
* A view or form mode.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Display\EntityDisplayInterface
|
||||
* An entity display.
|
||||
*/
|
||||
abstract protected function getEntityDisplay($entity_type_id, $bundle, $mode);
|
||||
|
||||
/**
|
||||
* Returns an array of applicable widget or formatter options for a field.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
*
|
||||
* @return array
|
||||
* An array of applicable widget or formatter options.
|
||||
*/
|
||||
protected function getApplicablePluginOptions(FieldDefinitionInterface $field_definition) {
|
||||
$options = $this->pluginManager->getOptions($field_definition->getType());
|
||||
$applicable_options = array();
|
||||
foreach ($options as $option => $label) {
|
||||
$plugin_class = DefaultFactory::getPluginClass($option, $this->pluginManager->getDefinition($option));
|
||||
if ($plugin_class::isApplicable($field_definition)) {
|
||||
$applicable_options[$option] = $label;
|
||||
}
|
||||
}
|
||||
return $applicable_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of widget or formatter options for a field.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
*
|
||||
* @return array
|
||||
* An array of widget or formatter options.
|
||||
*/
|
||||
protected function getPluginOptions(FieldDefinitionInterface $field_definition) {
|
||||
$applicable_options = $this->getApplicablePluginOptions($field_definition);
|
||||
return $applicable_options + array('hidden' => '- ' . $this->t('Hidden') . ' -');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the default widget or formatter plugin for a field type.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type.
|
||||
*
|
||||
* @return string
|
||||
* The widget or formatter plugin ID.
|
||||
*/
|
||||
abstract protected function getDefaultPlugin($field_type);
|
||||
|
||||
/**
|
||||
* Returns the form or view modes used by this form.
|
||||
*
|
||||
* @return array
|
||||
* An array of form or view mode info.
|
||||
*/
|
||||
abstract protected function getDisplayModes();
|
||||
|
||||
/**
|
||||
* Returns an array of form or view mode options.
|
||||
*
|
||||
* @return array
|
||||
* An array of form or view mode options.
|
||||
*/
|
||||
abstract protected function getDisplayModeOptions();
|
||||
|
||||
/**
|
||||
* Returns a link to the form or view mode admin page.
|
||||
*
|
||||
* @return array
|
||||
* An array of a form element to be rendered as a link.
|
||||
*/
|
||||
abstract protected function getDisplayModesLink();
|
||||
|
||||
/**
|
||||
* Returns the region to which a row in the display overview belongs.
|
||||
*
|
||||
* @param array $row
|
||||
* The row element.
|
||||
*
|
||||
* @return string|null
|
||||
* The region name this row belongs to.
|
||||
*/
|
||||
public function getRowRegion($row) {
|
||||
switch ($row['#row_type']) {
|
||||
case 'field':
|
||||
case 'extra_field':
|
||||
return ($row['plugin']['type']['#value'] == 'hidden' ? 'hidden' : 'content');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of visibility options for extra fields.
|
||||
*
|
||||
* @return array
|
||||
* An array of visibility options.
|
||||
*/
|
||||
protected function getExtraFieldVisibilityOptions() {
|
||||
return array(
|
||||
'visible' => $this->t('Visible'),
|
||||
'hidden' => '- ' . $this->t('Hidden') . ' -',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entity (form) displays for the current entity display type.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Display\EntityDisplayInterface[]
|
||||
* An array holding entity displays or entity form displays.
|
||||
*/
|
||||
protected function getDisplays() {
|
||||
$load_ids = array();
|
||||
$display_entity_type = $this->entity->getEntityTypeId();
|
||||
$entity_type = $this->entityManager->getDefinition($display_entity_type);
|
||||
$config_prefix = $entity_type->getConfigPrefix();
|
||||
$ids = $this->configFactory()->listAll($config_prefix . '.' . $this->entity->getTargetEntityTypeId() . '.' . $this->entity->getTargetBundle() . '.');
|
||||
foreach ($ids as $id) {
|
||||
$config_id = str_replace($config_prefix . '.', '', $id);
|
||||
list(,, $display_mode) = explode('.', $config_id);
|
||||
if ($display_mode != 'default') {
|
||||
$load_ids[] = $config_id;
|
||||
}
|
||||
}
|
||||
return $this->entityManager->getStorage($display_entity_type)->loadMultiple($load_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns form or view modes statuses for the bundle used by this form.
|
||||
*
|
||||
* @return array
|
||||
* An array of form or view mode statuses.
|
||||
*/
|
||||
protected function getDisplayStatuses() {
|
||||
$display_statuses = array();
|
||||
$displays = $this->getDisplays();
|
||||
foreach ($displays as $display) {
|
||||
$display_statuses[$display->get('mode')] = $display->status();
|
||||
}
|
||||
return $display_statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the updated display mode statuses.
|
||||
*
|
||||
* @param array $display_statuses
|
||||
* An array holding updated form or view mode statuses.
|
||||
*/
|
||||
protected function saveDisplayStatuses($display_statuses) {
|
||||
$displays = $this->getDisplays();
|
||||
foreach ($displays as $display) {
|
||||
// Only update the display if the status is changing.
|
||||
$new_status = $display_statuses[$display->get('mode')];
|
||||
if ($new_status !== $display->status()) {
|
||||
$display->set('status', $new_status);
|
||||
$display->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the table headers.
|
||||
*
|
||||
* @return array
|
||||
* The table header.
|
||||
*/
|
||||
abstract protected function getTableHeader();
|
||||
|
||||
/**
|
||||
* Returns the Url object for a specific entity (form) display edit form.
|
||||
*
|
||||
* @param string $mode
|
||||
* The form or view mode.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* A Url object for the overview route.
|
||||
*/
|
||||
abstract protected function getOverviewUrl($mode);
|
||||
|
||||
/**
|
||||
* Adds the widget or formatter third party settings forms.
|
||||
*
|
||||
* @param \Drupal\Core\Field\PluginSettingsInterface $plugin
|
||||
* The widget or formatter.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
* @param array $form
|
||||
* The (entire) configuration form array.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return array
|
||||
* The widget or formatter third party settings form.
|
||||
*/
|
||||
abstract protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state);
|
||||
|
||||
/**
|
||||
* Alters the widget or formatter settings summary.
|
||||
*
|
||||
* @param array $summary
|
||||
* The widget or formatter settings summary.
|
||||
* @param \Drupal\Core\Field\PluginSettingsInterface $plugin
|
||||
* The widget or formatter.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* The field definition.
|
||||
*/
|
||||
abstract protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition);
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides the add form for entity display modes.
|
||||
*/
|
||||
class EntityDisplayModeAddForm extends EntityDisplayModeFormBase {
|
||||
|
||||
/**
|
||||
* The entity type for which the display mode is being created.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetEntityTypeId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
|
||||
$this->targetEntityTypeId = $entity_type_id;
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
// Change replace_pattern to avoid undesired dots.
|
||||
$form['id']['#machine_name']['replace_pattern'] = '[^a-z0-9_]+';
|
||||
$definition = $this->entityManager->getDefinition($this->targetEntityTypeId);
|
||||
$form['#title'] = $this->t('Add new %label @entity-type', array('%label' => $definition->getLabel(), '@entity-type' => $this->entityType->getLowercaseLabel()));
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
$form_state->setValueForElement($form['id'], $this->targetEntityTypeId . '.' . $form_state->getValue('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
$definition = $this->entityManager->getDefinition($this->targetEntityTypeId);
|
||||
if (!$definition->get('field_ui_base_route') || !$definition->hasViewBuilderClass()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->entity->setTargetType($this->targetEntityTypeId);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
|
||||
/**
|
||||
* Provides the delete form for entity display modes.
|
||||
*/
|
||||
class EntityDisplayModeDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
$entity_type = $this->entity->getEntityType();
|
||||
return $this->t('Deleting a @entity-type will cause any output still requesting to use that @entity-type to use the default display settings.', array('@entity-type' => $entity_type->getLowercaseLabel()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
/**
|
||||
* Provides the edit form for entity display modes.
|
||||
*/
|
||||
class EntityDisplayModeEditForm extends EntityDisplayModeFormBase {
|
||||
|
||||
}
|
126
web/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php
Normal file
126
web/core/modules/field_ui/src/Form/EntityDisplayModeFormBase.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides the generic base class for entity display mode forms.
|
||||
*/
|
||||
abstract class EntityDisplayModeFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity query factory.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* The entity type definition.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityDisplayModeFormBase.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* The entity query factory.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(QueryFactory $query_factory, EntityManagerInterface $entity_manager) {
|
||||
$this->queryFactory = $query_factory;
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query'),
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function init(FormStateInterface $form_state) {
|
||||
parent::init($form_state);
|
||||
$this->entityType = $this->entityManager->getDefinition($this->entity->getEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Name'),
|
||||
'#maxlength' => 100,
|
||||
'#default_value' => $this->entity->label(),
|
||||
);
|
||||
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#description' => $this->t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
|
||||
'#disabled' => !$this->entity->isNew(),
|
||||
'#default_value' => $this->entity->id(),
|
||||
'#field_prefix' => $this->entity->isNew() ? $this->entity->getTargetType() . '.' : '',
|
||||
'#machine_name' => array(
|
||||
'exists' => array($this, 'exists'),
|
||||
'replace_pattern' => '[^a-z0-9_.]+',
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the display mode already exists.
|
||||
*
|
||||
* @param string|int $entity_id
|
||||
* The entity ID.
|
||||
* @param array $element
|
||||
* The form element.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the display mode exists, FALSE otherwise.
|
||||
*/
|
||||
public function exists($entity_id, array $element) {
|
||||
// Do not allow to add internal 'default' view mode.
|
||||
if ($entity_id == 'default') {
|
||||
return TRUE;
|
||||
}
|
||||
return (bool) $this->queryFactory
|
||||
->get($this->entity->getEntityTypeId())
|
||||
->condition('id', $element['#field_prefix'] . $entity_id)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
drupal_set_message($this->t('Saved the %label @entity-type.', array('%label' => $this->entity->label(), '@entity-type' => $this->entityType->getLowercaseLabel())));
|
||||
$this->entity->save();
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
|
||||
}
|
||||
|
||||
}
|
142
web/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php
Normal file
142
web/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\PluginSettingsInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Edit form for the EntityFormDisplay entity type.
|
||||
*/
|
||||
class EntityFormDisplayEditForm extends EntityDisplayFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $displayContext = 'form';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.field.field_type'),
|
||||
$container->get('plugin.manager.field.widget')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
|
||||
$field_row = parent::buildFieldRow($field_definition, $form, $form_state);
|
||||
|
||||
$field_name = $field_definition->getName();
|
||||
|
||||
// Update the (invisible) title of the 'plugin' column.
|
||||
$field_row['plugin']['#title'] = $this->t('Formatter for @title', array('@title' => $field_definition->getLabel()));
|
||||
if (!empty($field_row['plugin']['settings_edit_form']) && ($plugin = $this->entity->getRenderer($field_name))) {
|
||||
$plugin_type_info = $plugin->getPluginDefinition();
|
||||
$field_row['plugin']['settings_edit_form']['label']['#markup'] = $this->t('Widget settings:') . ' <span class="plugin-name">' . $plugin_type_info['label'] . '</span>';
|
||||
}
|
||||
|
||||
return $field_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityDisplay($entity_type_id, $bundle, $mode) {
|
||||
return entity_get_form_display($entity_type_id, $bundle, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultPlugin($field_type) {
|
||||
return isset($this->fieldTypes[$field_type]['default_widget']) ? $this->fieldTypes[$field_type]['default_widget'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModes() {
|
||||
return $this->entityManager->getFormModes($this->entity->getTargetEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModeOptions() {
|
||||
return $this->entityManager->getFormModeOptions($this->entity->getTargetEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModesLink() {
|
||||
return [
|
||||
'#type' => 'link',
|
||||
'#title' => t('Manage form modes'),
|
||||
'#url' => Url::fromRoute('entity.entity_form_mode.collection'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTableHeader() {
|
||||
return array(
|
||||
$this->t('Field'),
|
||||
$this->t('Weight'),
|
||||
$this->t('Parent'),
|
||||
array('data' => $this->t('Widget'), 'colspan' => 3),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getOverviewUrl($mode) {
|
||||
$entity_type = $this->entityManager->getDefinition($this->entity->getTargetEntityTypeId());
|
||||
return Url::fromRoute('entity.entity_form_display.' . $this->entity->getTargetEntityTypeId() . '.form_mode', [
|
||||
'form_mode_name' => $mode,
|
||||
] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
|
||||
$settings_form = array();
|
||||
// Invoke hook_field_widget_third_party_settings_form(), keying resulting
|
||||
// subforms by module name.
|
||||
foreach ($this->moduleHandler->getImplementations('field_widget_third_party_settings_form') as $module) {
|
||||
$settings_form[$module] = $this->moduleHandler->invoke($module, 'field_widget_third_party_settings_form', array(
|
||||
$plugin,
|
||||
$field_definition,
|
||||
$this->entity->getMode(),
|
||||
$form,
|
||||
$form_state,
|
||||
));
|
||||
}
|
||||
return $settings_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition) {
|
||||
$context = array(
|
||||
'widget' => $plugin,
|
||||
'field_definition' => $field_definition,
|
||||
'form_mode' => $this->entity->getMode(),
|
||||
);
|
||||
$this->moduleHandler->alter('field_widget_settings_summary', $summary, $context);
|
||||
}
|
||||
|
||||
}
|
24
web/core/modules/field_ui/src/Form/EntityFormModeAddForm.php
Normal file
24
web/core/modules/field_ui/src/Form/EntityFormModeAddForm.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides the add form for entity display modes.
|
||||
*/
|
||||
class EntityFormModeAddForm extends EntityDisplayModeAddForm {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
$definition = $this->entityManager->getDefinition($this->targetEntityTypeId);
|
||||
if (!$definition->get('field_ui_base_route') || !$definition->hasFormClasses()) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->entity->setTargetType($this->targetEntityTypeId);
|
||||
}
|
||||
|
||||
}
|
191
web/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
Normal file
191
web/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\PluginSettingsInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Edit form for the EntityViewDisplay entity type.
|
||||
*/
|
||||
class EntityViewDisplayEditForm extends EntityDisplayFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $displayContext = 'view';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.field.field_type'),
|
||||
$container->get('plugin.manager.field.formatter')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
|
||||
$field_row = parent::buildFieldRow($field_definition, $form, $form_state);
|
||||
|
||||
$field_name = $field_definition->getName();
|
||||
$display_options = $this->entity->getComponent($field_name);
|
||||
|
||||
// Insert the label column.
|
||||
$label = array(
|
||||
'label' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Label display for @title', array('@title' => $field_definition->getLabel())),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $this->getFieldLabelOptions(),
|
||||
'#default_value' => $display_options ? $display_options['label'] : 'above',
|
||||
),
|
||||
);
|
||||
|
||||
$label_position = array_search('plugin', array_keys($field_row));
|
||||
$field_row = array_slice($field_row, 0, $label_position, TRUE) + $label + array_slice($field_row, $label_position, count($field_row) - 1, TRUE);
|
||||
|
||||
// Update the (invisible) title of the 'plugin' column.
|
||||
$field_row['plugin']['#title'] = $this->t('Formatter for @title', array('@title' => $field_definition->getLabel()));
|
||||
if (!empty($field_row['plugin']['settings_edit_form']) && ($plugin = $this->entity->getRenderer($field_name))) {
|
||||
$plugin_type_info = $plugin->getPluginDefinition();
|
||||
$field_row['plugin']['settings_edit_form']['label']['#markup'] = $this->t('Format settings:') . ' <span class="plugin-name">' . $plugin_type_info['label'] . '</span>';
|
||||
}
|
||||
|
||||
return $field_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildExtraFieldRow($field_id, $extra_field) {
|
||||
$extra_field_row = parent::buildExtraFieldRow($field_id, $extra_field);
|
||||
|
||||
// Insert an empty placeholder for the label column.
|
||||
$label = array(
|
||||
'empty_cell' => array(
|
||||
'#markup' => ' '
|
||||
)
|
||||
);
|
||||
$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);
|
||||
|
||||
return $extra_field_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityDisplay($entity_type_id, $bundle, $mode) {
|
||||
return entity_get_display($entity_type_id, $bundle, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultPlugin($field_type) {
|
||||
return isset($this->fieldTypes[$field_type]['default_formatter']) ? $this->fieldTypes[$field_type]['default_formatter'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModes() {
|
||||
return $this->entityManager->getViewModes($this->entity->getTargetEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModeOptions() {
|
||||
return $this->entityManager->getViewModeOptions($this->entity->getTargetEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDisplayModesLink() {;
|
||||
return [
|
||||
'#type' => 'link',
|
||||
'#title' => t('Manage view modes'),
|
||||
'#url' => Url::fromRoute('entity.entity_view_mode.collection'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTableHeader() {
|
||||
return array(
|
||||
$this->t('Field'),
|
||||
$this->t('Weight'),
|
||||
$this->t('Parent'),
|
||||
$this->t('Label'),
|
||||
array('data' => $this->t('Format'), 'colspan' => 3),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getOverviewUrl($mode) {
|
||||
$entity_type = $this->entityManager->getDefinition($this->entity->getTargetEntityTypeId());
|
||||
return Url::fromRoute('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.view_mode', [
|
||||
'view_mode_name' => $mode,
|
||||
] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of visibility options for field labels.
|
||||
*
|
||||
* @return array
|
||||
* An array of visibility options.
|
||||
*/
|
||||
protected function getFieldLabelOptions() {
|
||||
return array(
|
||||
'above' => $this->t('Above'),
|
||||
'inline' => $this->t('Inline'),
|
||||
'hidden' => '- ' . $this->t('Hidden') . ' -',
|
||||
'visually_hidden' => '- ' . $this->t('Visually Hidden') . ' -',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function thirdPartySettingsForm(PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state) {
|
||||
$settings_form = array();
|
||||
// Invoke hook_field_formatter_third_party_settings_form(), keying resulting
|
||||
// subforms by module name.
|
||||
foreach ($this->moduleHandler->getImplementations('field_formatter_third_party_settings_form') as $module) {
|
||||
$settings_form[$module] = $this->moduleHandler->invoke($module, 'field_formatter_third_party_settings_form', array(
|
||||
$plugin,
|
||||
$field_definition,
|
||||
$this->entity->getMode(),
|
||||
$form,
|
||||
$form_state,
|
||||
));
|
||||
}
|
||||
return $settings_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterSettingsSummary(array &$summary, PluginSettingsInterface $plugin, FieldDefinitionInterface $field_definition) {
|
||||
$context = array(
|
||||
'formatter' => $plugin,
|
||||
'field_definition' => $field_definition,
|
||||
'view_mode' => $this->entity->getMode(),
|
||||
);
|
||||
$this->moduleHandler->alter('field_formatter_settings_summary', $summary, $context);
|
||||
}
|
||||
|
||||
}
|
116
web/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php
Normal file
116
web/core/modules/field_ui/src/Form/FieldConfigDeleteForm.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityDeleteForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for removing a field from a bundle.
|
||||
*/
|
||||
class FieldConfigDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldConfigDeleteForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
// If we are adding the field storage as a dependency to delete, then that
|
||||
// will list the field as a dependency. That is confusing, so remove it.
|
||||
// Also remove the entity type and the whole entity deletions details
|
||||
// element if nothing else is in there.
|
||||
if (isset($form['entity_deletes']['field_config']['#items']) && isset($form['entity_deletes']['field_config']['#items'][$this->entity->id()])) {
|
||||
unset($form['entity_deletes']['field_config']['#items'][$this->entity->id()]);
|
||||
if (empty($form['entity_deletes']['field_config']['#items'])) {
|
||||
unset($form['entity_deletes']['field_config']);
|
||||
if (!Element::children($form['entity_deletes'])) {
|
||||
$form['entity_deletes']['#access'] = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getConfigNamesToDelete(ConfigEntityInterface $entity) {
|
||||
/** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
|
||||
$field_storage = $entity->getFieldStorageDefinition();
|
||||
$config_names = [$entity->getConfigDependencyName()];
|
||||
|
||||
// If there is only one bundle left for this field storage, it will be
|
||||
// deleted too, notify the user about dependencies.
|
||||
if (count($field_storage->getBundles()) <= 1) {
|
||||
$config_names[] = $field_storage->getConfigDependencyName();
|
||||
}
|
||||
return $config_names;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$field_storage = $this->entity->getFieldStorageDefinition();
|
||||
$bundles = $this->entityManager->getBundleInfo($this->entity->getTargetEntityTypeId());
|
||||
$bundle_label = $bundles[$this->entity->getTargetBundle()]['label'];
|
||||
|
||||
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.', array('%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.', array('%field' => $this->entity->label(), '%type' => $bundle_label)), 'error');
|
||||
}
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
|
||||
// Fields are purged on cron. However field module prevents disabling modules
|
||||
// when field types they provided are used in a field until it is fully
|
||||
// purged. In the case that a field has minimal or no content, a single call
|
||||
// to field_purge_batch() will remove it from the system. Call this with a
|
||||
// low batch limit to avoid administrators having to wait for cron runs when
|
||||
// removing fields that meet this criteria.
|
||||
field_purge_batch(10);
|
||||
}
|
||||
|
||||
}
|
203
web/core/modules/field_ui/src/Form/FieldConfigEditForm.php
Normal file
203
web/core/modules/field_ui/src/Form/FieldConfigEditForm.php
Normal file
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Field\AllowedTagsXssTrait;
|
||||
use Drupal\Core\Field\FieldFilteredMarkup;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
|
||||
/**
|
||||
* Provides a form for the field settings form.
|
||||
*/
|
||||
class FieldConfigEditForm extends EntityForm {
|
||||
|
||||
use AllowedTagsXssTrait;
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\field\FieldConfigInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$field_storage = $this->entity->getFieldStorageDefinition();
|
||||
$bundles = $this->entityManager->getBundleInfo($this->entity->getTargetEntityTypeId());
|
||||
|
||||
$form_title = $this->t('%field settings for %bundle', array(
|
||||
'%field' => $this->entity->getLabel(),
|
||||
'%bundle' => $bundles[$this->entity->getTargetBundle()]['label'],
|
||||
));
|
||||
$form['#title'] = $form_title;
|
||||
|
||||
if ($field_storage->isLocked()) {
|
||||
$form['locked'] = array(
|
||||
'#markup' => $this->t('The field %field is locked and cannot be edited.', array('%field' => $this->entity->getLabel())),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
// Build the configurable field values.
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#default_value' => $this->entity->getLabel() ?: $field_storage->getName(),
|
||||
'#required' => TRUE,
|
||||
'#weight' => -20,
|
||||
);
|
||||
|
||||
$form['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Help text'),
|
||||
'#default_value' => $this->entity->getDescription(),
|
||||
'#rows' => 5,
|
||||
'#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => FieldFilteredMarkup::displayAllowedTags())) . '<br />' . $this->t('This field supports tokens.'),
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
$form['required'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Required field'),
|
||||
'#default_value' => $this->entity->isRequired(),
|
||||
'#weight' => -5,
|
||||
);
|
||||
|
||||
// Create an arbitrary entity object (used by the 'default value' widget).
|
||||
$ids = (object) array(
|
||||
'entity_type' => $this->entity->getTargetEntityTypeId(),
|
||||
'bundle' => $this->entity->getTargetBundle(),
|
||||
'entity_id' => NULL
|
||||
);
|
||||
$form['#entity'] = _field_create_entity_from_ids($ids);
|
||||
$items = $form['#entity']->get($this->entity->getName());
|
||||
$item = $items->first() ?: $items->appendItem();
|
||||
|
||||
// Add field settings for the field type and a container for third party
|
||||
// settings that modules can add to via hook_form_FORM_ID_alter().
|
||||
$form['settings'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#weight' => 10,
|
||||
);
|
||||
$form['settings'] += $item->fieldSettingsForm($form, $form_state);
|
||||
$form['third_party_settings'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#weight' => 11,
|
||||
);
|
||||
|
||||
// Add handling for default value.
|
||||
if ($element = $items->defaultValuesForm($form, $form_state)) {
|
||||
$element = array_merge($element, array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Default value'),
|
||||
'#open' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
'#description' => $this->t('The default value for this field, used when creating new content.'),
|
||||
));
|
||||
|
||||
$form['default_value'] = $element;
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save settings');
|
||||
|
||||
if (!$this->entity->isNew()) {
|
||||
$target_entity_type = $this->entityManager->getDefinition($this->entity->getTargetEntityTypeId());
|
||||
$route_parameters = [
|
||||
'field_config' => $this->entity->id(),
|
||||
] + FieldUI::getRouteBundleParameter($target_entity_type, $this->entity->getTargetBundle());
|
||||
$url = new Url('entity.field_config.' . $target_entity_type->id() . '_field_delete_form', $route_parameters);
|
||||
|
||||
if ($this->getRequest()->query->has('destination')) {
|
||||
$query = $url->getOption('query');
|
||||
$query['destination'] = $this->getRequest()->query->get('destination');
|
||||
$url->setOption('query', $query);
|
||||
}
|
||||
$actions['delete'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Delete'),
|
||||
'#url' => $url,
|
||||
'#access' => $this->entity->access('delete'),
|
||||
'#attributes' => array(
|
||||
'class' => array('button', 'button--danger'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
if (isset($form['default_value'])) {
|
||||
$item = $form['#entity']->get($this->entity->getName());
|
||||
$item->defaultValuesFormValidate($form['default_value'], $form, $form_state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
// Handle the default value.
|
||||
$default_value = array();
|
||||
if (isset($form['default_value'])) {
|
||||
$items = $form['#entity']->get($this->entity->getName());
|
||||
$default_value = $items->defaultValuesFormSubmit($form['default_value'], $form, $form_state);
|
||||
}
|
||||
$this->entity->setDefaultValue($default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$this->entity->save();
|
||||
|
||||
drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->entity->getLabel())));
|
||||
|
||||
$request = $this->getRequest();
|
||||
if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
|
||||
$request->query->remove('destinations');
|
||||
$form_state->setRedirectUrl($next_destination);
|
||||
}
|
||||
else {
|
||||
$form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The _title_callback for the field settings form.
|
||||
*
|
||||
* @param \Drupal\field\FieldConfigInterface $field_config
|
||||
* The field.
|
||||
*
|
||||
* @return string
|
||||
* The label of the field.
|
||||
*/
|
||||
public function getTitle(FieldConfigInterface $field_config) {
|
||||
return $field_config->label();
|
||||
}
|
||||
|
||||
}
|
555
web/core/modules/field_ui/src/Form/FieldStorageAddForm.php
Normal file
555
web/core/modules/field_ui/src/Form/FieldStorageAddForm.php
Normal file
|
@ -0,0 +1,555 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for the "field storage" add page.
|
||||
*/
|
||||
class FieldStorageAddForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The name of the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* The entity bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypePluginManager;
|
||||
|
||||
/**
|
||||
* The query factory to create entity queries.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
public $queryFactory;
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldStorageAddForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_plugin_manager
|
||||
* The field type plugin manager.
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* The entity query factory.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, QueryFactory $query_factory, ConfigFactoryInterface $config_factory) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->fieldTypePluginManager = $field_type_plugin_manager;
|
||||
$this->queryFactory = $query_factory;
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'field_ui_field_storage_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.field.field_type'),
|
||||
$container->get('entity.query'),
|
||||
$container->get('config.factory')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL) {
|
||||
if (!$form_state->get('entity_type_id')) {
|
||||
$form_state->set('entity_type_id', $entity_type_id);
|
||||
}
|
||||
if (!$form_state->get('bundle')) {
|
||||
$form_state->set('bundle', $bundle);
|
||||
}
|
||||
|
||||
$this->entityTypeId = $form_state->get('entity_type_id');
|
||||
$this->bundle = $form_state->get('bundle');
|
||||
|
||||
// Gather valid field types.
|
||||
$field_type_options = array();
|
||||
foreach ($this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions()) as $category => $field_types) {
|
||||
foreach ($field_types as $name => $field_type) {
|
||||
$field_type_options[$category][$name] = $field_type['label'];
|
||||
}
|
||||
}
|
||||
|
||||
$form['add'] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array('class' => array('form--inline', 'clearfix')),
|
||||
);
|
||||
|
||||
$form['add']['new_storage_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Add a new field'),
|
||||
'#options' => $field_type_options,
|
||||
'#empty_option' => $this->t('- Select a field type -'),
|
||||
);
|
||||
|
||||
// Re-use existing field.
|
||||
if ($existing_field_storage_options = $this->getExistingFieldStorageOptions()) {
|
||||
$form['add']['separator'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => $this->t('or'),
|
||||
);
|
||||
$form['add']['existing_storage_name'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Re-use an existing field'),
|
||||
'#options' => $existing_field_storage_options,
|
||||
'#empty_option' => $this->t('- Select an existing field -'),
|
||||
);
|
||||
|
||||
$form['#attached']['drupalSettings']['existingFieldLabels'] = $this->getExistingFieldLabels(array_keys($existing_field_storage_options));
|
||||
}
|
||||
else {
|
||||
// Provide a placeholder form element to simplify the validation code.
|
||||
$form['add']['existing_storage_name'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
// Field label and field_name.
|
||||
$form['new_storage_wrapper'] = array(
|
||||
'#type' => 'container',
|
||||
'#states' => array(
|
||||
'!visible' => array(
|
||||
':input[name="new_storage_type"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['new_storage_wrapper']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#size' => 15,
|
||||
);
|
||||
|
||||
$field_prefix = $this->config('field_ui.settings')->get('field_prefix');
|
||||
$form['new_storage_wrapper']['field_name'] = array(
|
||||
'#type' => 'machine_name',
|
||||
// This field should stay LTR even for RTL languages.
|
||||
'#field_prefix' => '<span dir="ltr">' . $field_prefix,
|
||||
'#field_suffix' => '</span>‎',
|
||||
'#size' => 15,
|
||||
'#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'),
|
||||
// Calculate characters depending on the length of the field prefix
|
||||
// setting. Maximum length is 32.
|
||||
'#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix),
|
||||
'#machine_name' => array(
|
||||
'source' => array('new_storage_wrapper', 'label'),
|
||||
'exists' => array($this, 'fieldNameExists'),
|
||||
),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
// Provide a separate label element for the "Re-use existing field" case
|
||||
// and place it outside the $form['add'] wrapper because those elements
|
||||
// are displayed inline.
|
||||
if ($existing_field_storage_options) {
|
||||
$form['existing_storage_label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#size' => 15,
|
||||
'#states' => array(
|
||||
'!visible' => array(
|
||||
':input[name="existing_storage_name"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Place the 'translatable' property as an explicit value so that contrib
|
||||
// modules can form_alter() the value for newly created fields. By default
|
||||
// we create field storage as translatable so it will be possible to enable
|
||||
// translation at field level.
|
||||
$form['translatable'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => TRUE,
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save and continue'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
|
||||
$form['#attached']['library'][] = 'field_ui/drupal.field_ui';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
// Missing field type.
|
||||
if (!$form_state->getValue('new_storage_type') && !$form_state->getValue('existing_storage_name')) {
|
||||
$form_state->setErrorByName('new_storage_type', $this->t('You need to select a field type or an existing field.'));
|
||||
}
|
||||
// Both field type and existing field option selected. This is prevented in
|
||||
// the UI with JavaScript but we also need a proper server-side validation.
|
||||
elseif ($form_state->getValue('new_storage_type') && $form_state->getValue('existing_storage_name')) {
|
||||
$form_state->setErrorByName('new_storage_type', $this->t('Adding a new field and re-using an existing field at the same time is not allowed.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validateAddNew($form, $form_state);
|
||||
$this->validateAddExisting($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'add new field' case.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @see \Drupal\field_ui\Form\FieldStorageAddForm::validateForm()
|
||||
*/
|
||||
protected function validateAddNew(array $form, FormStateInterface $form_state) {
|
||||
// Validate if any information was provided in the 'add new field' case.
|
||||
if ($form_state->getValue('new_storage_type')) {
|
||||
// Missing label.
|
||||
if (!$form_state->getValue('label')) {
|
||||
$form_state->setErrorByName('label', $this->t('Add new field: you need to provide a label.'));
|
||||
}
|
||||
|
||||
// Missing field name.
|
||||
if (!$form_state->getValue('field_name')) {
|
||||
$form_state->setErrorByName('field_name', $this->t('Add new field: you need to provide a machine name for the field.'));
|
||||
}
|
||||
// Field name validation.
|
||||
else {
|
||||
$field_name = $form_state->getValue('field_name');
|
||||
|
||||
// Add the field prefix.
|
||||
$field_name = $this->configFactory->get('field_ui.settings')->get('field_prefix') . $field_name;
|
||||
$form_state->setValueForElement($form['new_storage_wrapper']['field_name'], $field_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 're-use existing field' case.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @see \Drupal\field_ui\Form\FieldStorageAddForm::validateForm()
|
||||
*/
|
||||
protected function validateAddExisting(array $form, FormStateInterface $form_state) {
|
||||
if ($form_state->getValue('existing_storage_name')) {
|
||||
// Missing label.
|
||||
if (!$form_state->getValue('existing_storage_label')) {
|
||||
$form_state->setErrorByName('existing_storage_label', $this->t('Re-use existing field: you need to provide a label.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$error = FALSE;
|
||||
$values = $form_state->getValues();
|
||||
$destinations = array();
|
||||
$entity_type = $this->entityManager->getDefinition($this->entityTypeId);
|
||||
|
||||
// Create new field.
|
||||
if ($values['new_storage_type']) {
|
||||
$field_storage_values = [
|
||||
'field_name' => $values['field_name'],
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => $values['new_storage_type'],
|
||||
'translatable' => $values['translatable'],
|
||||
];
|
||||
$field_values = [
|
||||
'field_name' => $values['field_name'],
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $values['label'],
|
||||
// Field translatability should be explicitly enabled by the users.
|
||||
'translatable' => FALSE,
|
||||
];
|
||||
$widget_id = $formatter_id = NULL;
|
||||
|
||||
// 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];
|
||||
|
||||
// Merge in preconfigured field storage options.
|
||||
if (isset($field_options['field_storage_config'])) {
|
||||
foreach (array('cardinality', 'settings') as $key) {
|
||||
if (isset($field_options['field_storage_config'][$key])) {
|
||||
$field_storage_values[$key] = $field_options['field_storage_config'][$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge in preconfigured field options.
|
||||
if (isset($field_options['field_config'])) {
|
||||
foreach (array('required', 'settings') as $key) {
|
||||
if (isset($field_options['field_config'][$key])) {
|
||||
$field_values[$key] = $field_options['field_config'][$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$widget_id = isset($field_options['entity_form_display']['type']) ? $field_options['entity_form_display']['type'] : NULL;
|
||||
$formatter_id = isset($field_options['entity_view_display']['type']) ? $field_options['entity_view_display']['type'] : NULL;
|
||||
}
|
||||
|
||||
// Create the field storage and field.
|
||||
try {
|
||||
$this->entityManager->getStorage('field_storage_config')->create($field_storage_values)->save();
|
||||
$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);
|
||||
|
||||
// Always show the field settings step, as the cardinality needs to be
|
||||
// configured for new fields.
|
||||
$route_parameters = array(
|
||||
'field_config' => $field->id(),
|
||||
) + FieldUI::getRouteBundleParameter($entity_type, $this->bundle);
|
||||
$destinations[] = array('route_name' => "entity.field_config.{$this->entityTypeId}_storage_edit_form", 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => "entity.field_config.{$this->entityTypeId}_field_edit_form", 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => "entity.{$this->entityTypeId}.field_ui_fields", 'route_parameters' => $route_parameters);
|
||||
|
||||
// Store new field information for any additional submit handlers.
|
||||
$form_state->set(['fields_added', '_add_new_field'], $values['field_name']);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$error = TRUE;
|
||||
drupal_set_message($this->t('There was a problem creating field %label: @message', array('%label' => $values['label'], '@message' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Re-use existing field.
|
||||
if ($values['existing_storage_name']) {
|
||||
$field_name = $values['existing_storage_name'];
|
||||
|
||||
try {
|
||||
$field = $this->entityManager->getStorage('field_config')->create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $values['existing_storage_label'],
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$this->configureEntityFormDisplay($field_name);
|
||||
$this->configureEntityViewDisplay($field_name);
|
||||
|
||||
$route_parameters = array(
|
||||
'field_config' => $field->id(),
|
||||
) + FieldUI::getRouteBundleParameter($entity_type, $this->bundle);
|
||||
$destinations[] = array('route_name' => "entity.field_config.{$this->entityTypeId}_field_edit_form", 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => "entity.{$this->entityTypeId}.field_ui_fields", 'route_parameters' => $route_parameters);
|
||||
|
||||
// Store new field information for any additional submit handlers.
|
||||
$form_state->set(['fields_added', '_add_existing_field'], $field_name);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$error = TRUE;
|
||||
drupal_set_message($this->t('There was a problem creating field %label: @message', array('%label' => $values['label'], '@message' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if ($destinations) {
|
||||
$destination = $this->getDestinationArray();
|
||||
$destinations[] = $destination['destination'];
|
||||
$form_state->setRedirectUrl(FieldUI::getNextDestination($destinations, $form_state));
|
||||
}
|
||||
elseif (!$error) {
|
||||
drupal_set_message($this->t('Your settings have been saved.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the newly created field for the default view and form modes.
|
||||
*
|
||||
* @param string $field_name
|
||||
* The field name.
|
||||
* @param string|null $widget_id
|
||||
* (optional) The plugin ID of the widget. Defaults to NULL.
|
||||
*/
|
||||
protected function configureEntityFormDisplay($field_name, $widget_id = NULL) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the newly created field for the default view and form modes.
|
||||
*
|
||||
* @param string $field_name
|
||||
* The field name.
|
||||
* @param string|null $formatter_id
|
||||
* (optional) The plugin ID of the formatter. Defaults to NULL.
|
||||
*/
|
||||
protected function configureEntityViewDisplay($field_name, $formatter_id = NULL) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of existing field storages that can be added to a bundle.
|
||||
*
|
||||
* @return array
|
||||
* An array of existing field storages keyed by name.
|
||||
*/
|
||||
protected function getExistingFieldStorageOptions() {
|
||||
$options = array();
|
||||
// Load the field_storages and build the list of options.
|
||||
$field_types = $this->fieldTypePluginManager->getDefinitions();
|
||||
foreach ($this->entityManager->getFieldStorageDefinitions($this->entityTypeId) as $field_name => $field_storage) {
|
||||
// Do not show:
|
||||
// - non-configurable field storages,
|
||||
// - locked field storages,
|
||||
// - field storages that should not be added via user interface,
|
||||
// - field storages that already have a field in the bundle.
|
||||
$field_type = $field_storage->getType();
|
||||
if ($field_storage instanceof FieldStorageConfigInterface
|
||||
&& !$field_storage->isLocked()
|
||||
&& empty($field_types[$field_type]['no_ui'])
|
||||
&& !in_array($this->bundle, $field_storage->getBundles(), TRUE)) {
|
||||
$options[$field_name] = $this->t('@type: @field', array(
|
||||
'@type' => $field_types[$field_type]['label'],
|
||||
'@field' => $field_name,
|
||||
));
|
||||
}
|
||||
}
|
||||
asort($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the human-readable labels for the given field storage names.
|
||||
*
|
||||
* Since not all field storages are required to have a field, we can only
|
||||
* provide the field labels on a best-effort basis (e.g. the label of a field
|
||||
* storage without any field attached to a bundle will be the field name).
|
||||
*
|
||||
* @param array $field_names
|
||||
* An array of field names.
|
||||
*
|
||||
* @return array
|
||||
* An array of field labels keyed by field name.
|
||||
*/
|
||||
protected function getExistingFieldLabels(array $field_names) {
|
||||
// Get all the fields corresponding to the given field storage names and
|
||||
// this entity type.
|
||||
$field_ids = $this->queryFactory->get('field_config')
|
||||
->condition('entity_type', $this->entityTypeId)
|
||||
->condition('field_name', $field_names)
|
||||
->execute();
|
||||
$fields = $this->entityManager->getStorage('field_config')->loadMultiple($field_ids);
|
||||
|
||||
// Go through all the fields and use the label of the first encounter.
|
||||
$labels = array();
|
||||
foreach ($fields as $field) {
|
||||
if (!isset($labels[$field->getName()])) {
|
||||
$labels[$field->getName()] = $field->label();
|
||||
}
|
||||
}
|
||||
|
||||
// For field storages without any fields attached to a bundle, the default
|
||||
// label is the field name.
|
||||
$labels += array_combine($field_names, $field_names);
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field machine name is taken.
|
||||
*
|
||||
* @param string $value
|
||||
* The machine name, not prefixed.
|
||||
* @param array $element
|
||||
* An array containing the structure of the 'field_name' element.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return bool
|
||||
* Whether or not the field machine name is taken.
|
||||
*/
|
||||
public function fieldNameExists($value, $element, FormStateInterface $form_state) {
|
||||
// Don't validate the case when an existing field has been selected.
|
||||
if ($form_state->getValue('existing_storage_name')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Add the field prefix.
|
||||
$field_name = $this->configFactory->get('field_ui.settings')->get('field_prefix') . $value;
|
||||
|
||||
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
|
||||
return isset($field_storage_definitions[$field_name]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides a form for the "field storage" edit page.
|
||||
*/
|
||||
class FieldStorageConfigEditForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\field\FieldStorageConfigInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
|
||||
// The URL of this entity form contains only the ID of the field_config
|
||||
// but we are actually editing a field_storage_config entity.
|
||||
$field_config = FieldConfig::load($route_match->getRawParameter('field_config'));
|
||||
if (!$field_config) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $field_config->getFieldStorageDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $field_config
|
||||
* The ID of the field config whose field storage config is being edited.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $field_config = NULL) {
|
||||
if ($field_config) {
|
||||
$field = FieldConfig::load($field_config);
|
||||
$form_state->set('field_config', $field);
|
||||
|
||||
$form_state->set('entity_type_id', $field->getTargetEntityTypeId());
|
||||
$form_state->set('bundle', $field->getTargetBundle());
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$field_label = $form_state->get('field_config')->label();
|
||||
$form['#title'] = $field_label;
|
||||
$form['#prefix'] = '<p>' . $this->t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array('%field' => $field_label)) . '</p>';
|
||||
|
||||
// See if data already exists for this field.
|
||||
// If so, prevent changes to the field settings.
|
||||
if ($this->entity->hasData()) {
|
||||
$form['#prefix'] = '<div class="messages messages--error">' . $this->t('There is data for this field in the database. The field settings can no longer be changed.') . '</div>' . $form['#prefix'];
|
||||
}
|
||||
|
||||
// Add settings provided by the field module. The field module is
|
||||
// responsible for not returning settings that cannot be changed if
|
||||
// the field already has data.
|
||||
$form['settings'] = array(
|
||||
'#weight' => -10,
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
// Create an arbitrary entity object, so that we can have an instantiated
|
||||
// FieldItem.
|
||||
$ids = (object) array(
|
||||
'entity_type' => $form_state->get('entity_type_id'),
|
||||
'bundle' => $form_state->get('bundle'),
|
||||
'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'] = array(
|
||||
// Reset #parents so the additional container does not appear.
|
||||
'#parents' => array(),
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Allowed number of values'),
|
||||
'#attributes' => array('class' => array(
|
||||
'container-inline',
|
||||
'fieldgroup',
|
||||
'form-composite'
|
||||
)),
|
||||
);
|
||||
$form['cardinality_container']['cardinality'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Allowed number of values'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => array(
|
||||
'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'] = array(
|
||||
'#type' => 'number',
|
||||
'#default_value' => $cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED ? $cardinality : 1,
|
||||
'#min' => 1,
|
||||
'#title' => $this->t('Limit'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 2,
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="cardinality"]' => array('value' => 'number'),
|
||||
),
|
||||
'disabled' => array(
|
||||
':input[name="cardinality"]' => array('value' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$elements = parent::actions($form, $form_state);
|
||||
$elements['submit']['#value'] = $this->t('Save field settings');
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $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.'));
|
||||
}
|
||||
// If a specific cardinality is used, validate that there are no entities
|
||||
// with a higher delta.
|
||||
elseif (!$this->entity->isNew() && isset($field_storage_definitions[$this->entity->getName()]) && $form_state->getValue('cardinality') != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
|
||||
|
||||
// Get a count of entities that have a value in a delta higher than the
|
||||
// one selected. Deltas start with 0, so the selected value does not
|
||||
// need to be incremented.
|
||||
$entities_with_higher_delta = \Drupal::entityQuery($this->entity->getTargetEntityTypeId())
|
||||
->condition($this->entity->getName() . '.%delta', $form_state->getValue('cardinality'))
|
||||
->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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
// Save field cardinality.
|
||||
if ($form_state->getValue('cardinality') === 'number' && $form_state->getValue('cardinality_number')) {
|
||||
$form_state->setValue('cardinality', $form_state->getValue('cardinality_number'));
|
||||
}
|
||||
|
||||
return parent::buildEntity($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$field_label = $form_state->get('field_config')->label();
|
||||
try {
|
||||
$this->entity->save();
|
||||
drupal_set_message($this->t('Updated field %label field settings.', array('%label' => $field_label)));
|
||||
$request = $this->getRequest();
|
||||
if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
|
||||
$request->query->remove('destinations');
|
||||
$form_state->setRedirectUrl($next_destination);
|
||||
}
|
||||
else {
|
||||
$form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($form_state->get('entity_type_id'), $form_state->get('bundle')));
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($this->t('Attempt to update field %label failed: %message.', array('%label' => $field_label, '%message' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides local action definitions for all entity bundles.
|
||||
*/
|
||||
class FieldUiLocalAction extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity manager
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldUiLocalAction object.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider to load routes by name.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $route_provider, EntityManagerInterface $entity_manager) {
|
||||
$this->routeProvider = $route_provider;
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$this->derivatives = array();
|
||||
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->get('field_ui_base_route')) {
|
||||
$this->derivatives["field_storage_config_add_$entity_type_id"] = array(
|
||||
'route_name' => "field_ui.field_storage_config_add_$entity_type_id",
|
||||
'title' => $this->t('Add field'),
|
||||
'appears_on' => array("entity.$entity_type_id.field_ui_fields"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->derivatives as &$entry) {
|
||||
$entry += $base_plugin_definition;
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Plugin\Derivative;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides local task definitions for all entity bundles.
|
||||
*/
|
||||
class FieldUiLocalTask extends DeriverBase implements ContainerDeriverInterface {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* The entity manager
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Creates an FieldUiLocalTask object.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The translation manager.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $route_provider, EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
|
||||
$this->routeProvider = $route_provider;
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$this->derivatives = array();
|
||||
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->get('field_ui_base_route')) {
|
||||
$this->derivatives["overview_$entity_type_id"] = array(
|
||||
'route_name' => "entity.$entity_type_id.field_ui_fields",
|
||||
'weight' => 1,
|
||||
'title' => $this->t('Manage fields'),
|
||||
'base_route' => "entity.$entity_type_id.field_ui_fields",
|
||||
);
|
||||
|
||||
// 'Manage form display' tab.
|
||||
$this->derivatives["form_display_overview_$entity_type_id"] = array(
|
||||
'route_name' => "entity.entity_form_display.$entity_type_id.default",
|
||||
'weight' => 2,
|
||||
'title' => $this->t('Manage form display'),
|
||||
'base_route' => "entity.$entity_type_id.field_ui_fields",
|
||||
);
|
||||
|
||||
// 'Manage display' tab.
|
||||
$this->derivatives["display_overview_$entity_type_id"] = array(
|
||||
'route_name' => "entity.entity_view_display.$entity_type_id.default",
|
||||
'weight' => 3,
|
||||
'title' => $this->t('Manage display'),
|
||||
'base_route' => "entity.$entity_type_id.field_ui_fields",
|
||||
);
|
||||
|
||||
// Field edit tab.
|
||||
$this->derivatives["field_edit_$entity_type_id"] = array(
|
||||
'route_name' => "entity.field_config.{$entity_type_id}_field_edit_form",
|
||||
'title' => $this->t('Edit'),
|
||||
'base_route' => "entity.field_config.{$entity_type_id}_field_edit_form",
|
||||
);
|
||||
|
||||
// Field settings tab.
|
||||
$this->derivatives["field_storage_$entity_type_id"] = array(
|
||||
'route_name' => "entity.field_config.{$entity_type_id}_storage_edit_form",
|
||||
'title' => $this->t('Field settings'),
|
||||
'base_route' => "entity.field_config.{$entity_type_id}_field_edit_form",
|
||||
);
|
||||
|
||||
// View and form modes secondary tabs.
|
||||
// The same base $path for the menu item (with a placeholder) can be
|
||||
// used for all bundles of a given entity type; but depending on
|
||||
// administrator settings, each bundle has a different set of view
|
||||
// modes available for customisation. So we define menu items for all
|
||||
// view modes, and use a route requirement to determine which ones are
|
||||
// actually visible for a given bundle.
|
||||
$this->derivatives['field_form_display_default_' . $entity_type_id] = array(
|
||||
'title' => 'Default',
|
||||
'route_name' => "entity.entity_form_display.$entity_type_id.default",
|
||||
'parent_id' => "field_ui.fields:form_display_overview_$entity_type_id",
|
||||
'weight' => -1,
|
||||
);
|
||||
$this->derivatives['field_display_default_' . $entity_type_id] = array(
|
||||
'title' => 'Default',
|
||||
'route_name' => "entity.entity_view_display.$entity_type_id.default",
|
||||
'parent_id' => "field_ui.fields:display_overview_$entity_type_id",
|
||||
'weight' => -1,
|
||||
);
|
||||
|
||||
// One local task for each form mode.
|
||||
$weight = 0;
|
||||
foreach ($this->entityManager->getFormModes($entity_type_id) as $form_mode => $form_mode_info) {
|
||||
$this->derivatives['field_form_display_' . $form_mode . '_' . $entity_type_id] = array(
|
||||
'title' => $form_mode_info['label'],
|
||||
'route_name' => "entity.entity_form_display.$entity_type_id.form_mode",
|
||||
'route_parameters' => array(
|
||||
'form_mode_name' => $form_mode,
|
||||
),
|
||||
'parent_id' => "field_ui.fields:form_display_overview_$entity_type_id",
|
||||
'weight' => $weight++,
|
||||
'cache_tags' => $this->entityManager->getDefinition('entity_form_display')->getListCacheTags(),
|
||||
);
|
||||
}
|
||||
|
||||
// One local task for each view mode.
|
||||
$weight = 0;
|
||||
foreach ($this->entityManager->getViewModes($entity_type_id) as $view_mode => $form_mode_info) {
|
||||
$this->derivatives['field_display_' . $view_mode . '_' . $entity_type_id] = array(
|
||||
'title' => $form_mode_info['label'],
|
||||
'route_name' => "entity.entity_view_display.$entity_type_id.view_mode",
|
||||
'route_parameters' => array(
|
||||
'view_mode_name' => $view_mode,
|
||||
),
|
||||
'parent_id' => "field_ui.fields:display_overview_$entity_type_id",
|
||||
'weight' => $weight++,
|
||||
'cache_tags' => $this->entityManager->getDefinition('entity_view_display')->getListCacheTags(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->derivatives as &$entry) {
|
||||
$entry += $base_plugin_definition;
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the base_route definition for field_ui local tasks.
|
||||
*
|
||||
* @param array $local_tasks
|
||||
* An array of local tasks plugin definitions, keyed by plugin ID.
|
||||
*/
|
||||
public function alterLocalTasks(&$local_tasks) {
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($route_name = $entity_type->get('field_ui_base_route')) {
|
||||
$local_tasks["field_ui.fields:overview_$entity_type_id"]['base_route'] = $route_name;
|
||||
$local_tasks["field_ui.fields:form_display_overview_$entity_type_id"]['base_route'] = $route_name;
|
||||
$local_tasks["field_ui.fields:display_overview_$entity_type_id"]['base_route'] = $route_name;
|
||||
$local_tasks["field_ui.fields:field_form_display_default_$entity_type_id"]['base_route'] = $route_name;
|
||||
$local_tasks["field_ui.fields:field_display_default_$entity_type_id"]['base_route'] = $route_name;
|
||||
|
||||
foreach ($this->entityManager->getFormModes($entity_type_id) as $form_mode => $form_mode_info) {
|
||||
$local_tasks['field_ui.fields:field_form_display_' . $form_mode . '_' . $entity_type_id]['base_route'] = $route_name;
|
||||
}
|
||||
|
||||
foreach ($this->entityManager->getViewModes($entity_type_id) as $view_mode => $form_mode_info) {
|
||||
$local_tasks['field_ui.fields:field_display_' . $view_mode . '_' . $entity_type_id]['base_route'] = $route_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldUiRouteEnhancer object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enhance(array $defaults, Request $request) {
|
||||
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
|
||||
// to get it.
|
||||
$defaults['bundle'] = $defaults['_raw_variables']->get($bundle);
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(Route $route) {
|
||||
return ($route->hasOption('_field_ui'));
|
||||
}
|
||||
|
||||
}
|
170
web/core/modules/field_ui/src/Routing/RouteSubscriber.php
Normal file
170
web/core/modules/field_ui/src/Routing/RouteSubscriber.php
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Subscriber for Field UI routes.
|
||||
*/
|
||||
class RouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* The entity type manager
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a RouteSubscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
foreach ($this->manager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($route_name = $entity_type->get('field_ui_base_route')) {
|
||||
// Try to get the route from the current collection.
|
||||
if (!$entity_route = $collection->get($route_name)) {
|
||||
continue;
|
||||
}
|
||||
$path = $entity_route->getPath();
|
||||
|
||||
$options = $entity_route->getOptions();
|
||||
if ($bundle_entity_type = $entity_type->getBundleEntityType()) {
|
||||
$options['parameters'][$bundle_entity_type] = array(
|
||||
'type' => 'entity:' . $bundle_entity_type,
|
||||
);
|
||||
}
|
||||
// Special parameter used to easily recognize all Field UI routes.
|
||||
$options['_field_ui'] = TRUE;
|
||||
|
||||
$defaults = array(
|
||||
'entity_type_id' => $entity_type_id,
|
||||
);
|
||||
// If the entity type has no bundles and it doesn't use {bundle} in its
|
||||
// admin path, use the entity type.
|
||||
if (strpos($path, '{bundle}') === FALSE) {
|
||||
$defaults['bundle'] = !$entity_type->hasKey('bundle') ? $entity_type_id : '';
|
||||
}
|
||||
|
||||
$route = new Route(
|
||||
"$path/fields/{field_config}",
|
||||
array(
|
||||
'_entity_form' => 'field_config.edit',
|
||||
'_title_callback' => '\Drupal\field_ui\Form\FieldConfigEditForm::getTitle',
|
||||
) + $defaults,
|
||||
array('_entity_access' => 'field_config.update'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.field_config.{$entity_type_id}_field_edit_form", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/fields/{field_config}/storage",
|
||||
array('_entity_form' => 'field_storage_config.edit') + $defaults,
|
||||
array('_permission' => 'administer ' . $entity_type_id . ' fields'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.field_config.{$entity_type_id}_storage_edit_form", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/fields/{field_config}/delete",
|
||||
array('_entity_form' => 'field_config.delete') + $defaults,
|
||||
array('_entity_access' => 'field_config.delete'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.field_config.{$entity_type_id}_field_delete_form", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/fields",
|
||||
array(
|
||||
'_controller' => '\Drupal\field_ui\Controller\FieldConfigListController::listing',
|
||||
'_title' => 'Manage fields',
|
||||
) + $defaults,
|
||||
array('_permission' => 'administer ' . $entity_type_id . ' fields'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.{$entity_type_id}.field_ui_fields", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/fields/add-field",
|
||||
array(
|
||||
'_form' => '\Drupal\field_ui\Form\FieldStorageAddForm',
|
||||
'_title' => 'Add field',
|
||||
) + $defaults,
|
||||
array('_permission' => 'administer ' . $entity_type_id . ' fields'),
|
||||
$options
|
||||
);
|
||||
$collection->add("field_ui.field_storage_config_add_$entity_type_id", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/form-display",
|
||||
array(
|
||||
'_entity_form' => 'entity_form_display.edit',
|
||||
'_title' => 'Manage form display',
|
||||
'form_mode_name' => 'default',
|
||||
) + $defaults,
|
||||
array('_field_ui_form_mode_access' => 'administer ' . $entity_type_id . ' form display'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.entity_form_display.{$entity_type_id}.default", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/form-display/{form_mode_name}",
|
||||
array(
|
||||
'_entity_form' => 'entity_form_display.edit',
|
||||
'_title' => 'Manage form display',
|
||||
) + $defaults,
|
||||
array('_field_ui_form_mode_access' => 'administer ' . $entity_type_id . ' form display'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.entity_form_display.{$entity_type_id}.form_mode", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/display",
|
||||
array(
|
||||
'_entity_form' => 'entity_view_display.edit',
|
||||
'_title' => 'Manage display',
|
||||
'view_mode_name' => 'default',
|
||||
) + $defaults,
|
||||
array('_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.entity_view_display.{$entity_type_id}.default", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/display/{view_mode_name}",
|
||||
array(
|
||||
'_entity_form' => 'entity_view_display.edit',
|
||||
'_title' => 'Manage display',
|
||||
) + $defaults,
|
||||
array('_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'),
|
||||
$options
|
||||
);
|
||||
$collection->add("entity.entity_view_display.{$entity_type_id}.view_mode", $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events = parent::getSubscribedEvents();
|
||||
$events[RoutingEvents::ALTER] = array('onAlterRoutes', -100);
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
124
web/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php
Normal file
124
web/core/modules/field_ui/src/Tests/EntityDisplayModeTest.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the entity display modes UI.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class EntityDisplayModeTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block', 'entity_test', 'field_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EntityViewMode user interface.
|
||||
*/
|
||||
public function testEntityViewModeUI() {
|
||||
// Test the listing page.
|
||||
$this->drupalGet('admin/structure/display-modes/view');
|
||||
$this->assertResponse(403);
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer display modes')));
|
||||
$this->drupalGet('admin/structure/display-modes/view');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('Add view mode'));
|
||||
$this->assertLinkByHref('admin/structure/display-modes/view/add');
|
||||
$this->assertLinkByHref('admin/structure/display-modes/view/add/entity_test');
|
||||
|
||||
$this->drupalGet('admin/structure/display-modes/view/add/entity_test_mulrev');
|
||||
$this->assertResponse(404);
|
||||
|
||||
$this->drupalGet('admin/structure/display-modes/view/add');
|
||||
$this->assertNoLink(t('Test entity - revisions and data table'), 'An entity type with no view builder cannot have view modes.');
|
||||
|
||||
// Test adding a view mode including dots in machine_name.
|
||||
$this->clickLink(t('Test entity'));
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName()) . '.' . strtolower($this->randomMachineName()),
|
||||
'label' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('The machine-readable name must contain only lowercase letters, numbers, and underscores.');
|
||||
|
||||
// Test adding a view mode.
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'label' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Saved the %label view mode.', array('%label' => $edit['label'])));
|
||||
|
||||
// Test editing the view mode.
|
||||
$this->drupalGet('admin/structure/display-modes/view/manage/entity_test.' . $edit['id']);
|
||||
|
||||
// Test deleting the view mode.
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the view mode %label?', array('%label' => $edit['label'])));
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
$this->assertRaw(t('The view mode %label has been deleted.', array('%label' => $edit['label'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EntityFormMode user interface.
|
||||
*/
|
||||
public function testEntityFormModeUI() {
|
||||
// Test the listing page.
|
||||
$this->drupalGet('admin/structure/display-modes/form');
|
||||
$this->assertResponse(403);
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer display modes')));
|
||||
$this->drupalGet('admin/structure/display-modes/form');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('Add form mode'));
|
||||
$this->assertLinkByHref('admin/structure/display-modes/form/add');
|
||||
|
||||
$this->drupalGet('admin/structure/display-modes/form/add/entity_test_no_label');
|
||||
$this->assertResponse(404);
|
||||
|
||||
$this->drupalGet('admin/structure/display-modes/form/add');
|
||||
$this->assertNoLink(t('Entity Test without label'), 'An entity type with no form cannot have form modes.');
|
||||
|
||||
// Test adding a view mode including dots in machine_name.
|
||||
$this->clickLink(t('Test entity'));
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName()) . '.' . strtolower($this->randomMachineName()),
|
||||
'label' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('The machine-readable name must contain only lowercase letters, numbers, and underscores.');
|
||||
|
||||
// Test adding a form mode.
|
||||
$edit = array(
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'label' => $this->randomString(),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('Saved the %label form mode.', array('%label' => $edit['label'])));
|
||||
|
||||
// Test editing the form mode.
|
||||
$this->drupalGet('admin/structure/display-modes/form/manage/entity_test.' . $edit['id']);
|
||||
|
||||
// Test deleting the form mode.
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertRaw(t('Are you sure you want to delete the form mode %label?', array('%label' => $edit['label'])));
|
||||
$this->drupalPostForm(NULL, NULL, t('Delete'));
|
||||
$this->assertRaw(t('The form mode %label has been deleted.', array('%label' => $edit['label'])));
|
||||
}
|
||||
|
||||
}
|
111
web/core/modules/field_ui/src/Tests/FieldUIDeleteTest.php
Normal file
111
web/core/modules/field_ui/src/Tests/FieldUIDeleteTest.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?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 = array('node', 'field_ui', 'field_test', 'block', 'field_test_views');
|
||||
|
||||
/**
|
||||
* Test views to enable
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $testViews = array('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(array('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.
|
||||
*/
|
||||
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(array('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(array('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), array('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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests indentation on Field UI.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class FieldUIIndentationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_ui', 'field_ui_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a test user.
|
||||
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node display'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Create Basic page node type.
|
||||
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
|
||||
|
||||
}
|
||||
|
||||
function testIndentation() {
|
||||
$this->drupalGet('admin/structure/types/manage/page/display');
|
||||
$this->assertRaw('js-indentation indentation');
|
||||
}
|
||||
|
||||
}
|
120
web/core/modules/field_ui/src/Tests/FieldUIRouteTest.php
Normal file
120
web/core/modules/field_ui/src/Tests/FieldUIRouteTest.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormMode;
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the functionality of the Field UI route subscriber.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class FieldUIRouteTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block', 'entity_test', 'field_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that entity types with bundles do not break following entity types.
|
||||
*/
|
||||
public function testFieldUIRoutes() {
|
||||
$this->drupalGet('entity_test_no_id/structure/entity_test/fields');
|
||||
$this->assertText('No fields are present yet.');
|
||||
|
||||
$this->drupalGet('admin/config/people/accounts/fields');
|
||||
$this->assertTitle('Manage fields | Drupal');
|
||||
$this->assertLocalTasks();
|
||||
|
||||
// Test manage display tabs and titles.
|
||||
$this->drupalGet('admin/config/people/accounts/display/compact');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalGet('admin/config/people/accounts/display');
|
||||
$this->assertTitle('Manage display | Drupal');
|
||||
$this->assertLocalTasks();
|
||||
|
||||
$edit = array('display_modes_custom[compact]' => TRUE);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('admin/config/people/accounts/display/compact');
|
||||
$this->assertTitle('Manage display | Drupal');
|
||||
$this->assertLocalTasks();
|
||||
|
||||
// Test manage form display tabs and titles.
|
||||
$this->drupalGet('admin/config/people/accounts/form-display/register');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalGet('admin/config/people/accounts/form-display');
|
||||
$this->assertTitle('Manage form display | Drupal');
|
||||
$this->assertLocalTasks();
|
||||
|
||||
$edit = array('display_modes_custom[register]' => TRUE);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet('admin/config/people/accounts/form-display/register');
|
||||
$this->assertTitle('Manage form display | Drupal');
|
||||
$this->assertLocalTasks();
|
||||
$this->assert(count($this->xpath('//ul/li[1]/a[contains(text(), :text)]', array(':text' => 'Default'))) == 1, 'Default secondary tab is in first position.');
|
||||
|
||||
// Create new view mode and verify it's available on the Manage Display
|
||||
// screen after enabling it.
|
||||
EntityViewMode::create(array(
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'targetEntityType' => 'user',
|
||||
))->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
|
||||
$edit = array('display_modes_custom[test]' => TRUE);
|
||||
$this->drupalPostForm('admin/config/people/accounts/display', $edit, t('Save'));
|
||||
$this->assertLink('Test');
|
||||
|
||||
// Create new form mode and verify it's available on the Manage Form
|
||||
// Display screen after enabling it.
|
||||
EntityFormMode::create(array(
|
||||
'id' => 'user.test',
|
||||
'label' => 'Test',
|
||||
'targetEntityType' => 'user',
|
||||
))->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
|
||||
$edit = array('display_modes_custom[test]' => TRUE);
|
||||
$this->drupalPostForm('admin/config/people/accounts/form-display', $edit, t('Save'));
|
||||
$this->assertLink('Test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that local tasks exists.
|
||||
*/
|
||||
public function assertLocalTasks() {
|
||||
$this->assertLink('Settings');
|
||||
$this->assertLink('Manage fields');
|
||||
$this->assertLink('Manage display');
|
||||
$this->assertLink('Manage form display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that admin routes are correctly marked as such.
|
||||
*/
|
||||
public function testAdminRoute() {
|
||||
$route = \Drupal::service('router.route_provider')->getRouteByName('entity.entity_test.field_ui_fields');
|
||||
$is_admin = \Drupal::service('router.admin_context')->isAdminRoute($route);
|
||||
$this->assertTrue($is_admin, 'Admin route correctly marked for "Manage fields" page.');
|
||||
}
|
||||
|
||||
}
|
127
web/core/modules/field_ui/src/Tests/FieldUiTestTrait.php
Normal file
127
web/core/modules/field_ui/src/Tests/FieldUiTestTrait.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Tests;
|
||||
|
||||
/**
|
||||
* 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(), array $field_edit = array()) {
|
||||
$label = $label ?: $this->randomString();
|
||||
$initial_edit = array(
|
||||
'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.', array('%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.', array('%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.', array('%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 = array()) {
|
||||
$label = $label ?: $this->randomString();
|
||||
$initial_edit = array(
|
||||
'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.
|
||||
$main_content = $this->cssSelect('.region-content');
|
||||
$this->setRawContent(reset($main_content)->asXml());
|
||||
$this->assertRaw('field-config-edit-form', 'The field config edit form is present.');
|
||||
$this->assertNoRaw('&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.', array('%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', array('%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, array(), t('Delete'));
|
||||
$this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%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.');
|
||||
}
|
||||
|
||||
}
|
543
web/core/modules/field_ui/src/Tests/ManageDisplayTest.php
Normal file
543
web/core/modules/field_ui/src/Tests/ManageDisplayTest.php
Normal file
|
@ -0,0 +1,543 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_ui\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests the Field UI "Manage display" and "Manage form display" screens.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class ManageDisplayTest extends WebTestBase {
|
||||
|
||||
use FieldUiTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_ui', 'taxonomy', 'search', 'field_test', 'field_third_party_test', 'block');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
// Create a test user.
|
||||
$admin_user = $this->drupalCreateUser(array('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(array('name' => $type_name, 'type' => $type_name));
|
||||
$this->type = $type->id();
|
||||
|
||||
// Create a default vocabulary.
|
||||
$vocabulary = Vocabulary::create(array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'help' => '',
|
||||
'nodes' => array('article' => 'article'),
|
||||
'weight' => mt_rand(0, 10),
|
||||
));
|
||||
$vocabulary->save();
|
||||
$this->vocabulary = $vocabulary->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests formatter settings.
|
||||
*/
|
||||
function testFormatterUI() {
|
||||
$manage_fields = 'admin/structure/types/manage/' . $this->type;
|
||||
$manage_display = $manage_fields . '/display';
|
||||
|
||||
// Create a field, and a node with some data for the field.
|
||||
$this->fieldUIAddNewField($manage_fields, 'test', 'Test field');
|
||||
|
||||
// Get the display options (formatter and settings) that were automatically
|
||||
// assigned for the 'default' display.
|
||||
$display = entity_get_display('node', $this->type, 'default');
|
||||
$display_options = $display->getComponent('field_test');
|
||||
$format = $display_options['type'];
|
||||
$default_settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings($format);
|
||||
$setting_name = key($default_settings);
|
||||
$setting_value = $display_options['settings'][$setting_name];
|
||||
|
||||
// Display the "Manage display" screen and check that the expected formatter
|
||||
// is selected.
|
||||
$this->drupalGet($manage_display);
|
||||
$this->assertFieldByName('fields[field_test][type]', $format, 'The expected formatter is selected.');
|
||||
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
|
||||
|
||||
// Check whether formatter weights are respected.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-fields-field-test-type'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
$expected_options = array (
|
||||
'field_no_settings',
|
||||
'field_empty_test',
|
||||
'field_empty_setting',
|
||||
'field_test_default',
|
||||
'field_test_multiple',
|
||||
'field_test_with_prepare_view',
|
||||
'field_test_applicable',
|
||||
'hidden',
|
||||
);
|
||||
$this->assertEqual($options, $expected_options, 'The expected formatter ordering is respected.');
|
||||
|
||||
// Change the formatter and check that the summary is updated.
|
||||
$edit = array('fields[field_test][type]' => 'field_test_multiple', 'refresh_rows' => 'field_test');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh')));
|
||||
$format = 'field_test_multiple';
|
||||
$default_settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings($format);
|
||||
$setting_name = key($default_settings);
|
||||
$setting_value = $default_settings[$setting_name];
|
||||
$this->assertFieldByName('fields[field_test][type]', $format, 'The expected formatter is selected.');
|
||||
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
|
||||
|
||||
// Submit the form and check that the display is updated.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$display = entity_get_display('node', $this->type, 'default');
|
||||
$display_options = $display->getComponent('field_test');
|
||||
$current_format = $display_options['type'];
|
||||
$current_setting_value = $display_options['settings'][$setting_name];
|
||||
$this->assertEqual($current_format, $format, 'The formatter was updated.');
|
||||
$this->assertEqual($current_setting_value, $setting_value, 'The setting was updated.');
|
||||
|
||||
// Assert that hook_field_formatter_settings_summary_alter() is called.
|
||||
$this->assertText('field_test_field_formatter_settings_summary_alter');
|
||||
|
||||
// Click on the formatter settings button to open the formatter settings
|
||||
// form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
|
||||
// Assert that the field added in
|
||||
// field_test_field_formatter_third_party_settings_form() is present.
|
||||
$fieldname = 'fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_field_formatter_third_party_settings_form]';
|
||||
$this->assertField($fieldname, 'The field added in hook_field_formatter_third_party_settings_form() is present on the settings form.');
|
||||
$edit = array($fieldname => 'foo');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
|
||||
|
||||
// Save the form to save the third party settings.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$id = 'node.' . $this->type . '.default';
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('entity_view_display');
|
||||
$storage->resetCache([$id]);
|
||||
$display = $storage->load($id);
|
||||
$this->assertEqual($display->getRenderer('field_test')->getThirdPartySetting('field_third_party_test', 'field_test_field_formatter_third_party_settings_form'), 'foo');
|
||||
$this->assertTrue(in_array('field_third_party_test', $display->calculateDependencies()->getDependencies()['module']), 'The display has a dependency on field_third_party_test module.');
|
||||
|
||||
// Confirm that the third party settings are not updated on the settings form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
$this->assertFieldByName($fieldname, '');
|
||||
|
||||
// Test the empty setting formatter.
|
||||
$edit = array('fields[field_test][type]' => 'field_empty_setting');
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertNoText('Default empty setting now has a value.');
|
||||
$this->assertFieldById('edit-fields-field-test-settings-edit');
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
$fieldname = 'fields[field_test][settings_edit_form][settings][field_empty_setting]';
|
||||
$edit = array($fieldname => 'non empty setting');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
|
||||
$this->assertText('Default empty setting now has a value.');
|
||||
|
||||
// Test the settings form behavior. An edit button should be present since
|
||||
// there are third party settings to configure.
|
||||
$edit = array('fields[field_test][type]' => 'field_no_settings', 'refresh_rows' => 'field_test');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh')));
|
||||
$this->assertFieldByName('field_test_settings_edit');
|
||||
|
||||
// Make sure we can save the third party settings when there are no settings available
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
|
||||
|
||||
// When a module providing third-party settings to a formatter (or widget)
|
||||
// is uninstalled, the formatter remains enabled but the provided settings,
|
||||
// together with the corresponding form elements, are removed from the
|
||||
// display component.
|
||||
\Drupal::service('module_installer')->uninstall(array('field_third_party_test'));
|
||||
|
||||
// Ensure the button is still there after the module has been disabled.
|
||||
$this->drupalGet($manage_display);
|
||||
$this->assertResponse(200);
|
||||
$this->assertFieldByName('field_test_settings_edit');
|
||||
|
||||
// Ensure that third-party form elements are not present anymore.
|
||||
$this->drupalPostAjaxForm(NULL, array(), 'field_test_settings_edit');
|
||||
$fieldname = 'fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_field_formatter_third_party_settings_form]';
|
||||
$this->assertNoField($fieldname);
|
||||
|
||||
// Ensure that third-party settings were removed from the formatter.
|
||||
$display = EntityViewDisplay::load("node.{$this->type}.default");
|
||||
$component = $display->getComponent('field_test');
|
||||
$this->assertFalse(array_key_exists('field_third_party_test', $component['third_party_settings']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests widget settings.
|
||||
*/
|
||||
public function testWidgetUI() {
|
||||
// Admin Manage Fields page.
|
||||
$manage_fields = 'admin/structure/types/manage/' . $this->type;
|
||||
// Admin Manage Display page.
|
||||
$manage_display = $manage_fields . '/form-display';
|
||||
|
||||
// Creates a new field that can be used with multiple formatters.
|
||||
// Reference: Drupal\field_test\Plugin\Field\FieldWidget\TestFieldWidgetMultiple::isApplicable().
|
||||
$this->fieldUIAddNewField($manage_fields, 'test', 'Test field');
|
||||
|
||||
// Get the display options (formatter and settings) that were automatically
|
||||
// assigned for the 'default' display.
|
||||
$display = entity_get_form_display('node', $this->type, 'default');
|
||||
$display_options = $display->getComponent('field_test');
|
||||
$widget_type = $display_options['type'];
|
||||
$default_settings = \Drupal::service('plugin.manager.field.widget')->getDefaultSettings($widget_type);
|
||||
$setting_name = key($default_settings);
|
||||
$setting_value = $display_options['settings'][$setting_name];
|
||||
|
||||
// Display the "Manage form display" screen and check if the expected
|
||||
// widget is selected.
|
||||
$this->drupalGet($manage_display);
|
||||
$this->assertFieldByName('fields[field_test][type]', $widget_type, 'The expected widget is selected.');
|
||||
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
|
||||
|
||||
// Check whether widget weights are respected.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-fields-field-test-type'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
$expected_options = array (
|
||||
'test_field_widget',
|
||||
'test_field_widget_multiple',
|
||||
'hidden',
|
||||
);
|
||||
$this->assertEqual($options, $expected_options, 'The expected widget ordering is respected.');
|
||||
|
||||
// Change the widget and check that the summary is updated.
|
||||
$edit = array('fields[field_test][type]' => 'test_field_widget_multiple', 'refresh_rows' => 'field_test');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh')));
|
||||
$widget_type = 'test_field_widget_multiple';
|
||||
$default_settings = \Drupal::service('plugin.manager.field.widget')->getDefaultSettings($widget_type);
|
||||
$setting_name = key($default_settings);
|
||||
$setting_value = $default_settings[$setting_name];
|
||||
$this->assertFieldByName('fields[field_test][type]', $widget_type, 'The expected widget is selected.');
|
||||
$this->assertText("$setting_name: $setting_value", 'The expected summary is displayed.');
|
||||
|
||||
// Submit the form and check that the display is updated.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$display = entity_get_form_display('node', $this->type, 'default');
|
||||
$display_options = $display->getComponent('field_test');
|
||||
$current_widget = $display_options['type'];
|
||||
$current_setting_value = $display_options['settings'][$setting_name];
|
||||
$this->assertEqual($current_widget, $widget_type, 'The widget was updated.');
|
||||
$this->assertEqual($current_setting_value, $setting_value, 'The setting was updated.');
|
||||
|
||||
// Assert that hook_field_widget_settings_summary_alter() is called.
|
||||
$this->assertText('field_test_field_widget_settings_summary_alter');
|
||||
|
||||
// Click on the widget settings button to open the widget settings form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
|
||||
// Assert that the field added in
|
||||
// field_test_field_widget_third_party_settings_form() is present.
|
||||
$fieldname = 'fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_widget_third_party_settings_form]';
|
||||
$this->assertField($fieldname, 'The field added in hook_field_widget_third_party_settings_form() is present on the settings form.');
|
||||
$edit = array($fieldname => 'foo');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
|
||||
|
||||
// Save the form to save the third party settings.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('entity_form_display');
|
||||
$storage->resetCache(array('node.' . $this->type . '.default'));
|
||||
$display = $storage->load('node.' . $this->type . '.default');
|
||||
$this->assertEqual($display->getRenderer('field_test')->getThirdPartySetting('field_third_party_test', 'field_test_widget_third_party_settings_form'), 'foo');
|
||||
$this->assertTrue(in_array('field_third_party_test', $display->calculateDependencies()->getDependencies()['module']), 'Form display does not have a dependency on field_third_party_test module.');
|
||||
|
||||
// Confirm that the third party settings are not updated on the settings form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
|
||||
$this->assertFieldByName($fieldname, '');
|
||||
|
||||
// Creates a new field that can not be used with the multiple formatter.
|
||||
// Reference: Drupal\field_test\Plugin\Field\FieldWidget\TestFieldWidgetMultiple::isApplicable().
|
||||
$this->fieldUIAddNewField($manage_fields, 'onewidgetfield', 'One Widget Field');
|
||||
|
||||
// Go to the Manage Form Display.
|
||||
$this->drupalGet($manage_display);
|
||||
|
||||
// Checks if the select elements contain the specified options.
|
||||
$this->assertFieldSelectOptions('fields[field_test][type]', array('test_field_widget', 'test_field_widget_multiple', 'hidden'));
|
||||
$this->assertFieldSelectOptions('fields[field_onewidgetfield][type]', array('test_field_widget', 'hidden'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests switching view modes to use custom or 'default' settings'.
|
||||
*/
|
||||
function testViewModeCustom() {
|
||||
// Create a field, and a node with some data for the field.
|
||||
$this->fieldUIAddNewField('admin/structure/types/manage/' . $this->type, 'test', 'Test field');
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
// For this test, use a formatter setting value that is an integer unlikely
|
||||
// to appear in a rendered node other than as part of the field being tested
|
||||
// (for example, unlikely to be part of the "Submitted by ... on ..." line).
|
||||
$value = 12345;
|
||||
$settings = array(
|
||||
'type' => $this->type,
|
||||
'field_test' => array(array('value' => $value)),
|
||||
);
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
|
||||
// Gather expected output values with the various formatters.
|
||||
$formatter_plugin_manager = \Drupal::service('plugin.manager.field.formatter');
|
||||
$field_test_default_settings = $formatter_plugin_manager->getDefaultSettings('field_test_default');
|
||||
$field_test_with_prepare_view_settings = $formatter_plugin_manager->getDefaultSettings('field_test_with_prepare_view');
|
||||
$output = array(
|
||||
'field_test_default' => $field_test_default_settings['test_formatter_setting'] . '|' . $value,
|
||||
'field_test_with_prepare_view' => $field_test_with_prepare_view_settings['test_formatter_setting_additional'] . '|' . $value . '|' . ($value + 1),
|
||||
);
|
||||
|
||||
// Check that the field is displayed with the default formatter in 'rss'
|
||||
// mode (uses 'default'), and hidden in 'teaser' mode (uses custom settings).
|
||||
$this->assertNodeViewText($node, 'rss', $output['field_test_default'], "The field is displayed as expected in view modes that use 'default' settings.");
|
||||
$this->assertNodeViewNoText($node, 'teaser', $value, "The field is hidden in view modes that use custom settings.");
|
||||
|
||||
// Change formatter for 'default' mode, check that the field is displayed
|
||||
// accordingly in 'rss' mode.
|
||||
$edit = array(
|
||||
'fields[field_test][type]' => 'field_test_with_prepare_view',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display', $edit, t('Save'));
|
||||
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected in view modes that use 'default' settings.");
|
||||
|
||||
// Specialize the 'rss' mode, check that the field is displayed the same.
|
||||
$edit = array(
|
||||
"display_modes_custom[rss]" => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display', $edit, t('Save'));
|
||||
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected in newly specialized 'rss' mode.");
|
||||
|
||||
// Set the field to 'hidden' in the view mode, check that the field is
|
||||
// hidden.
|
||||
$edit = array(
|
||||
'fields[field_test][type]' => 'hidden',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display/rss', $edit, t('Save'));
|
||||
$this->assertNodeViewNoText($node, 'rss', $value, "The field is hidden in 'rss' mode.");
|
||||
|
||||
// Set the view mode back to 'default', check that the field is displayed
|
||||
// accordingly.
|
||||
$edit = array(
|
||||
"display_modes_custom[rss]" => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display', $edit, t('Save'));
|
||||
$this->assertNodeViewText($node, 'rss', $output['field_test_with_prepare_view'], "The field is displayed as expected when 'rss' mode is set back to 'default' settings.");
|
||||
|
||||
// Specialize the view mode again.
|
||||
$edit = array(
|
||||
"display_modes_custom[rss]" => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display', $edit, t('Save'));
|
||||
// Check that the previous settings for the view mode have been kept.
|
||||
$this->assertNodeViewNoText($node, 'rss', $value, "The previous settings are kept when 'rss' mode is specialized again.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the local tasks are displayed correctly for view modes.
|
||||
*/
|
||||
public function testViewModeLocalTasks() {
|
||||
$manage_display = 'admin/structure/types/manage/' . $this->type . '/display';
|
||||
$this->drupalGet($manage_display);
|
||||
$this->assertNoLink('Full content');
|
||||
$this->drupalGet($manage_display . '/teaser');
|
||||
$this->assertNoLink('Full content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that fields with no explicit display settings do not break.
|
||||
*/
|
||||
function testNonInitializedFields() {
|
||||
// Create a test field.
|
||||
$this->fieldUIAddNewField('admin/structure/types/manage/' . $this->type, 'test', 'Test');
|
||||
|
||||
// Check that the field appears as 'hidden' on the 'Manage display' page
|
||||
// for the 'teaser' mode.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->type . '/display/teaser');
|
||||
$this->assertFieldByName('fields[field_test][type]', 'hidden', 'The field is displayed as \'hidden \'.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hiding the view modes fieldset when there's only one available.
|
||||
*/
|
||||
function testSingleViewMode() {
|
||||
$this->drupalGet('admin/structure/taxonomy/manage/' . $this->vocabulary . '/display');
|
||||
$this->assertNoText('Use custom display settings for the following view modes', 'Custom display settings fieldset found.');
|
||||
|
||||
// This may not trigger a notice when 'view_modes_custom' isn't available.
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/' . $this->vocabulary . '/overview/display', array(), t('Save'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a message is shown when there are no fields.
|
||||
*/
|
||||
function testNoFieldsDisplayOverview() {
|
||||
// Create a fresh content type without any fields.
|
||||
NodeType::create(array(
|
||||
'type' => 'no_fields',
|
||||
'name' => 'No fields',
|
||||
))->save();
|
||||
|
||||
$this->drupalGet('admin/structure/types/manage/no_fields/display');
|
||||
$this->assertRaw(t('There are no fields yet added. You can add new fields on the <a href=":link">Manage fields</a> page.', array(':link' => \Drupal::url('entity.node.field_ui_fields', array('node_type' => 'no_fields')))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string is found in the rendered node in a view mode.
|
||||
*
|
||||
* @param EntityInterface $node
|
||||
* The node.
|
||||
* @param $view_mode
|
||||
* The view mode in which the node should be displayed.
|
||||
* @param $text
|
||||
* Plain text to look for.
|
||||
* @param $message
|
||||
* Message to display.
|
||||
*
|
||||
* @return
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
function assertNodeViewText(EntityInterface $node, $view_mode, $text, $message) {
|
||||
return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string is not found in the rendered node in a view mode.
|
||||
*
|
||||
* @param EntityInterface $node
|
||||
* The node.
|
||||
* @param $view_mode
|
||||
* The view mode in which the node should be displayed.
|
||||
* @param $text
|
||||
* Plain text to look for.
|
||||
* @param $message
|
||||
* Message to display.
|
||||
* @return
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
function assertNodeViewNoText(EntityInterface $node, $view_mode, $text, $message) {
|
||||
return $this->assertNodeViewTextHelper($node, $view_mode, $text, $message, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string is (not) found in the rendered nodein a view mode.
|
||||
*
|
||||
* This helper function is used by assertNodeViewText() and
|
||||
* assertNodeViewNoText().
|
||||
*
|
||||
* @param EntityInterface $node
|
||||
* The node.
|
||||
* @param $view_mode
|
||||
* The view mode in which the node should be displayed.
|
||||
* @param $text
|
||||
* Plain text to look for.
|
||||
* @param $message
|
||||
* Message to display.
|
||||
* @param $not_exists
|
||||
* TRUE if this text should not exist, FALSE if it should.
|
||||
*
|
||||
* @return
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
function assertNodeViewTextHelper(EntityInterface $node, $view_mode, $text, $message, $not_exists) {
|
||||
// Make sure caches on the tester side are refreshed after changes
|
||||
// submitted on the tested side.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
|
||||
// Save current content so that we can restore it when we're done.
|
||||
$old_content = $this->getRawContent();
|
||||
|
||||
// Render a cloned node, so that we do not alter the original.
|
||||
$clone = clone $node;
|
||||
$element = node_view($clone, $view_mode);
|
||||
$output = \Drupal::service('renderer')->renderRoot($element);
|
||||
$this->verbose(t('Rendered node - view mode: @view_mode', array('@view_mode' => $view_mode)) . '<hr />' . $output);
|
||||
|
||||
// Assign content so that WebTestBase functions can be used.
|
||||
$this->setRawContent($output);
|
||||
$method = ($not_exists ? 'assertNoText' : 'assertText');
|
||||
$return = $this->{$method}((string) $text, $message);
|
||||
|
||||
// Restore previous content.
|
||||
$this->setRawContent($old_content);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a select element contains the specified options.
|
||||
*
|
||||
* @param string $name
|
||||
* The field name.
|
||||
* @param array $expected_options
|
||||
* An array of expected options.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertFieldSelectOptions($name, array $expected_options) {
|
||||
$xpath = $this->buildXPathQuery('//select[@name=:name]', array(':name' => $name));
|
||||
$fields = $this->xpath($xpath);
|
||||
if ($fields) {
|
||||
$field = $fields[0];
|
||||
$options = $this->getAllOptionsList($field);
|
||||
|
||||
sort($options);
|
||||
sort($expected_options);
|
||||
|
||||
return $this->assertIdentical($options, $expected_options);
|
||||
}
|
||||
else {
|
||||
return $this->fail('Unable to find field ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all options from a select element.
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* The select element field information.
|
||||
*
|
||||
* @return array
|
||||
* An array of option values as strings.
|
||||
*/
|
||||
protected function getAllOptionsList(\SimpleXMLElement $element) {
|
||||
$options = array();
|
||||
// Add all options items.
|
||||
foreach ($element->option as $option) {
|
||||
$options[] = (string) $option['value'];
|
||||
}
|
||||
|
||||
// Loops trough all the option groups
|
||||
foreach ($element->optgroup as $optgroup) {
|
||||
$options = array_merge($this->getAllOptionsList($optgroup), $options);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
768
web/core/modules/field_ui/src/Tests/ManageFieldsTest.php
Normal file
768
web/core/modules/field_ui/src/Tests/ManageFieldsTest.php
Normal file
|
@ -0,0 +1,768 @@
|
|||
<?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 = array('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(array('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(array('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(array('type' => 'page', 'name' => 'Basic page'));
|
||||
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
|
||||
|
||||
// Create a vocabulary named "Tags".
|
||||
$vocabulary = Vocabulary::create(array(
|
||||
'name' => 'Tags',
|
||||
'vid' => 'tags',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$vocabulary->save();
|
||||
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
function manageFieldsPage($type = '') {
|
||||
$type = empty($type) ? $this->contentType : $type;
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type . '/fields');
|
||||
// Check all table columns.
|
||||
$table_headers = array(
|
||||
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.', array('%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.
|
||||
*/
|
||||
function createField() {
|
||||
// Create a test field.
|
||||
$this->fieldUIAddNewField('admin/structure/types/manage/' . $this->contentType, $this->fieldNameInput, $this->fieldLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing an existing field.
|
||||
*/
|
||||
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 = array(
|
||||
'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 = array(
|
||||
'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.
|
||||
*/
|
||||
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]', array(':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().
|
||||
*/
|
||||
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 = array(
|
||||
'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 = array(
|
||||
'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 = array(
|
||||
'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, array(), 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 = array(
|
||||
'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', array('@label' => $this->fieldLabel, '@type' => $this->contentType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that default value is correctly validated and saved.
|
||||
*/
|
||||
function testDefaultValue() {
|
||||
// Create a test field storage and field.
|
||||
$field_name = 'test';
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'test_field'
|
||||
))->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'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 = array($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 = array($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(), array(array('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 = array($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 = array(
|
||||
'required' => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
|
||||
$this->drupalGet($admin_path);
|
||||
$this->drupalPostForm(NULL, array(), 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.
|
||||
*/
|
||||
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(array('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.
|
||||
*/
|
||||
function testDisallowedFieldNames() {
|
||||
// Reset the field prefix so we can test properly.
|
||||
$this->config('field_ui.settings')->set('field_prefix', '')->save();
|
||||
|
||||
$label = 'Disallowed field';
|
||||
$edit = array(
|
||||
'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.
|
||||
*/
|
||||
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(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
'locked' => TRUE
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->contentType,
|
||||
))->save();
|
||||
entity_get_form_display('node', $this->contentType, 'default')
|
||||
->setComponent($field_name, array(
|
||||
'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]', array(':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]', array(':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]', array(':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.
|
||||
*/
|
||||
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(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => $field_name,
|
||||
))->save();
|
||||
$field = array(
|
||||
'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.', array('%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]', array(':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]', array(':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]', array(':field_type' => $field_type)), SafeMarkup::format('Configurable field type @field_type is available.', array('@field_type' => $field_type)));
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', array(':field_type' => $field_type)), SafeMarkup::format('Non-configurable field type @field_type is not available.', array('@field_type' => $field_type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a duplicate field name is caught by validation.
|
||||
*/
|
||||
function testDuplicateFieldName() {
|
||||
// field_tags already exists, so we're expecting an error when trying to
|
||||
// create a new field with the same name.
|
||||
$edit = array(
|
||||
'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, array(), '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 'http://example.com' 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
function testHelpDescriptions() {
|
||||
// Create an image field
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_image',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
))->save();
|
||||
|
||||
FieldConfig::create(array(
|
||||
'field_name' => 'field_image',
|
||||
'entity_type' => 'node',
|
||||
'label' => 'Image',
|
||||
'bundle' => 'article',
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')->setComponent('field_image')->save();
|
||||
|
||||
$edit = array(
|
||||
'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 = array(
|
||||
'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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
47
web/core/modules/field_ui/templates/field-ui-table.html.twig
Normal file
47
web/core/modules/field_ui/templates/field-ui-table.html.twig
Normal file
|
@ -0,0 +1,47 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to display a Field UI table.
|
||||
*
|
||||
* Available variables:
|
||||
* - attributes: HTML attributes to apply to the <table> tag.
|
||||
* - caption: A localized string for the <caption> tag.
|
||||
* - colgroups: Column groups. Each group contains the following properties:
|
||||
* - attributes: HTML attributes to apply to the <col> tag.
|
||||
* Note: Drupal currently supports only one table header row, see
|
||||
* https://www.drupal.org/node/893530 and
|
||||
* http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_table/7#comment-5109.
|
||||
* - header: Table header cells. Each cell contains the following properties:
|
||||
* - tag: The HTML tag name to use; either TH or TD.
|
||||
* - attributes: HTML attributes to apply to the tag.
|
||||
* - content: A localized string for the title of the column.
|
||||
* - field: Field name (required for column sorting).
|
||||
* - sort: Default sort order for this column ("asc" or "desc").
|
||||
* - sticky: A flag indicating whether to use a "sticky" table header.
|
||||
* - rows: Table rows. Each row contains the following properties:
|
||||
* - attributes: HTML attributes to apply to the <tr> tag.
|
||||
* - data: Table cells.
|
||||
* - no_striping: A flag indicating that the row should receive no
|
||||
* 'even / odd' styling. Defaults to FALSE.
|
||||
* - cells: Table cells of the row. Each cell contains the following keys:
|
||||
* - tag: The HTML tag name to use; either 'th' or 'td'.
|
||||
* - attributes: Any HTML attributes, such as "colspan", to apply to the
|
||||
* table cell.
|
||||
* - content: The string to display in the table cell.
|
||||
* - active_table_sort: A boolean indicating whether the cell is the active
|
||||
table sort.
|
||||
* - footer: Table footer rows, in the same format as the rows variable.
|
||||
* - empty: The message to display in an extra row if table does not have
|
||||
* any rows.
|
||||
* - no_striping: A boolean indicating that the row should receive no striping.
|
||||
* - header_columns: The number of columns in the header.
|
||||
*
|
||||
* @see template_preprocess_field_ui_table()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{# Add Ajax wrapper. #}
|
||||
<div id="field-display-overview-wrapper">
|
||||
{% include 'table.html.twig' %}
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
name: 'Field UI test'
|
||||
type: module
|
||||
description: 'Support module for Field UI tests.'
|
||||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Field UI test module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_BASE_ID_alter().
|
||||
*/
|
||||
function field_ui_test_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
|
||||
$table = &$form['fields'];
|
||||
|
||||
foreach (Element::children($table) as $name) {
|
||||
$table[$name]['parent_wrapper']['parent']['#options'] = array('indent' => 'Indent');
|
||||
$table[$name]['parent_wrapper']['parent']['#default_value'] = 'indent';
|
||||
}
|
||||
|
||||
$table['indent'] = [
|
||||
'#attributes' => array('class' => array('draggable', 'field-group'), 'id' => 'indent-id'),
|
||||
'#row_type' => 'group',
|
||||
'#region_callback' => 'field_ui_test_region_callback',
|
||||
'#js_settings' => array('rowHandler' => 'group'),
|
||||
'human_name' => array(
|
||||
'#markup' => 'Indent',
|
||||
'#prefix' => '<span class="group-label">',
|
||||
'#suffix' => '</span>',
|
||||
),
|
||||
'weight' => array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => 0,
|
||||
'#size' => 3,
|
||||
'#attributes' => array('class' => array('field-weight')),
|
||||
),
|
||||
'parent_wrapper' => array(
|
||||
'parent' => array(
|
||||
'#type' => 'select',
|
||||
'#options' => array('indent' => 'Indent'),
|
||||
'#empty_value' => '',
|
||||
'#default_value' => '',
|
||||
'#attributes' => array('class' => array('field-parent')),
|
||||
'#parents' => array('fields', 'indent', 'parent'),
|
||||
),
|
||||
'hidden_name' => array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => 'indent',
|
||||
'#attributes' => array('class' => array('field-name')),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
function field_ui_test_region_callback($row) {
|
||||
return 'content';
|
||||
}
|
650
web/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
Normal file
650
web/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
Normal file
|
@ -0,0 +1,650 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests the entity display configuration entities.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class EntityDisplayTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = array('field_ui', 'field', 'entity_test', 'user', 'text', 'field_test', 'node', 'system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(array('field', 'node', 'user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic CRUD operations on entity display objects.
|
||||
*/
|
||||
public function testEntityDisplayCRUD() {
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
$expected = array();
|
||||
|
||||
// Check that providing no 'weight' results in the highest current weight
|
||||
// being assigned. The 'name' field's formatter has weight -5, therefore
|
||||
// these follow.
|
||||
$expected['component_1'] = array('weight' => -4, 'settings' => array(), 'third_party_settings' => array());
|
||||
$expected['component_2'] = array('weight' => -3, 'settings' => array(), 'third_party_settings' => array());
|
||||
$display->setComponent('component_1');
|
||||
$display->setComponent('component_2');
|
||||
$this->assertEqual($display->getComponent('component_1'), $expected['component_1']);
|
||||
$this->assertEqual($display->getComponent('component_2'), $expected['component_2']);
|
||||
|
||||
// Check that arbitrary options are correctly stored.
|
||||
$expected['component_3'] = array('weight' => 10, 'third_party_settings' => array('field_test' => array('foo' => 'bar')), 'settings' => array());
|
||||
$display->setComponent('component_3', $expected['component_3']);
|
||||
$this->assertEqual($display->getComponent('component_3'), $expected['component_3']);
|
||||
|
||||
// Check that the display can be properly saved and read back.
|
||||
$display->save();
|
||||
$display = EntityViewDisplay::load($display->id());
|
||||
foreach (array('component_1', 'component_2', 'component_3') as $name) {
|
||||
$this->assertEqual($display->getComponent($name), $expected[$name]);
|
||||
}
|
||||
|
||||
// Ensure that third party settings were added to the config entity.
|
||||
// These are added by entity_test_entity_presave() implemented in
|
||||
// entity_test module.
|
||||
$this->assertEqual('bar', $display->getThirdPartySetting('entity_test', 'foo'), 'Third party settings were added to the entity view display.');
|
||||
|
||||
// Check that getComponents() returns options for all components.
|
||||
$expected['name'] = array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => -5,
|
||||
'settings' => array(
|
||||
'link_to_entity' => FALSE,
|
||||
),
|
||||
'third_party_settings' => array(),
|
||||
);
|
||||
$this->assertEqual($display->getComponents(), $expected);
|
||||
|
||||
// Check that a component can be removed.
|
||||
$display->removeComponent('component_3');
|
||||
$this->assertNULL($display->getComponent('component_3'));
|
||||
|
||||
// Check that the removal is correctly persisted.
|
||||
$display->save();
|
||||
$display = EntityViewDisplay::load($display->id());
|
||||
$this->assertNULL($display->getComponent('component_3'));
|
||||
|
||||
// Check that createCopy() creates a new component that can be correctly
|
||||
// saved.
|
||||
EntityViewMode::create(array('id' => $display->getTargetEntityTypeId() . '.other_view_mode', 'targetEntityType' => $display->getTargetEntityTypeId()))->save();
|
||||
$new_display = $display->createCopy('other_view_mode');
|
||||
$new_display->save();
|
||||
$new_display = EntityViewDisplay::load($new_display->id());
|
||||
$dependencies = $new_display->calculateDependencies()->getDependencies();
|
||||
$this->assertEqual(array('config' => array('core.entity_view_mode.entity_test.other_view_mode'), 'module' => array('entity_test')), $dependencies);
|
||||
$this->assertEqual($new_display->getTargetEntityTypeId(), $display->getTargetEntityTypeId());
|
||||
$this->assertEqual($new_display->getTargetBundle(), $display->getTargetBundle());
|
||||
$this->assertEqual($new_display->getMode(), 'other_view_mode');
|
||||
$this->assertEqual($new_display->getComponents(), $display->getComponents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sorting of components by name on basic CRUD operations
|
||||
*/
|
||||
public function testEntityDisplayCRUDSort() {
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
));
|
||||
$display->setComponent('component_3');
|
||||
$display->setComponent('component_1');
|
||||
$display->setComponent('component_2');
|
||||
$display->save();
|
||||
$components = array_keys($display->getComponents());
|
||||
// The name field is not configurable so will be added automatically.
|
||||
$expected = array ( 0 => 'component_1', 1 => 'component_2', 2 => 'component_3', 'name');
|
||||
$this->assertIdentical($components, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity_get_display().
|
||||
*/
|
||||
public function testEntityGetDisplay() {
|
||||
// Check that entity_get_display() returns a fresh object when no
|
||||
// configuration entry exists.
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertTrue($display->isNew());
|
||||
|
||||
// Add some components and save the display.
|
||||
$display->setComponent('component_1', array('weight' => 10, 'settings' => array()))
|
||||
->save();
|
||||
|
||||
// Check that entity_get_display() returns the correct object.
|
||||
$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'), array( 'weight' => 10, 'settings' => array(), 'third_party_settings' => array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field component within an entity display object.
|
||||
*/
|
||||
public function testExtraFieldComponent() {
|
||||
entity_test_create_bundle('bundle_with_extra_fields');
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'bundle_with_extra_fields',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
// Check that the default visibility taken into account for extra fields
|
||||
// unknown in the display.
|
||||
$this->assertEqual($display->getComponent('display_extra_field'), array('weight' => 5));
|
||||
$this->assertNull($display->getComponent('display_extra_field_hidden'));
|
||||
|
||||
// Check that setting explicit options overrides the defaults.
|
||||
$display->removeComponent('display_extra_field');
|
||||
$display->setComponent('display_extra_field_hidden', array('weight' => 10));
|
||||
$this->assertNull($display->getComponent('display_extra_field'));
|
||||
$this->assertEqual($display->getComponent('display_extra_field_hidden'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field component within an entity display object.
|
||||
*/
|
||||
public function testFieldComponent() {
|
||||
$field_name = 'test_field';
|
||||
// Create a field storage and a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
// Check that providing no options results in default values being used.
|
||||
$display->setComponent($field_name);
|
||||
$field_type_info = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_storage->getType());
|
||||
$default_formatter = $field_type_info['default_formatter'];
|
||||
$formatter_settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings($default_formatter);
|
||||
$expected = array(
|
||||
'weight' => -4,
|
||||
'label' => 'above',
|
||||
'type' => $default_formatter,
|
||||
'settings' => $formatter_settings,
|
||||
'third_party_settings' => array(),
|
||||
);
|
||||
$this->assertEqual($display->getComponent($field_name), $expected);
|
||||
|
||||
// Check that the getFormatter() method returns the correct formatter plugin.
|
||||
$formatter = $display->getRenderer($field_name);
|
||||
$this->assertEqual($formatter->getPluginId(), $default_formatter);
|
||||
$this->assertEqual($formatter->getSettings(), $formatter_settings);
|
||||
|
||||
// Check that the formatter is statically persisted, by assigning an
|
||||
// arbitrary property and reading it back.
|
||||
$random_value = $this->randomString();
|
||||
$formatter->randomValue = $random_value;
|
||||
$formatter = $display->getRenderer($field_name);
|
||||
$this->assertEqual($formatter->randomValue, $random_value);
|
||||
|
||||
// Check that changing the definition creates a new formatter.
|
||||
$display->setComponent($field_name, array(
|
||||
'type' => 'field_test_multiple',
|
||||
));
|
||||
$formatter = $display->getRenderer($field_name);
|
||||
$this->assertEqual($formatter->getPluginId(), 'field_test_multiple');
|
||||
$this->assertFalse(isset($formatter->randomValue));
|
||||
|
||||
// Check that the display has dependencies on the field and the module that
|
||||
// provides the formatter.
|
||||
$dependencies = $display->calculateDependencies()->getDependencies();
|
||||
$this->assertEqual(array('config' => array('field.field.entity_test.entity_test.test_field'), 'module' => array('entity_test', 'field_test')), $dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field component for a base field.
|
||||
*/
|
||||
public function testBaseFieldComponent() {
|
||||
$display = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test_base_field_display',
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
// Check that default options are correctly filled in.
|
||||
$formatter_settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings('text_default');
|
||||
$expected = array(
|
||||
'test_no_display' => NULL,
|
||||
'test_display_configurable' => array(
|
||||
'label' => 'above',
|
||||
'type' => 'text_default',
|
||||
'settings' => $formatter_settings,
|
||||
'third_party_settings' => array(),
|
||||
'weight' => 10,
|
||||
),
|
||||
'test_display_non_configurable' => array(
|
||||
'label' => 'above',
|
||||
'type' => 'text_default',
|
||||
'settings' => $formatter_settings,
|
||||
'third_party_settings' => array(),
|
||||
'weight' => 11,
|
||||
),
|
||||
);
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
|
||||
// Check that saving the display only writes data for fields whose display
|
||||
// is configurable.
|
||||
$display->save();
|
||||
$config = $this->config('core.entity_view_display.' . $display->id());
|
||||
$data = $config->get();
|
||||
$this->assertFalse(isset($data['content']['test_no_display']));
|
||||
$this->assertFalse(isset($data['hidden']['test_no_display']));
|
||||
$this->assertEqual($data['content']['test_display_configurable'], $expected['test_display_configurable']);
|
||||
$this->assertFalse(isset($data['content']['test_display_non_configurable']));
|
||||
$this->assertFalse(isset($data['hidden']['test_display_non_configurable']));
|
||||
|
||||
// Check that defaults are correctly filled when loading the display.
|
||||
$display = EntityViewDisplay::load($display->id());
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
|
||||
// Check that data manually written for fields whose display is not
|
||||
// configurable is discarded when loading the display.
|
||||
$data['content']['test_display_non_configurable'] = $expected['test_display_non_configurable'];
|
||||
$data['content']['test_display_non_configurable']['weight']++;
|
||||
$config->setData($data)->save();
|
||||
$display = EntityViewDisplay::load($display->id());
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a bundle.
|
||||
*/
|
||||
public function testDeleteBundle() {
|
||||
// Create a node bundle, display and form display object.
|
||||
$type = NodeType::create(array('type' => 'article'));
|
||||
$type->save();
|
||||
node_add_body_field($type);
|
||||
entity_get_display('node', 'article', 'default')->save();
|
||||
entity_get_form_display('node', 'article', 'default')->save();
|
||||
|
||||
// Delete the bundle.
|
||||
$type->delete();
|
||||
$display = EntityViewDisplay::load('node.article.default');
|
||||
$this->assertFalse((bool) $display);
|
||||
$form_display = EntityFormDisplay::load('node.article.default');
|
||||
$this->assertFalse((bool) $form_display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting field.
|
||||
*/
|
||||
public function testDeleteField() {
|
||||
$field_name = 'test_field';
|
||||
// Create a field storage and a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Create default and teaser entity display.
|
||||
EntityViewMode::create(array('id' => 'entity_test.teaser', 'targetEntityType' => 'entity_test'))->save();
|
||||
EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
))->setComponent($field_name)->save();
|
||||
EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'teaser',
|
||||
))->setComponent($field_name)->save();
|
||||
|
||||
// Check the component exists.
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertTrue($display->getComponent($field_name));
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'teaser');
|
||||
$this->assertTrue($display->getComponent($field_name));
|
||||
|
||||
// Delete the field.
|
||||
$field->delete();
|
||||
|
||||
// Check that the component has been removed from the entity displays.
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'teaser');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval().
|
||||
*/
|
||||
public function testOnDependencyRemoval() {
|
||||
$this->enableModules(array('field_plugins_test'));
|
||||
|
||||
$field_name = 'test_field';
|
||||
// Create a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'text'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
))->setComponent($field_name, array('type' => 'field_plugins_test_text_formatter'))->save();
|
||||
|
||||
// Check the component exists and is of the correct type.
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertEqual($display->getComponent($field_name)['type'], 'field_plugins_test_text_formatter');
|
||||
|
||||
// Removing the field_plugins_test module should change the component to use
|
||||
// the default formatter for test fields.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'field_plugins_test');
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertEqual($display->getComponent($field_name)['type'], 'text_default');
|
||||
|
||||
// Removing the text module should remove the field from the view display.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'text');
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that entity view display changes invalidates cache tags.
|
||||
*/
|
||||
public function testEntityDisplayInvalidateCacheTags() {
|
||||
$cache = \Drupal::cache();
|
||||
$cache->set('cid', 'kittens', Cache::PERMANENT, ['config:entity_view_display_list']);
|
||||
$display = EntityViewDisplay::create([
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
]);
|
||||
$display->setComponent('kitten');
|
||||
$display->save();
|
||||
$this->assertFalse($cache->get('cid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getDisplayModeOptions().
|
||||
*/
|
||||
public function testGetDisplayModeOptions() {
|
||||
NodeType::create(array('type' => 'article'))->save();
|
||||
|
||||
EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'article',
|
||||
'mode' => 'default',
|
||||
))->setStatus(TRUE)->save();
|
||||
|
||||
$display_teaser = EntityViewDisplay::create(array(
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'article',
|
||||
'mode' => 'teaser',
|
||||
));
|
||||
$display_teaser->save();
|
||||
|
||||
EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'user',
|
||||
'bundle' => 'user',
|
||||
'mode' => 'default',
|
||||
))->setStatus(TRUE)->save();
|
||||
|
||||
$form_display_teaser = EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'user',
|
||||
'bundle' => 'user',
|
||||
'mode' => 'register',
|
||||
));
|
||||
$form_display_teaser->save();
|
||||
|
||||
// Test getViewModeOptionsByBundle().
|
||||
$view_modes = \Drupal::entityManager()->getViewModeOptionsByBundle('node', 'article');
|
||||
$this->assertEqual($view_modes, array('default' => 'Default'));
|
||||
$display_teaser->setStatus(TRUE)->save();
|
||||
$view_modes = \Drupal::entityManager()->getViewModeOptionsByBundle('node', 'article');
|
||||
$this->assertEqual($view_modes, array('default' => 'Default', 'teaser' => 'Teaser'));
|
||||
|
||||
// Test getFormModeOptionsByBundle().
|
||||
$form_modes = \Drupal::entityManager()->getFormModeOptionsByBundle('user', 'user');
|
||||
$this->assertEqual($form_modes, array('default' => 'Default'));
|
||||
$form_display_teaser->setStatus(TRUE)->save();
|
||||
$form_modes = \Drupal::entityManager()->getFormModeOptionsByBundle('user', 'user');
|
||||
$this->assertEqual($form_modes, array('default' => 'Default', 'register' => 'Register'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests components dependencies additions.
|
||||
*/
|
||||
public function testComponentDependencies() {
|
||||
$this->enableModules(['dblog', 'color']);
|
||||
$this->installSchema('dblog', ['watchdog']);
|
||||
$this->installEntitySchema('user');
|
||||
/** @var \Drupal\user\RoleInterface[] $roles */
|
||||
$roles = [];
|
||||
// Create two arbitrary user roles.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$roles[$i] = Role::create([
|
||||
'id' => Unicode::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());
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
// Create a new form display without components.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
]);
|
||||
$form_display->save();
|
||||
|
||||
$dependencies = ['user.role.' . $roles[0]->id(), 'user.role.' . $roles[1]->id()];
|
||||
|
||||
// The config object should not depend on none of the two $roles.
|
||||
$this->assertNoDependency('config', $dependencies[0], $form_display);
|
||||
$this->assertNoDependency('config', $dependencies[1], $form_display);
|
||||
|
||||
// Add a widget of type 'test_field_widget'.
|
||||
$component = [
|
||||
'type' => 'test_field_widget',
|
||||
'settings' => [
|
||||
'test_widget_setting' => $this->randomString(),
|
||||
'role' => $roles[0]->id(),
|
||||
'role2' => $roles[1]->id(),
|
||||
],
|
||||
'third_party_settings' => [
|
||||
'color' => ['foo' => 'bar'],
|
||||
],
|
||||
];
|
||||
$form_display->setComponent($field_name, $component);
|
||||
$form_display->save();
|
||||
|
||||
// Now, the form display should depend on both user roles $roles.
|
||||
$this->assertDependency('config', $dependencies[0], $form_display);
|
||||
$this->assertDependency('config', $dependencies[1], $form_display);
|
||||
// The form display should depend on 'color' module.
|
||||
$this->assertDependency('module', 'color', $form_display);
|
||||
|
||||
// Delete the first user role entity.
|
||||
$roles[0]->delete();
|
||||
|
||||
// Reload the form display.
|
||||
$form_display = EntityFormDisplay::load($form_display->id());
|
||||
// The display exists.
|
||||
$this->assertFalse(empty($form_display));
|
||||
// The form display should not depend on $role[0] anymore.
|
||||
$this->assertNoDependency('config', $dependencies[0], $form_display);
|
||||
// The form display should depend on 'anonymous' user role.
|
||||
$this->assertDependency('config', 'user.role.anonymous', $form_display);
|
||||
// The form display should depend on 'color' module.
|
||||
$this->assertDependency('module', 'color', $form_display);
|
||||
|
||||
// Manually trigger the removal of configuration belonging to the module
|
||||
// because KernelTestBase::disableModules() is not aware of this.
|
||||
$this->container->get('config.manager')->uninstall('module', 'color');
|
||||
// Uninstall 'color' module.
|
||||
$this->disableModules(['color']);
|
||||
|
||||
// Reload the form display.
|
||||
$form_display = EntityFormDisplay::load($form_display->id());
|
||||
// The display exists.
|
||||
$this->assertFalse(empty($form_display));
|
||||
// The component is still enabled.
|
||||
$this->assertNotNull($form_display->getComponent($field_name));
|
||||
// The form display should not depend on 'color' module anymore.
|
||||
$this->assertNoDependency('module', 'color', $form_display);
|
||||
|
||||
// Delete the 2nd user role entity.
|
||||
$roles[1]->delete();
|
||||
|
||||
// Reload the form display.
|
||||
$form_display = EntityFormDisplay::load($form_display->id());
|
||||
// The display exists.
|
||||
$this->assertFalse(empty($form_display));
|
||||
// The component has been disabled.
|
||||
$this->assertNull($form_display->getComponent($field_name));
|
||||
$this->assertTrue($form_display->get('hidden')[$field_name]);
|
||||
// The correct warning message has been logged.
|
||||
$arguments = ['@display' => (string) t('Entity form display'), '@id' => $form_display->id(), '@name' => $field_name];
|
||||
$logged = (bool) Database::getConnection()->select('watchdog', 'w')
|
||||
->fields('w', ['wid'])
|
||||
->condition('type', 'system')
|
||||
->condition('message', "@display '@id': Component '@name' was disabled because its settings depend on removed dependencies.")
|
||||
->condition('variables', serialize($arguments))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
$this->assertTrue($logged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that $key is a $type type dependency of $display config entity.
|
||||
*
|
||||
* @param string $type
|
||||
* The dependency type: 'config', 'content', 'module' or 'theme'.
|
||||
* @param string $key
|
||||
* The string to be checked.
|
||||
* @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
|
||||
* The entity display object to get dependencies from.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertDependency($type, $key, EntityDisplayInterface $display) {
|
||||
return $this->assertDependencyHelper(TRUE, $type, $key, $display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that $key is not a $type type dependency of $display config entity.
|
||||
*
|
||||
* @param string $type
|
||||
* The dependency type: 'config', 'content', 'module' or 'theme'.
|
||||
* @param string $key
|
||||
* The string to be checked.
|
||||
* @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
|
||||
* The entity display object to get dependencies from.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertNoDependency($type, $key, EntityDisplayInterface $display) {
|
||||
return $this->assertDependencyHelper(FALSE, $type, $key, $display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a helper for dependency assertions.
|
||||
*
|
||||
* @param bool $assertion
|
||||
* Assertion: positive or negative.
|
||||
* @param string $type
|
||||
* The dependency type: 'config', 'content', 'module' or 'theme'.
|
||||
* @param string $key
|
||||
* The string to be checked.
|
||||
* @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
|
||||
* The entity display object to get dependencies from.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertDependencyHelper($assertion, $type, $key, EntityDisplayInterface $display) {
|
||||
$all_dependencies = $display->getDependencies();
|
||||
$dependencies = !empty($all_dependencies[$type]) ? $all_dependencies[$type] : [];
|
||||
$context = $display instanceof EntityViewDisplayInterface ? 'View' : 'Form';
|
||||
$value = $assertion ? in_array($key, $dependencies) : !in_array($key, $dependencies);
|
||||
$args = ['@context' => $context, '@id' => $display->id(), '@type' => $type, '@key' => $key];
|
||||
$message = $assertion ? new FormattableMarkup("@context display '@id' depends on @type '@key'.", $args) : new FormattableMarkup("@context display '@id' do not depend on @type '@key'.", $args);
|
||||
return $this->assert($value, $message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\field_ui\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityFormMode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the entity display configuration entities.
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class EntityFormDisplayTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['field_ui', 'field', 'entity_test', 'field_test', 'user', 'text'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity_get_form_display().
|
||||
*/
|
||||
public function testEntityGetFromDisplay() {
|
||||
// Check that entity_get_form_display() returns a fresh object when no
|
||||
// configuration entry exists.
|
||||
$form_display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertTrue($form_display->isNew());
|
||||
|
||||
// Add some components and save the display.
|
||||
$form_display->setComponent('component_1', array('weight' => 10))
|
||||
->save();
|
||||
|
||||
// Check that entity_get_form_display() returns the correct object.
|
||||
$form_display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertFalse($form_display->isNew());
|
||||
$this->assertEqual($form_display->id(), 'entity_test.entity_test.default');
|
||||
$this->assertEqual($form_display->getComponent('component_1'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field component within an EntityFormDisplay object.
|
||||
*/
|
||||
public function testFieldComponent() {
|
||||
// Create a field storage and a field.
|
||||
$field_name = 'test_field';
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$form_display = EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
// Check that providing no options results in default values being used.
|
||||
$form_display->setComponent($field_name);
|
||||
$field_type_info = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_storage->getType());
|
||||
$default_widget = $field_type_info['default_widget'];
|
||||
$widget_settings = \Drupal::service('plugin.manager.field.widget')->getDefaultSettings($default_widget);
|
||||
$expected = array(
|
||||
'weight' => 3,
|
||||
'type' => $default_widget,
|
||||
'settings' => $widget_settings,
|
||||
'third_party_settings' => array(),
|
||||
);
|
||||
$this->assertEqual($form_display->getComponent($field_name), $expected);
|
||||
|
||||
// Check that the getWidget() method returns the correct widget plugin.
|
||||
$widget = $form_display->getRenderer($field_name);
|
||||
$this->assertEqual($widget->getPluginId(), $default_widget);
|
||||
$this->assertEqual($widget->getSettings(), $widget_settings);
|
||||
|
||||
// Check that the widget is statically persisted, by assigning an
|
||||
// arbitrary property and reading it back.
|
||||
$random_value = $this->randomString();
|
||||
$widget->randomValue = $random_value;
|
||||
$widget = $form_display->getRenderer($field_name);
|
||||
$this->assertEqual($widget->randomValue, $random_value);
|
||||
|
||||
// Check that changing the definition creates a new widget.
|
||||
$form_display->setComponent($field_name, array(
|
||||
'type' => 'field_test_multiple',
|
||||
));
|
||||
$widget = $form_display->getRenderer($field_name);
|
||||
$this->assertEqual($widget->getPluginId(), 'test_field_widget');
|
||||
$this->assertFalse(isset($widget->randomValue));
|
||||
|
||||
// Check that specifying an unknown widget (e.g. case of a disabled module)
|
||||
// gets stored as is in the display, but results in the default widget being
|
||||
// used.
|
||||
$form_display->setComponent($field_name, array(
|
||||
'type' => 'unknown_widget',
|
||||
));
|
||||
$options = $form_display->getComponent($field_name);
|
||||
$this->assertEqual($options['type'], 'unknown_widget');
|
||||
$widget = $form_display->getRenderer($field_name);
|
||||
$this->assertEqual($widget->getPluginId(), $default_widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field component for a base field.
|
||||
*/
|
||||
public function testBaseFieldComponent() {
|
||||
$display = EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test_base_field_display',
|
||||
'bundle' => 'entity_test_base_field_display',
|
||||
'mode' => 'default',
|
||||
));
|
||||
|
||||
// Check that default options are correctly filled in.
|
||||
$formatter_settings = \Drupal::service('plugin.manager.field.widget')->getDefaultSettings('text_textfield');
|
||||
$expected = array(
|
||||
'test_no_display' => NULL,
|
||||
'test_display_configurable' => array(
|
||||
'type' => 'text_textfield',
|
||||
'settings' => $formatter_settings,
|
||||
'third_party_settings' => array(),
|
||||
'weight' => 10,
|
||||
),
|
||||
'test_display_non_configurable' => array(
|
||||
'type' => 'text_textfield',
|
||||
'settings' => $formatter_settings,
|
||||
'third_party_settings' => array(),
|
||||
'weight' => 11,
|
||||
),
|
||||
);
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
|
||||
// Check that saving the display only writes data for fields whose display
|
||||
// is configurable.
|
||||
$display->save();
|
||||
$config = $this->config('core.entity_form_display.' . $display->id());
|
||||
$data = $config->get();
|
||||
$this->assertFalse(isset($data['content']['test_no_display']));
|
||||
$this->assertFalse(isset($data['hidden']['test_no_display']));
|
||||
$this->assertEqual($data['content']['test_display_configurable'], $expected['test_display_configurable']);
|
||||
$this->assertFalse(isset($data['content']['test_display_non_configurable']));
|
||||
$this->assertFalse(isset($data['hidden']['test_display_non_configurable']));
|
||||
|
||||
// Check that defaults are correctly filled when loading the display.
|
||||
$display = EntityFormDisplay::load($display->id());
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
|
||||
// Check that data manually written for fields whose display is not
|
||||
// configurable is discarded when loading the display.
|
||||
$data['content']['test_display_non_configurable'] = $expected['test_display_non_configurable'];
|
||||
$data['content']['test_display_non_configurable']['weight']++;
|
||||
$config->setData($data)->save();
|
||||
$display = EntityFormDisplay::load($display->id());
|
||||
foreach ($expected as $field_name => $options) {
|
||||
$this->assertEqual($display->getComponent($field_name), $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting field.
|
||||
*/
|
||||
public function testDeleteField() {
|
||||
$field_name = 'test_field';
|
||||
// Create a field storage and a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Create default and compact entity display.
|
||||
EntityFormMode::create(array('id' => 'entity_test.compact', 'targetEntityType' => 'entity_test'))->save();
|
||||
EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
))->setComponent($field_name)->save();
|
||||
EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'compact',
|
||||
))->setComponent($field_name)->save();
|
||||
|
||||
// Check the component exists.
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertTrue($display->getComponent($field_name));
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'compact');
|
||||
$this->assertTrue($display->getComponent($field_name));
|
||||
|
||||
// Delete the field.
|
||||
$field->delete();
|
||||
|
||||
// Check that the component has been removed from the entity displays.
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'compact');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval().
|
||||
*/
|
||||
public function testOnDependencyRemoval() {
|
||||
$this->enableModules(array('field_plugins_test'));
|
||||
|
||||
$field_name = 'test_field';
|
||||
// Create a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'text'
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
EntityFormDisplay::create(array(
|
||||
'targetEntityType' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'mode' => 'default',
|
||||
))->setComponent($field_name, array('type' => 'field_plugins_test_text_widget'))->save();
|
||||
|
||||
// Check the component exists and is of the correct type.
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertEqual($display->getComponent($field_name)['type'], 'field_plugins_test_text_widget');
|
||||
|
||||
// Removing the field_plugins_test module should change the component to use
|
||||
// the default widget for test fields.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'field_plugins_test');
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertEqual($display->getComponent($field_name)['type'], 'text_textfield');
|
||||
|
||||
// Removing the text module should remove the field from the form display.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'text');
|
||||
$display = entity_get_form_display('entity_test', 'entity_test', 'default');
|
||||
$this->assertFalse($display->getComponent($field_name));
|
||||
}
|
||||
|
||||
}
|
72
web/core/modules/field_ui/tests/src/Unit/FieldUiTest.php
Normal file
72
web/core/modules/field_ui/tests/src/Unit/FieldUiTest.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\field_ui\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\field_ui\FieldUI;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\field_ui\FieldUI
|
||||
*
|
||||
* @group field_ui
|
||||
*/
|
||||
class FieldUiTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The path validator.
|
||||
*
|
||||
* @var \Drupal\Core\Path\PathValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $pathValidator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->pathValidator = $this->getMock('Drupal\Core\Path\PathValidatorInterface');
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('path.validator', $this->pathValidator);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getNextDestination
|
||||
*/
|
||||
public function testGetNextDestination() {
|
||||
$destinations = ['admin', 'admin/content'];
|
||||
$expected_uri = 'base:admin';
|
||||
$expected_query = [
|
||||
'destinations' => ['admin/content'],
|
||||
];
|
||||
$actual = FieldUI::getNextDestination($destinations);
|
||||
$this->assertSame($expected_uri, $actual->getUri());
|
||||
$this->assertSame($expected_query, $actual->getOption('query'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getNextDestination
|
||||
*/
|
||||
public function testGetNextDestinationEmpty() {
|
||||
$destinations = [];
|
||||
$actual = FieldUI::getNextDestination($destinations);
|
||||
$this->assertNull($actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getNextDestination
|
||||
*/
|
||||
public function testGetNextDestinationRouteName() {
|
||||
$destinations = [['route_name' => 'system.admin'], ['route_name' => 'system.admin_content']];
|
||||
$expected_route_name = 'system.admin';
|
||||
$expected_query = [
|
||||
'destinations' => [['route_name' => 'system.admin_content']],
|
||||
];
|
||||
$actual = FieldUI::getNextDestination($destinations);
|
||||
$this->assertSame($expected_route_name, $actual->getRouteName());
|
||||
$this->assertSame($expected_query, $actual->getOption('query'));
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue