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' => '
' . t('Add default field') .'
', + '#suffix' => '
', + ), + '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'];