Issue #1963340 by amateescu, dags, andypost, agentrickard, mgifford, yoroy, pguillard, jibran, YesCT, xjm, LewisNyman, swentel, Hydra, yched, tim.plunkett, rteijeiro, ainz, Xano, Bojhan, Berdir: Change field UI so that adding a field is a separate task
parent
7abdb6540b
commit
c02f12b7cb
|
@ -311,17 +311,16 @@ function comment_view_multiple($comments, $view_mode = 'full', $langcode = NULL)
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for field_ui_field_overview_form.
|
||||
* Implements hook_form_FORM_ID_alter() for field_ui_field_storage_add_form.
|
||||
*/
|
||||
function comment_form_field_ui_field_overview_form_alter(&$form, FormStateInterface $form_state) {
|
||||
function comment_form_field_ui_field_storage_add_form_alter(&$form, FormStateInterface $form_state) {
|
||||
$request = \Drupal::request();
|
||||
if ($form['#entity_type'] == 'comment' && $request->attributes->has('commented_entity_type')) {
|
||||
if ($form_state->get('entity_type_id') == 'comment' && $request->attributes->has('commented_entity_type')) {
|
||||
$form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($request->attributes->get('commented_entity_type'), $request->attributes->get('field_name'));
|
||||
}
|
||||
$entity_type_id = $form['#entity_type'];
|
||||
if (!_comment_entity_uses_integer_id($entity_type_id)) {
|
||||
if (!_comment_entity_uses_integer_id($form_state->get('entity_type_id'))) {
|
||||
// You cannot use comment fields on entity types with non-integer IDs.
|
||||
unset($form['fields']['_add_new_field']['type']['#options']['comment']);
|
||||
unset($form['add']['new_storage_type']['#options']['comment']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -408,24 +408,24 @@ class CommentNonNodeTest extends WebTestBase {
|
|||
'administer entity_test_string_id fields',
|
||||
));
|
||||
$this->drupalLogin($limited_user);
|
||||
// Visit the Field UI overview.
|
||||
$this->drupalGet('entity_test_string_id/structure/entity_test/fields');
|
||||
// Visit the Field UI field add page.
|
||||
$this->drupalGet('entity_test_string_id/structure/entity_test/fields/add-field');
|
||||
// Ensure field isn't shown for string IDs.
|
||||
$this->assertNoOption('edit-fields-add-new-field-type', 'comment');
|
||||
$this->assertNoOption('edit-new-storage-type', 'comment');
|
||||
// Ensure a core field type shown.
|
||||
$this->assertOption('edit-fields-add-new-field-type', 'boolean');
|
||||
$this->assertOption('edit-new-storage-type', 'boolean');
|
||||
|
||||
// Create a bundle for entity_test_no_id.
|
||||
entity_test_create_bundle('entity_test', 'Entity Test', 'entity_test_no_id');
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'administer entity_test_no_id fields',
|
||||
)));
|
||||
// Visit the Field UI overview.
|
||||
$this->drupalGet('entity_test_no_id/structure/entity_test/fields');
|
||||
// Visit the Field UI field add page.
|
||||
$this->drupalGet('entity_test_no_id/structure/entity_test/fields/add-field');
|
||||
// Ensure field isn't shown for empty IDs.
|
||||
$this->assertNoOption('edit-fields-add-new-field-type', 'comment');
|
||||
$this->assertNoOption('edit-new-storage-type', 'comment');
|
||||
// Ensure a core field type shown.
|
||||
$this->assertOption('edit-fields-add-new-field-type', 'boolean');
|
||||
$this->assertOption('edit-new-storage-type', 'boolean');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -247,6 +247,8 @@ class ContactSitewideTest extends WebTestBase {
|
|||
|
||||
$this->clickLink(t('Manage fields'), $i);
|
||||
$this->assertResponse(200);
|
||||
$this->clickLink(t('Add field'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Create a simple textfield.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\entity_reference\Tests;
|
||||
|
||||
use Drupal\field_ui\Tests\FieldUiTestTrait;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
|
@ -17,6 +18,8 @@ use Drupal\taxonomy\Entity\Vocabulary;
|
|||
*/
|
||||
class EntityReferenceAdminTest extends WebTestBase {
|
||||
|
||||
use FieldUiTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
|
@ -55,11 +58,11 @@ class EntityReferenceAdminTest extends WebTestBase {
|
|||
$bundle_path = 'admin/structure/types/manage/' . $this->type;
|
||||
|
||||
// First step: 'Add new field' on the 'Manage fields' page.
|
||||
$this->drupalPostForm($bundle_path . '/fields', array(
|
||||
'fields[_add_new_field][label]' => 'Test label',
|
||||
'fields[_add_new_field][field_name]' => 'test',
|
||||
'fields[_add_new_field][type]' => 'entity_reference',
|
||||
), t('Save'));
|
||||
$this->drupalPostForm($bundle_path . '/fields/add-field', array(
|
||||
'label' => 'Test label',
|
||||
'field_name' => 'test',
|
||||
'new_storage_type' => 'entity_reference',
|
||||
), t('Save and continue'));
|
||||
|
||||
// Node should be selected by default.
|
||||
$this->assertFieldByName('field_storage[settings][target_type]', 'node');
|
||||
|
@ -198,24 +201,13 @@ class EntityReferenceAdminTest extends WebTestBase {
|
|||
// Generate a random field name, must be only lowercase characters.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
|
||||
// Create the initial entity reference.
|
||||
$this->drupalPostForm($bundle_path . '/fields', array(
|
||||
'fields[_add_new_field][label]' => $this->randomMachineName(),
|
||||
'fields[_add_new_field][field_name]' => $field_name,
|
||||
'fields[_add_new_field][type]' => 'entity_reference',
|
||||
), t('Save'));
|
||||
|
||||
// Select the correct target type given in the parameters and save field settings.
|
||||
$this->drupalPostForm(NULL, array('field_storage[settings][target_type]' => $target_type), t('Save field settings'));
|
||||
|
||||
// Select required fields if there are any.
|
||||
$edit = array();
|
||||
$storage_edit = $field_edit = array();
|
||||
$storage_edit['field_storage[settings][target_type]'] = $target_type;
|
||||
if ($bundle) {
|
||||
$edit['field[settings][handler_settings][target_bundles][' . $bundle . ']'] = TRUE;
|
||||
$field_edit['field[settings][handler_settings][target_bundles][' . $bundle . ']'] = TRUE;
|
||||
}
|
||||
|
||||
// Save settings.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
$this->fieldUIAddNewField($bundle_path, $field_name, NULL, 'entity_reference', $storage_edit, $field_edit);
|
||||
|
||||
// Returns the generated field name.
|
||||
return $field_name;
|
||||
|
|
|
@ -3,37 +3,28 @@
|
|||
* Stylesheet for the Field UI module.
|
||||
*/
|
||||
|
||||
/* 'Manage fields' and 'Manage display' overviews */
|
||||
.field-ui-overview .add-new .label-input {
|
||||
float: left; /* LTR */
|
||||
/* Add new field page. */
|
||||
.field-ui-field-storage-add-form .field-type-wrapper .form-item {
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
[dir="rtl"] .field-ui-overview .add-new .label-input {
|
||||
[dir="rtl"] .field-ui-field-storage-add-form .field-type-wrapper .form-item {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
margin-right: 0;
|
||||
}
|
||||
.field-ui-overview .add-new .description {
|
||||
margin-bottom: 0;
|
||||
max-width: 250px;
|
||||
}
|
||||
.field-ui-overview .add-new .form-type-machine-name .description {
|
||||
white-space: normal;
|
||||
}
|
||||
.field-ui-overview .add-new .add-new-placeholder {
|
||||
font-weight: bold;
|
||||
padding-bottom: .5em;
|
||||
.field-ui-field-storage-add-form .field-type-wrapper .form-item-separator {
|
||||
margin-top: 2.3em;
|
||||
}
|
||||
|
||||
/* 'Manage fields' and 'Manage display' overviews */
|
||||
.field-ui-overview .region-title td {
|
||||
font-weight: bold;
|
||||
}
|
||||
.field-ui-overview .region-message td {
|
||||
font-style: italic;
|
||||
}
|
||||
.field-ui-overview .region-add-new-title {
|
||||
display: none;
|
||||
}
|
||||
.field-ui-overview .add-new td {
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 'Manage form display' and 'Manage display' overview */
|
||||
.field-ui-overview .field-plugin-summary-cell {
|
||||
|
|
|
@ -3,10 +3,53 @@
|
|||
* Attaches the behaviors for the Field UI module.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
Drupal.behaviors.fieldUIFieldStorageAddForm = {
|
||||
attach: function (context) {
|
||||
var $form = $(context).find('#field-ui-field-storage-add-form').once('field_ui_add');
|
||||
if ($form.length) {
|
||||
// Add a few '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(
|
||||
'.form-item-label label,' +
|
||||
'.form-item-field-name label,' +
|
||||
'.form-item-existing-storage-label label')
|
||||
.addClass('form-required');
|
||||
|
||||
var $newFieldType = $form.find('select[name="new_storage_type"]');
|
||||
var $existingStorageName = $form.find('select[name="existing_storage_name"]');
|
||||
|
||||
// When the user selects a new field type, clear the "existing field"
|
||||
// selection.
|
||||
$newFieldType.change(function () {
|
||||
if ($(this).val() != '') {
|
||||
// Reset the "existing storage name" selection.
|
||||
$existingStorageName.val('').change();
|
||||
}
|
||||
});
|
||||
|
||||
// When the user selects an existing storage name, clear the "new field
|
||||
// type" selection and populate the 'existing_storage_label' element.
|
||||
$existingStorageName.change(function () {
|
||||
if ($(this).val() != '') {
|
||||
// Reset the "new field type" selection.
|
||||
$newFieldType.val('').change();
|
||||
|
||||
// Pre-populate the "existing storage label" element.
|
||||
if (drupalSettings.existingFieldLabels[$(this).val()] !== undefined) {
|
||||
$(context).find('input[name="existing_storage_label"]').val(drupalSettings.existingFieldLabels[$(this).val()]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Drupal.behaviors.fieldUIDisplayOverview = {
|
||||
attach: function (context, settings) {
|
||||
$(context).find('table#field-display-overview').once('field-display-overview', function () {
|
||||
|
@ -249,4 +292,4 @@
|
|||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
|
|
|
@ -11,3 +11,7 @@ field_ui.entity_form_mode_add:
|
|||
weight: 1
|
||||
appears_on:
|
||||
- field_ui.entity_form_mode_list
|
||||
|
||||
field_ui.field_storage_config_add:
|
||||
class: \Drupal\Core\Menu\LocalActionDefault
|
||||
deriver: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalAction
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field_ui\Controller\FieldConfigListController.
|
||||
*/
|
||||
|
||||
namespace Drupal\field_ui\Controller;
|
||||
|
||||
use Drupal\Core\Entity\Controller\EntityListController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* 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 \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function listing($entity_type_id = NULL, $bundle = NULL, Request $request = NULL) {
|
||||
if (!$bundle) {
|
||||
$entity_info = $this->entityManager()->getDefinition($entity_type_id);
|
||||
$bundle = $request->attributes->get('_raw_variables')->get($entity_info->getBundleEntityType());
|
||||
}
|
||||
return $this->entityManager()->getListBuilder('field_config')->render($entity_type_id, $bundle, $request);
|
||||
}
|
||||
|
||||
}
|
|
@ -204,7 +204,7 @@ class DisplayOverview extends DisplayOverviewBase {
|
|||
*/
|
||||
protected function getOverviewRoute($mode) {
|
||||
return Url::fromRoute('field_ui.display_overview_view_mode_' . $this->entity_type, [
|
||||
$this->bundleEntityType => $this->bundle,
|
||||
$this->bundleEntityTypeId => $this->bundle,
|
||||
'view_mode_name' => $mode,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\field_ui;
|
|||
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\Display\EntityDisplayInterface;
|
||||
|
@ -16,13 +17,51 @@ use Drupal\Core\Entity\EntityManagerInterface;
|
|||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Field\PluginSettingsInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Field UI display overview base class.
|
||||
*/
|
||||
abstract class DisplayOverviewBase extends OverviewBase {
|
||||
abstract class DisplayOverviewBase extends FormBase {
|
||||
|
||||
/**
|
||||
* The name of the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entity_type = '';
|
||||
|
||||
/**
|
||||
* The entity bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = '';
|
||||
|
||||
/**
|
||||
* The name of the entity type which provides bundles for the entity type
|
||||
* defined above.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundleEntityTypeId;
|
||||
|
||||
/**
|
||||
* The entity view or form mode.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = '';
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The display context. Either 'view' or 'form'.
|
||||
|
@ -65,8 +104,7 @@ abstract class DisplayOverviewBase extends OverviewBase {
|
|||
* The configuration factory.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, PluginManagerBase $plugin_manager, ConfigFactoryInterface $config_factory) {
|
||||
parent::__construct($entity_manager);
|
||||
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->fieldTypes = $field_type_manager->getDefinitions();
|
||||
$this->pluginManager = $plugin_manager;
|
||||
$this->configFactory = $config_factory;
|
||||
|
@ -85,7 +123,23 @@ abstract class DisplayOverviewBase extends OverviewBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* 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(
|
||||
|
@ -101,6 +155,20 @@ abstract class DisplayOverviewBase extends OverviewBase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -118,8 +186,16 @@ abstract class DisplayOverviewBase extends OverviewBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL, $mode = 'default') {
|
||||
parent::buildForm($form, $form_state, $entity_type_id, $bundle);
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$this->bundleEntityTypeId = $entity_type->getBundleEntityType();
|
||||
|
||||
if (!$form_state->get('bundle')) {
|
||||
$bundle = $bundle ?: $this->getRequest()->attributes->get('_raw_variables')->get($this->bundleEntityTypeId);
|
||||
$form_state->set('bundle', $bundle);
|
||||
}
|
||||
|
||||
$this->entity_type = $entity_type_id;
|
||||
$this->bundle = $form_state->get('bundle');
|
||||
$this->mode = $mode;
|
||||
|
||||
$field_definitions = $this->getFieldDefinitions();
|
||||
|
@ -689,6 +765,120 @@ abstract class DisplayOverviewBase extends OverviewBase {
|
|||
return $form['fields'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-render tasks on field_ui_table elements.
|
||||
*
|
||||
* This function is assigned as a #pre_render callback in
|
||||
* field_ui_element_info().
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @see drupal_render()
|
||||
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
||||
*/
|
||||
public 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 = array('' => array('name' => '', 'children' => array()));
|
||||
$trees = array_fill_keys(array_keys($regions), $tree);
|
||||
|
||||
$parents = array();
|
||||
$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], array($parent)) : array();
|
||||
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] = array('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 = array(
|
||||
'#theme' => 'indentation',
|
||||
'#size' => $depth,
|
||||
);
|
||||
$row[$cell]['#prefix'] = drupal_render($indentation) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '');
|
||||
}
|
||||
|
||||
// Add row id and associate JS settings.
|
||||
$id = Html::getClass($name);
|
||||
$row['#attributes']['id'] = $id;
|
||||
if (isset($row['#js_settings'])) {
|
||||
$row['#js_settings'] += array(
|
||||
'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], array($this, '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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the rendering order of an array representing a tree.
|
||||
*
|
||||
* Callback for array_reduce() within
|
||||
* \Drupal\field_ui\DisplayOverviewBase::tablePreRender().
|
||||
*/
|
||||
public function reduceOrder($array, $a) {
|
||||
$array = !isset($array) ? array() : $array;
|
||||
if ($a['name']) {
|
||||
$array[] = $a['name'];
|
||||
}
|
||||
if (!empty($a['children'])) {
|
||||
uasort($a['children'], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
$array = array_merge($array, array_reduce($a['children'], array($this, 'reduceOrder')));
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity display object used by this form.
|
||||
*
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\String;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -18,6 +23,20 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -25,6 +44,13 @@ class FieldConfigListBuilder extends ConfigEntityListBuilder {
|
|||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new class instance.
|
||||
*
|
||||
|
@ -32,27 +58,103 @@ class FieldConfigListBuilder extends ConfigEntityListBuilder {
|
|||
* 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) {
|
||||
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'));
|
||||
return new static($entity_type, $container->get('entity.manager'), $container->get('plugin.manager.field.field_type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
// The actual field config overview is rendered by
|
||||
// \Drupal\field_ui\FieldOverview, so we should not use this class to build
|
||||
// lists.
|
||||
throw new \Exception('This class is only used for operations and not for building lists.');
|
||||
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['#attributes']['id'] = 'field-overview';
|
||||
$build['#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();
|
||||
$target_bundle_entity_type_id = $this->entityManager->getDefinition($this->targetEntityTypeId)->getBundleEntityType();
|
||||
$route_parameters = array(
|
||||
$target_bundle_entity_type_id => $this->targetBundle,
|
||||
'field_config' => $field_config->id(),
|
||||
);
|
||||
|
||||
$row = array(
|
||||
'id' => Html::getClass($field_config->getName()),
|
||||
'data' => array(
|
||||
'label' => String::checkPlain($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('field_ui.storage_edit_' . $this->targetEntityTypeId, $route_parameters),
|
||||
'#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Add the operations.
|
||||
$row['data'] = $row['data'] + parent::buildRow($field_config);
|
||||
|
||||
if (!empty($field_storage->locked)) {
|
||||
$row['data']['operations'] = array('data' => array('#markup' => $this->t('Locked')));
|
||||
$row['class'][] = 'menu-disabled';
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,537 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field_ui\FieldOverview.
|
||||
*/
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityListBuilderInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
use Drupal\field_ui\OverviewBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
|
||||
/**
|
||||
* Field UI field overview form.
|
||||
*/
|
||||
class FieldOverview extends OverviewBase {
|
||||
|
||||
/**
|
||||
* The field type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldOverview.
|
||||
*
|
||||
* @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(EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_manager);
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRegions() {
|
||||
return array(
|
||||
'content' => array(
|
||||
'title' => $this->t('Content'),
|
||||
'invisible' => TRUE,
|
||||
// @todo Bring back this message in https://drupal.org/node/1963340.
|
||||
//'message' => $this->t('No fields are present yet.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'field_ui_field_overview_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL) {
|
||||
parent::buildForm($form, $form_state, $entity_type_id, $bundle);
|
||||
|
||||
// Gather bundle information.
|
||||
$fields = array_filter(\Drupal::entityManager()->getFieldDefinitions($this->entity_type, $this->bundle), function ($field_definition) {
|
||||
return $field_definition instanceof FieldConfigInterface;
|
||||
});
|
||||
$field_types = $this->fieldTypeManager->getDefinitions();
|
||||
|
||||
// Field prefix.
|
||||
$field_prefix = \Drupal::config('field_ui.settings')->get('field_prefix');
|
||||
|
||||
$form += array(
|
||||
'#entity_type' => $this->entity_type,
|
||||
'#bundle' => $this->bundle,
|
||||
'#fields' => array_keys($fields),
|
||||
);
|
||||
|
||||
$table = array(
|
||||
'#type' => 'field_ui_table',
|
||||
'#tree' => TRUE,
|
||||
'#header' => array(
|
||||
$this->t('Label'),
|
||||
array(
|
||||
'data' => $this->t('Machine name'),
|
||||
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
|
||||
),
|
||||
$this->t('Field type'),
|
||||
$this->t('Operations'),
|
||||
),
|
||||
'#regions' => $this->getRegions(),
|
||||
'#attributes' => array(
|
||||
'class' => array('field-ui-overview'),
|
||||
'id' => 'field-overview',
|
||||
),
|
||||
);
|
||||
|
||||
// Fields.
|
||||
foreach ($fields as $name => $field) {
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
$route_parameters = array(
|
||||
$this->bundleEntityType => $this->bundle,
|
||||
'field_config' => $field->id(),
|
||||
);
|
||||
$table[$name] = array(
|
||||
'#attributes' => array(
|
||||
'id' => Html::getClass($name),
|
||||
),
|
||||
'label' => array(
|
||||
'#markup' => String::checkPlain($field->getLabel()),
|
||||
),
|
||||
'field_name' => array(
|
||||
'#markup' => $field->getName(),
|
||||
),
|
||||
'type' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => $field_types[$field_storage->getType()]['label'],
|
||||
'#url' => Url::fromRoute('field_ui.storage_edit_' . $this->entity_type, $route_parameters),
|
||||
'#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))),
|
||||
),
|
||||
);
|
||||
|
||||
$table[$name]['operations']['data'] = array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $this->entityManager->getListBuilder('field_config')->getOperations($field),
|
||||
);
|
||||
|
||||
if (!empty($field_storage->locked)) {
|
||||
$table[$name]['operations'] = array('#markup' => $this->t('Locked'));
|
||||
$table[$name]['#attributes']['class'][] = 'menu-disabled';
|
||||
}
|
||||
}
|
||||
|
||||
// Gather valid field types.
|
||||
$field_type_options = array();
|
||||
foreach ($field_types as $name => $field_type) {
|
||||
// Skip field types which should not be added via user interface.
|
||||
if (empty($field_type['no_ui'])) {
|
||||
$field_type_options[$name] = $field_type['label'];
|
||||
}
|
||||
}
|
||||
asort($field_type_options);
|
||||
|
||||
// Additional row: add new field.
|
||||
if ($field_type_options) {
|
||||
$name = '_add_new_field';
|
||||
$table[$name] = array(
|
||||
'#attributes' => array('class' => array('add-new')),
|
||||
'label' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('New field label'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 15,
|
||||
'#description' => $this->t('Label'),
|
||||
'#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . $this->t('Add new field') .'</div>',
|
||||
'#suffix' => '</div>',
|
||||
),
|
||||
'field_name' => array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => $this->t('New field name'),
|
||||
'#title_display' => 'invisible',
|
||||
// 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),
|
||||
'#prefix' => '<div class="add-new-placeholder"> </div>',
|
||||
'#machine_name' => array(
|
||||
'source' => array('fields', $name, 'label'),
|
||||
'exists' => array($this, 'fieldNameExists'),
|
||||
'standalone' => TRUE,
|
||||
'label' => '',
|
||||
),
|
||||
'#required' => FALSE,
|
||||
),
|
||||
'type' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Type of new field'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $field_type_options,
|
||||
'#empty_option' => $this->t('- Select a field type -'),
|
||||
'#description' => $this->t('Type of data to store.'),
|
||||
'#attributes' => array('class' => array('field-type-select')),
|
||||
'#cell_attributes' => array('colspan' => 2),
|
||||
'#prefix' => '<div class="add-new-placeholder"> </div>',
|
||||
),
|
||||
// Place the 'translatable' property as an explicit value so that
|
||||
// contrib modules can form_alter() the value for newly created fields.
|
||||
'translatable' => array(
|
||||
'#type' => 'value',
|
||||
'#value' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Additional row: re-use existing field storages.
|
||||
$existing_fields = $this->getExistingFieldStorageOptions();
|
||||
if ($existing_fields) {
|
||||
// Build list of options.
|
||||
$existing_field_options = array();
|
||||
foreach ($existing_fields as $field_name => $info) {
|
||||
$text = $this->t('@type: @field', array(
|
||||
'@type' => $info['type_label'],
|
||||
'@field' => $info['field'],
|
||||
));
|
||||
$existing_field_options[$field_name] = Unicode::truncate($text, 80, FALSE, TRUE);
|
||||
}
|
||||
asort($existing_field_options);
|
||||
$name = '_add_existing_field';
|
||||
$table[$name] = array(
|
||||
'#attributes' => array('class' => array('add-new')),
|
||||
'#row_type' => 'add_new_field',
|
||||
'#region_callback' => array($this, 'getRowRegion'),
|
||||
'label' => array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Existing field label'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 15,
|
||||
'#description' => $this->t('Label'),
|
||||
'#attributes' => array('class' => array('label-textfield')),
|
||||
'#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . $this->t('Re-use existing field') .'</div>',
|
||||
'#suffix' => '</div>',
|
||||
),
|
||||
'field_name' => array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Existing field to share'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $existing_field_options,
|
||||
'#empty_option' => $this->t('- Select an existing field -'),
|
||||
'#description' => $this->t('Field to share'),
|
||||
'#attributes' => array('class' => array('field-select')),
|
||||
'#cell_attributes' => array('colspan' => 3),
|
||||
'#prefix' => '<div class="add-new-placeholder"> </div>',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// We can set the 'rows_order' element, needed by theme_field_ui_table(),
|
||||
// here instead of a #pre_render callback because this form doesn't have the
|
||||
// tabledrag behavior anymore.
|
||||
$table['#regions']['content']['rows_order'] = array();
|
||||
foreach (Element::children($table) as $name) {
|
||||
$table['#regions']['content']['rows_order'][] = $name;
|
||||
}
|
||||
|
||||
$form['fields'] = $table;
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#button_type' => 'primary',
|
||||
'#value' => $this->t('Save'));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->validateAddNew($form, $form_state);
|
||||
$this->validateAddExisting($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 'add new field' row.
|
||||
*
|
||||
* @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\FieldOverview::validateForm()
|
||||
*/
|
||||
protected function validateAddNew(array $form, FormStateInterface $form_state) {
|
||||
$field = $form_state->getValue(array('fields', '_add_new_field'));
|
||||
|
||||
// Validate if any information was provided in the 'add new field' row.
|
||||
if (array_filter(array($field['label'], $field['field_name'], $field['type']))) {
|
||||
// Missing label.
|
||||
if (!$field['label']) {
|
||||
$form_state->setErrorByName('fields][_add_new_field][label', $this->t('Add new field: you need to provide a label.'));
|
||||
}
|
||||
|
||||
// Missing field name.
|
||||
if (!$field['field_name']) {
|
||||
$form_state->setErrorByName('fields][_add_new_field][field_name', $this->t('Add new field: you need to provide a machine name for the field.'));
|
||||
}
|
||||
// Field name validation.
|
||||
else {
|
||||
$field_name = $field['field_name'];
|
||||
|
||||
// Add the field prefix.
|
||||
$field_name = \Drupal::config('field_ui.settings')->get('field_prefix') . $field_name;
|
||||
$form_state->setValueForElement($form['fields']['_add_new_field']['field_name'], $field_name);
|
||||
}
|
||||
|
||||
// Missing field type.
|
||||
if (!$field['type']) {
|
||||
$form_state->setErrorByName('fields][_add_new_field][type', $this->t('Add new field: you need to select a field type.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the 're-use existing field' row.
|
||||
*
|
||||
* @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\FieldOverview::validate()
|
||||
*/
|
||||
protected function validateAddExisting(array $form, FormStateInterface $form_state) {
|
||||
// The form element might be absent if no existing fields can be added to
|
||||
// this bundle.
|
||||
if ($field = $form_state->getValue(array('fields', '_add_existing_field'))) {
|
||||
// Validate if any information was provided in the
|
||||
// 're-use existing field' row.
|
||||
if (array_filter(array($field['label'], $field['field_name']))) {
|
||||
// Missing label.
|
||||
if (!$field['label']) {
|
||||
$form_state->setErrorByName('fields][_add_existing_field][label', $this->t('Re-use existing field: you need to provide a label.'));
|
||||
}
|
||||
|
||||
// Missing existing field name.
|
||||
if (!$field['field_name']) {
|
||||
$form_state->setErrorByName('fields][_add_existing_field][field_name', $this->t('Re-use existing field: you need to select a field.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\field_ui\OverviewBase::submitForm().
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$error = FALSE;
|
||||
$form_values = $form_state->getValue('fields');
|
||||
$destinations = array();
|
||||
|
||||
// Create new field.
|
||||
if (!empty($form_values['_add_new_field']['field_name'])) {
|
||||
$values = $form_values['_add_new_field'];
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => $values['field_name'],
|
||||
'entity_type' => $this->entity_type,
|
||||
'type' => $values['type'],
|
||||
'translatable' => $values['translatable'],
|
||||
);
|
||||
$field = array(
|
||||
'field_name' => $values['field_name'],
|
||||
'entity_type' => $this->entity_type,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $values['label'],
|
||||
// Field translatability should be explicitly enabled by the users.
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
|
||||
// Create the field storage and field.
|
||||
try {
|
||||
$this->entityManager->getStorage('field_storage_config')->create($field_storage)->save();
|
||||
$new_field = $this->entityManager->getStorage('field_config')->create($field);
|
||||
$new_field->save();
|
||||
|
||||
// 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.
|
||||
entity_get_form_display($this->entity_type, $this->bundle, 'default')
|
||||
->setComponent($values['field_name'])
|
||||
->save();
|
||||
|
||||
// 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.
|
||||
entity_get_display($this->entity_type, $this->bundle, 'default')
|
||||
->setComponent($values['field_name'])
|
||||
->save();
|
||||
|
||||
// Always show the field settings step, as the cardinality needs to be
|
||||
// configured for new fields.
|
||||
$route_parameters = array(
|
||||
$this->bundleEntityType => $this->bundle,
|
||||
'field_config' => $new_field->id(),
|
||||
);
|
||||
$destinations[] = array('route_name' => 'field_ui.storage_edit_' . $this->entity_type, 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => 'field_ui.field_edit_' . $this->entity_type, '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' => $field['label'], '!message' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Re-use existing field.
|
||||
if (!empty($form_values['_add_existing_field']['field_name'])) {
|
||||
$values = $form_values['_add_existing_field'];
|
||||
$field_name = $values['field_name'];
|
||||
$field_storage = FieldStorageConfig::loadByName($this->entity_type, $field_name);
|
||||
if (!empty($field_storage->locked)) {
|
||||
drupal_set_message($this->t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error');
|
||||
}
|
||||
else {
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $this->entity_type,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $values['label'],
|
||||
);
|
||||
|
||||
try {
|
||||
$new_field = $this->entityManager->getStorage('field_config')->create($field);
|
||||
$new_field->save();
|
||||
|
||||
// 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.
|
||||
entity_get_form_display($this->entity_type, $this->bundle, 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// 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.
|
||||
entity_get_display($this->entity_type, $this->bundle, 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
$destinations[] = array(
|
||||
'route_name' => 'field_ui.field_edit_' . $this->entity_type,
|
||||
'route_parameters' => array(
|
||||
$this->bundleEntityType => $this->bundle,
|
||||
'field_config' => $new_field->id(),
|
||||
),
|
||||
);
|
||||
// Store new field information for any additional submit handlers.
|
||||
$form_state->set(['fields_added', '_add_existing_field'], $field['field_name']);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$error = TRUE;
|
||||
drupal_set_message($this->t('There was a problem creating field %label: @message.', array('%label' => $field['label'], '@message' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($destinations) {
|
||||
$destination = drupal_get_destination();
|
||||
$destinations[] = $destination['destination'];
|
||||
$form_state->setRedirectUrl(FieldUI::getNextDestination($destinations, $form_state));
|
||||
}
|
||||
elseif (!$error) {
|
||||
drupal_set_message($this->t('Your settings have been saved.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->fieldTypeManager->getDefinitions();
|
||||
foreach ($this->entityManager->getFieldStorageDefinitions($this->entity_type) 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] = array(
|
||||
'type' => $field_type,
|
||||
'type_label' => $field_types[$field_type]['label'],
|
||||
'field' => $field_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field machine name is taken.
|
||||
*
|
||||
* @param string $value
|
||||
* The machine name, not prefixed.
|
||||
*
|
||||
* @return bool
|
||||
* Whether or not the field machine name is taken.
|
||||
*/
|
||||
public function fieldNameExists($value) {
|
||||
// Add the field prefix.
|
||||
$field_name = \Drupal::config('field_ui.settings')->get('field_prefix') . $value;
|
||||
|
||||
$field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($this->entity_type);
|
||||
return isset($field_storage_definitions[$field_name]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,524 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field_ui\Form\FieldStorageAddForm.
|
||||
*/
|
||||
|
||||
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 name of the entity type which provides bundles for the entity type
|
||||
* defined above.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundleEntityTypeId;
|
||||
|
||||
/**
|
||||
* 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\QueryFactoryInterface
|
||||
*/
|
||||
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\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
|
||||
* The entity query 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) {
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$this->bundleEntityTypeId = $entity_type->getBundleEntityType();
|
||||
|
||||
if (!$form_state->get('entity_type_id')) {
|
||||
$form_state->set('entity_type_id', $entity_type_id);
|
||||
}
|
||||
if (!$form_state->get('bundle')) {
|
||||
$bundle = $bundle ?: $this->getRequest()->attributes->get('_raw_variables')->get($this->bundleEntityTypeId);
|
||||
$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->getDefinitions() as $name => $field_type) {
|
||||
// Skip field types which should not be added via user interface.
|
||||
if (empty($field_type['no_ui'])) {
|
||||
$field_type_options[$name] = $field_type['label'];
|
||||
}
|
||||
}
|
||||
asort($field_type_options);
|
||||
|
||||
$form['add'] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array('class' => array('field-type-wrapper', '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.
|
||||
$form['translatable'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => FALSE,
|
||||
);
|
||||
|
||||
$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();
|
||||
|
||||
// Create new field.
|
||||
if ($values['new_storage_type']) {
|
||||
// Create the field storage and field.
|
||||
try {
|
||||
$this->entityManager->getStorage('field_storage_config')->create(array(
|
||||
'field_name' => $values['field_name'],
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => $values['new_storage_type'],
|
||||
'translatable' => $values['translatable'],
|
||||
))->save();
|
||||
|
||||
$field = $this->entityManager->getStorage('field_config')->create(array(
|
||||
'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,
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$this->configureEntityDisplays($values['field_name']);
|
||||
|
||||
// Always show the field settings step, as the cardinality needs to be
|
||||
// configured for new fields.
|
||||
$route_parameters = array(
|
||||
$this->bundleEntityTypeId => $this->bundle,
|
||||
'field_config' => $field->id(),
|
||||
);
|
||||
$destinations[] = array('route_name' => 'field_ui.storage_edit_' . $this->entityTypeId, 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => 'field_ui.field_edit_' . $this->entityTypeId, 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => 'field_ui.overview_' . $this->entityTypeId, '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->configureEntityDisplays($field_name);
|
||||
|
||||
$route_parameters = array(
|
||||
$this->bundleEntityTypeId => $this->bundle,
|
||||
'field_config' => $field->id(),
|
||||
);
|
||||
$destinations[] = array('route_name' => 'field_ui.field_edit_' . $this->entityTypeId, 'route_parameters' => $route_parameters);
|
||||
$destinations[] = array('route_name' => 'field_ui.overview_' . $this->entityTypeId, '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 = drupal_get_destination();
|
||||
$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.
|
||||
*/
|
||||
protected function configureEntityDisplays($field_name) {
|
||||
// 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.
|
||||
entity_get_form_display($this->entityTypeId, $this->bundle, 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// 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.
|
||||
entity_get_display($this->entityTypeId, $this->bundle, 'default')
|
||||
->setComponent($field_name)
|
||||
->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]);
|
||||
}
|
||||
|
||||
}
|
|
@ -169,7 +169,7 @@ class FormDisplayOverview extends DisplayOverviewBase {
|
|||
*/
|
||||
protected function getOverviewRoute($mode) {
|
||||
return Url::fromRoute('field_ui.form_display_overview_form_mode_' . $this->entity_type, [
|
||||
$this->bundleEntityType => $this->bundle,
|
||||
$this->bundleEntityTypeId => $this->bundle,
|
||||
'form_mode_name' => $mode,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field_ui\OverviewBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\field_ui;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Abstract base class for Field UI overview forms.
|
||||
*/
|
||||
abstract class OverviewBase extends FormBase {
|
||||
|
||||
/**
|
||||
* The name of the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entity_type = '';
|
||||
|
||||
/**
|
||||
* The entity bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = '';
|
||||
|
||||
/**
|
||||
* The entity type of the entity bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundleEntityType;
|
||||
|
||||
/**
|
||||
* The entity view or form mode.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = '';
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new OverviewBase.
|
||||
*
|
||||
* @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, $entity_type_id = NULL, $bundle = NULL) {
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$this->bundleEntityType = $entity_type->getBundleEntityType();
|
||||
$stored_bundle = $form_state->get('bundle');
|
||||
if (!$stored_bundle) {
|
||||
if (!$bundle) {
|
||||
$bundle = $this->getRequest()->attributes->get('_raw_variables')->get($this->bundleEntityType);
|
||||
}
|
||||
$stored_bundle = $bundle;
|
||||
$form_state->set('bundle', $bundle);
|
||||
}
|
||||
|
||||
$this->entity_type = $entity_type_id;
|
||||
$this->bundle = $stored_bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract public function getRegions();
|
||||
|
||||
/**
|
||||
* Returns an associative array of all regions.
|
||||
*/
|
||||
public function getRegionOptions() {
|
||||
$options = array();
|
||||
foreach ($this->getRegions() as $region => $data) {
|
||||
$options[$region] = $data['title'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs pre-render tasks on field_ui_table elements.
|
||||
*
|
||||
* This function is assigned as a #pre_render callback in
|
||||
* field_ui_element_info().
|
||||
*
|
||||
* @param array $element
|
||||
* 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.
|
||||
*
|
||||
* @see drupal_render()
|
||||
* @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
||||
*/
|
||||
public 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 = array('' => array('name' => '', 'children' => array()));
|
||||
$trees = array_fill_keys(array_keys($regions), $tree);
|
||||
|
||||
$parents = array();
|
||||
$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], array($parent)) : array();
|
||||
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] = array('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 = array(
|
||||
'#theme' => 'indentation',
|
||||
'#size' => $depth,
|
||||
);
|
||||
$row[$cell]['#prefix'] = drupal_render($indentation) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '');
|
||||
}
|
||||
|
||||
// Add row id and associate JS settings.
|
||||
$id = Html::getClass($name);
|
||||
$row['#attributes']['id'] = $id;
|
||||
if (isset($row['#js_settings'])) {
|
||||
$row['#js_settings'] += array(
|
||||
'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], array($this, '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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the rendering order of an array representing a tree.
|
||||
*
|
||||
* Callback for array_reduce() within
|
||||
* \Drupal\field_ui\OverviewBase::tablePreRender().
|
||||
*/
|
||||
public function reduceOrder($array, $a) {
|
||||
$array = !isset($array) ? array() : $array;
|
||||
if ($a['name']) {
|
||||
$array[] = $a['name'];
|
||||
}
|
||||
if (!empty($a['children'])) {
|
||||
uasort($a['children'], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
|
||||
$array = array_merge($array, array_reduce($a['children'], array($this, 'reduceOrder')));
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @fie
|
||||
* Contains \Drupal\field_ui\Plugin\Derivative\FieldUiLocalAction.
|
||||
*/
|
||||
|
||||
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("field_ui.overview_$entity_type_id"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->derivatives as &$entry) {
|
||||
$entry += $base_plugin_definition;
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
|
@ -90,7 +90,7 @@ class RouteSubscriber extends RouteSubscriberBase {
|
|||
$route = new Route(
|
||||
"$path/fields",
|
||||
array(
|
||||
'_form' => '\Drupal\field_ui\FieldOverview',
|
||||
'_controller' => '\Drupal\field_ui\Controller\FieldConfigListController::listing',
|
||||
'_title' => 'Manage fields',
|
||||
) + $defaults,
|
||||
array('_permission' => 'administer ' . $entity_type_id . ' fields'),
|
||||
|
@ -98,6 +98,16 @@ class RouteSubscriber extends RouteSubscriberBase {
|
|||
);
|
||||
$collection->add("field_ui.overview_$entity_type_id", $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')
|
||||
);
|
||||
$collection->add("field_ui.field_storage_config_add_$entity_type_id", $route);
|
||||
|
||||
$route = new Route(
|
||||
"$path/form-display",
|
||||
array(
|
||||
|
|
|
@ -19,7 +19,7 @@ class FieldUIRouteTest extends WebTestBase {
|
|||
/**
|
||||
* Modules to enable.
|
||||
*/
|
||||
public static $modules = array('field_ui_test');
|
||||
public static $modules = array('entity_test', 'field_ui');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -34,10 +34,8 @@ class FieldUIRouteTest extends WebTestBase {
|
|||
* Ensures that entity types with bundles do not break following entity types.
|
||||
*/
|
||||
public function testFieldUIRoutes() {
|
||||
$this->drupalGet('field-ui-test-no-bundle/manage/fields');
|
||||
// @todo Bring back this assertion in https://drupal.org/node/1963340.
|
||||
// @see \Drupal\field_ui\FieldOverview::getRegions()
|
||||
//$this->assertText('No fields are present yet.');
|
||||
$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');
|
||||
|
|
|
@ -34,19 +34,19 @@ trait FieldUiTestTrait {
|
|||
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(
|
||||
'fields[_add_new_field][field_name]' => $field_name,
|
||||
'fields[_add_new_field][type]' => $field_type,
|
||||
'fields[_add_new_field][label]' => $label,
|
||||
'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";
|
||||
$bundle_path = "$bundle_path/fields/add-field";
|
||||
}
|
||||
|
||||
// First step : 'Add new field' on the 'Manage fields' page.
|
||||
$this->drupalPostForm($bundle_path, $initial_edit, t('Save'));
|
||||
// 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.');
|
||||
|
@ -68,7 +68,7 @@ trait FieldUiTestTrait {
|
|||
*
|
||||
* @param string $bundle_path
|
||||
* Admin path of the bundle that the field is to be attached to.
|
||||
* @param string $existing_field_name
|
||||
* @param string $existing_storage_name
|
||||
* The name of the existing field storage for which we want to add a new
|
||||
* field.
|
||||
* @param string $label
|
||||
|
@ -77,15 +77,15 @@ trait FieldUiTestTrait {
|
|||
* (optional) $edit parameter for drupalPostForm() on the second step
|
||||
* ('Field settings' form).
|
||||
*/
|
||||
public function fieldUIAddExistingField($bundle_path, $existing_field_name, $label = NULL, array $field_edit = array()) {
|
||||
public function fieldUIAddExistingField($bundle_path, $existing_storage_name, $label = NULL, array $field_edit = array()) {
|
||||
$label = $label ?: $this->randomString();
|
||||
$initial_edit = array(
|
||||
'fields[_add_existing_field][label]' => $label,
|
||||
'fields[_add_existing_field][field_name]' => $existing_field_name,
|
||||
'existing_storage_name' => $existing_storage_name,
|
||||
'existing_storage_label' => $label,
|
||||
);
|
||||
|
||||
// First step : 'Re-use existing field' on the 'Manage fields' page.
|
||||
$this->drupalPostForm("$bundle_path/fields", $initial_edit, t('Save'));
|
||||
// First step: 'Re-use existing field' on the 'Add field' page.
|
||||
$this->drupalPostForm("$bundle_path/fields/add-field", $initial_edit, t('Save and continue'));
|
||||
$this->assertNoRaw('&lt;', 'The page does not have double escaped HTML tags.');
|
||||
|
||||
// Second step: 'Field settings' form.
|
||||
|
|
|
@ -118,11 +118,8 @@ class ManageFieldsTest extends WebTestBase {
|
|||
$this->assertRaw($table_header . '</th>', format_string('%table_header table header was found.', array('%table_header' => $table_header)));
|
||||
}
|
||||
|
||||
// "Add new field" and "Re-use existing field" aren't a table heading so just
|
||||
// test the text.
|
||||
foreach (array('Add new field', 'Re-use existing field') as $element) {
|
||||
$this->assertText($element, format_string('"@element" was found.', array('@element' => $element)));
|
||||
}
|
||||
// Test the "Add field" action link.
|
||||
$this->assertLink('Add field');
|
||||
|
||||
// Assert entity operations for all fields.
|
||||
$result = $this->xpath('//ul[@class = "dropbutton"]/li/a');
|
||||
|
@ -178,12 +175,14 @@ class ManageFieldsTest extends WebTestBase {
|
|||
*/
|
||||
function addExistingField() {
|
||||
// Check "Re-use existing field" appears.
|
||||
$this->drupalGet('admin/structure/types/manage/page/fields');
|
||||
$this->assertRaw(t('Re-use existing field'), '"Re-use existing field" was found.');
|
||||
$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-add-existing-field-field-name"]//option[@value="comment"]'), 'The list of options respects entity type restrictions.');
|
||||
$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->field_name)), '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->field_name, $this->field_label . '_2');
|
||||
|
@ -254,8 +253,8 @@ class ManageFieldsTest extends WebTestBase {
|
|||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
}
|
||||
// Check "Re-use existing field" appears.
|
||||
$this->drupalGet('admin/structure/types/manage/page/fields');
|
||||
$this->assertRaw(t('Re-use existing field'), '"Re-use existing field" was found.');
|
||||
$this->drupalGet('admin/structure/types/manage/page/fields/add-field');
|
||||
$this->assertRaw(t('Re-use an existing field'), '"Re-use existing field" was found.');
|
||||
// Add a new field for the orphaned storage.
|
||||
$this->fieldUIAddExistingField("admin/structure/types/manage/page", $this->field_name);
|
||||
}
|
||||
|
@ -296,11 +295,11 @@ class ManageFieldsTest extends WebTestBase {
|
|||
|
||||
// Try to create the field.
|
||||
$edit = array(
|
||||
'fields[_add_new_field][label]' => $field_exceed_max_length_label,
|
||||
'fields[_add_new_field][field_name]' => $field_exceed_max_length_input,
|
||||
'label' => $field_exceed_max_length_label,
|
||||
'field_name' => $field_exceed_max_length_input,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/fields', $edit, t('Save'));
|
||||
$this->assertText('New field name cannot be longer than 22 characters but is currently 23 characters long.');
|
||||
$this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/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->type, $this->field_name_input, $this->field_label);
|
||||
|
@ -427,20 +426,20 @@ class ManageFieldsTest extends WebTestBase {
|
|||
|
||||
$label = 'Disallowed field';
|
||||
$edit = array(
|
||||
'fields[_add_new_field][label]' => $label,
|
||||
'fields[_add_new_field][type]' => 'test_field',
|
||||
'label' => $label,
|
||||
'new_storage_type' => 'test_field',
|
||||
);
|
||||
|
||||
// Try with an entity key.
|
||||
$edit['fields[_add_new_field][field_name]'] = 'title';
|
||||
$edit['field_name'] = 'title';
|
||||
$bundle_path = 'admin/structure/types/manage/' . $this->type;
|
||||
$this->drupalPostForm("$bundle_path/fields", $edit, t('Save'));
|
||||
$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['fields[_add_new_field][field_name]'] = 'sticky';
|
||||
$edit['field_name'] = 'sticky';
|
||||
$bundle_path = 'admin/structure/types/manage/' . $this->type;
|
||||
$this->drupalPostForm("$bundle_path/fields", $edit, t('Save'));
|
||||
$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.'));
|
||||
}
|
||||
|
||||
|
@ -485,11 +484,10 @@ class ManageFieldsTest extends WebTestBase {
|
|||
* Tests that Field UI respects the 'no_ui' flag in the field type definition.
|
||||
*/
|
||||
function testHiddenFields() {
|
||||
$bundle_path = 'admin/structure/types/manage/' . $this->type . '/fields/';
|
||||
|
||||
// Check that the field type is not available in the 'add new field' row.
|
||||
$this->drupalGet($bundle_path);
|
||||
$this->assertFalse($this->xpath('//select[@id="edit-fields-add-new-field-type"]//option[@value="hidden_test_field"]'), "The 'add new field' select respects field types 'no_ui' property.");
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->type . '/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';
|
||||
|
@ -512,23 +510,23 @@ class ManageFieldsTest extends WebTestBase {
|
|||
|
||||
// Check that the newly added field appears on the 'Manage Fields'
|
||||
// screen.
|
||||
$this->drupalGet($bundle_path);
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->type . '/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.
|
||||
$bundle_path = 'admin/structure/types/manage/article/fields/';
|
||||
$this->drupalGet($bundle_path);
|
||||
$this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), "The 're-use existing field' select respects field types 'no_ui' property.");
|
||||
$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-fields-add-new-field-type"]//option[@value=:field_type]', array(':field_type' => $field_type)), String::format('Configurable field type @field_type is available.', array('@field_type' => $field_type)));
|
||||
$this->assertTrue($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', array(':field_type' => $field_type)), String::format('Configurable field type @field_type is available.', array('@field_type' => $field_type)));
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($this->xpath('//select[@id="edit-fields-add-new-field-type"]//option[@value=:field_type]', array(':field_type' => $field_type)), String::format('Non-configurable field type @field_type is not available.', array('@field_type' => $field_type)));
|
||||
$this->assertFalse($this->xpath('//select[@id="edit-new-storage-type"]//option[@value=:field_type]', array(':field_type' => $field_type)), String::format('Non-configurable field type @field_type is not available.', array('@field_type' => $field_type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -553,12 +551,12 @@ class ManageFieldsTest extends WebTestBase {
|
|||
// field_tags already exists, so we're expecting an error when trying to
|
||||
// create a new field with the same name.
|
||||
$edit = array(
|
||||
'fields[_add_new_field][field_name]' => 'tags',
|
||||
'fields[_add_new_field][label]' => $this->randomMachineName(),
|
||||
'fields[_add_new_field][type]' => 'taxonomy_term_reference',
|
||||
'field_name' => 'tags',
|
||||
'label' => $this->randomMachineName(),
|
||||
'new_storage_type' => 'taxonomy_term_reference',
|
||||
);
|
||||
$url = 'admin/structure/types/manage/' . $this->type . '/fields';
|
||||
$this->drupalPostForm($url, $edit, t('Save'));
|
||||
$url = 'admin/structure/types/manage/' . $this->type . '/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.');
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
name: "Field UI tests"
|
||||
type: module
|
||||
description: "Support module for Field UI testing."
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
|
||||
dependencies:
|
||||
- field_ui
|
||||
- entity_test
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field_ui_test\Entity\FieldUITestNoBundle.
|
||||
*/
|
||||
|
||||
namespace Drupal\field_ui_test\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Defines the test Field UI class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "field_ui_test_no_bundle",
|
||||
* label = @Translation("Test Field UI entity, no bundle"),
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FieldUITestNoBundle extends EntityTest {
|
||||
|
||||
}
|
Loading…
Reference in New Issue