diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_no_instance.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.field_no_instance.yml
new file mode 100644
index 0000000..2b043cd
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test_config/config/field.field.field_no_instance.yml
@@ -0,0 +1,19 @@
+id: field_no_instance
+langcode: und
+type: text
+settings:
+ max_length: '255'
+module: text
+active: 1
+entity_types: { }
+storage:
+ type: field_sql_storage
+ settings: { }
+ module: field_sql_storage
+ active: 1
+locked: '0'
+cardinality: '1'
+translatable: false
+indexes:
+ format:
+ - format
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index d73ffac..63a4207 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -488,6 +488,26 @@ function field_ui_existing_field_options($entity_type, $bundle) {
}
/**
+ * Returns an array of default fields to be added to a bundle.
+ */
+function field_ui_default_field_options($entity_type, $bundle, $existing_fields) {
+ $info = array();
+ $field_types = field_info_field_types();
+ foreach (field_info_fields() as $field) {
+ if (!field_info_instance($entity_type, $field['field_name'], $bundle) && !isset($existing_fields[$field['field_name']])) {
+ $info[$field['field_name']] = array(
+ 'type' => $field['type'],
+ 'type_label' => $field_types[$field['type']]['label'],
+ 'field' => $field['field_name'],
+ 'label' => '',
+ 'widget_type' => $field_types[$field['type']]['default_widget'],
+ );
+ }
+ }
+ return $info;
+}
+
+/**
* Form constructor for the field settings edit page.
*
* @see field_ui_menu()
diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js
index b4621e4..1c2647d 100644
--- a/core/modules/field_ui/field_ui.js
+++ b/core/modules/field_ui/field_ui.js
@@ -45,7 +45,8 @@ Drupal.fieldUIFieldOverview = {
$this.trigger('change', false);
});
- // 'Existing field' select updates its 'Widget' select and 'Label' textfield.
+ // 'Existing field' and 'Default field' select updates its 'Widget' select
+ // and 'Label' textfield.
$table.find('.field-select').each(function () {
var $this = $(this);
var $tr = $this.closest('tr');
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
index 925caef..f52b2cc 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
@@ -385,6 +385,84 @@ public function buildForm(array $form, array &$form_state) {
),
);
}
+
+ // Additional row: Add default field.
+ $default_fields = field_ui_default_field_options($this->entity_type, $this->bundle, $existing_fields);
+ if ($default_fields && $widget_type_options) {
+ // Build list of options.
+ $default_field_options = array();
+ foreach ($default_fields as $field_name => $info) {
+ $text = t('@type: @field', array(
+ '@type' => $info['type_label'],
+ '@field' => $info['field'],
+ ));
+ $default_field_options[$field_name] = truncate_utf8($text, 80, FALSE, TRUE);
+ }
+ asort($default_fields);
+ $name = '_add_default_field';
+ $table[$name] = array(
+ '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')),
+ '#row_type' => 'add_new_field',
+ '#region_callback' => 'field_ui_field_overview_row_region',
+ 'label' => array(
+ '#type' => 'textfield',
+ '#title' => t('Existing field label'),
+ '#title_display' => 'invisible',
+ '#size' => 15,
+ '#description' => t('Label'),
+ '#attributes' => array('class' => array('label-textfield')),
+ '#prefix' => '
',
+ ),
+ 'weight' => array(
+ '#type' => 'textfield',
+ '#default_value' => $max_weight + 3,
+ '#size' => 3,
+ '#title_display' => 'invisible',
+ '#title' => t('Weight for added field'),
+ '#attributes' => array('class' => array('field-weight')),
+ '#prefix' => '
',
+ ),
+ 'parent_wrapper' => array(
+ 'parent' => array(
+ '#type' => 'select',
+ '#title' => t('Parent for default field'),
+ '#title_display' => 'invisible',
+ '#options' => $table['#parent_options'],
+ '#empty_value' => '',
+ '#attributes' => array('class' => array('field-parent')),
+ '#prefix' => '
',
+ '#parents' => array('fields', $name, 'parent'),
+ ),
+ 'hidden_name' => array(
+ '#type' => 'hidden',
+ '#default_value' => $name,
+ '#attributes' => array('class' => array('field-name')),
+ ),
+ ),
+ 'field_name' => array(
+ '#type' => 'select',
+ '#title' => t('Default field'),
+ '#title_display' => 'invisible',
+ '#options' => $default_field_options,
+ '#empty_option' => t('- Select a default field -'),
+ '#attributes' => array('class' => array('field-select')),
+ '#cell_attributes' => array('colspan' => 2),
+ '#prefix' => '
',
+ ),
+ 'widget_type' => array(
+ '#type' => 'select',
+ '#title' => t('Widget for default field'),
+ '#title_display' => 'invisible',
+ '#options' => $widget_type_options,
+ '#empty_option' => t('- Select a widget -'),
+ '#description' => t('Form element to edit the data.'),
+ '#attributes' => array('class' => array('widget-type-select')),
+ '#cell_attributes' => array('colspan' => 3),
+ '#prefix' => '
',
+ ),
+ );
+ }
$form['fields'] = $table;
// Add AJAX wrapper.
@@ -403,7 +481,8 @@ public function buildForm(array $form, array &$form_state) {
// Add settings for the update selects behavior.
$js_fields = array();
- foreach ($existing_fields as $field_name => $info) {
+ $select_fields = $existing_fields + $default_fields;
+ foreach ($select_fields as $field_name => $info) {
$js_fields[$field_name] = array('label' => $info['label'], 'type' => $info['type'], 'widget' => $info['widget_type']);
}
@@ -425,6 +504,7 @@ public function buildForm(array $form, array &$form_state) {
public function validateForm(array &$form, array &$form_state) {
$this->validateAddNew($form, $form_state);
$this->validateAddExisting($form, $form_state);
+ $this->validateAddDefault($form, $form_state);
}
/**
@@ -524,6 +604,53 @@ protected function validateAddExisting(array $form, array &$form_state) {
}
/**
+ * Validates the 'add default field' row.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param array $form_state
+ * A reference to a keyed array containing the current state of the form.
+ *
+ * @see Drupal\field_ui\FieldOverview::validateForm()
+ */
+ protected function validateAddDefault(array $form, array &$form_state) {
+ // The form element might be absent if no default fields can be added to
+ // this bundle.
+ if (isset($form_state['values']['fields']['_add_default_field'])) {
+
+ // Validate if any information was provided in the 'add default field' row.
+ if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) {
+ // Missing label.
+ if (!$field['label']) {
+ form_set_error('fields][_add_default_field][label', t('Add default field: you need to provide a label.'));
+ }
+
+ // Missing field name.
+ if (!$field['field_name']) {
+ form_set_error('fields][_add_default_field][field_name', t('Add default field: you need to provide a field name.'));
+ }
+ // Field name validation.
+ else {
+ $field_name = $field['field_name'];
+ form_set_value($form['fields']['_add_default_field']['field_name'], $field_name, $form_state);
+ }
+
+ // Missing widget type.
+ if (!$field['widget_type']) {
+ form_set_error('fields][_add_default_field][widget_type', t('Add default field: you need to select a widget.'));
+ }
+ // Wrong widget type.
+ elseif ($field['field_name'] && ($existing_field = field_info_field($field['field_name']))) {
+ $widget_types = field_ui_widget_type_options($existing_field['type']);
+ if (!isset($widget_types[$field['widget_type']])) {
+ form_set_error('fields][_add_existing_field][widget_type', t('Re-use existing field: invalid widget.'));
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Overrides \Drupal\field_ui\OverviewBase::submitForm().
*/
public function submitForm(array &$form, array &$form_state) {
@@ -632,6 +759,45 @@ public function submitForm(array &$form, array &$form_state) {
}
}
+ // Create default field.
+ $field = array();
+ if (!empty($form_values['_add_default_field']['field_name'])) {
+ $values = $form_values['_add_default_field'];
+ $instance = array(
+ 'field_name' => $values['field_name'],
+ 'entity_type' => $this->entity_type,
+ 'bundle' => $this->bundle,
+ 'label' => $values['label'],
+ 'widget' => array(
+ 'type' => $values['widget_type'],
+ 'weight' => $values['weight'],
+ ),
+ );
+
+ // Create the field and instance.
+ try {
+ field_create_instance($instance);
+
+ // 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.
+ $destinations[] = $this->adminPath. '/fields/' . $values['field_name'] . '/field-settings';
+ $destinations[] = $this->adminPath . '/fields/' . $values['field_name'];
+
+ // Store new field information for any additional submit handlers.
+ $form_state['fields_added']['_add_default_field'] = $values['field_name'];
+ }
+ catch (Exception $e) {
+ drupal_set_message(t('There was a problem creating field %label: !message', array('%label' => $instance['label'], '!message' => $e->getMessage())), 'error');
+ }
+ }
+
if ($destinations) {
$destination = drupal_get_destination();
$destinations[] = $destination['destination'];