field_ui.admin.inc 36.7 KB
Newer Older
1
2
3
4
5
6
7
<?php

/**
 * @file
 * Administrative interface for custom field type creation.
 */

8
use Drupal\field\FieldInstance;
9
10
use Drupal\field_ui\FieldOverview;
use Drupal\field_ui\DisplayOverview;
11

12
/**
13
14
15
 * Page callback: Lists all defined fields for quick reference.
 *
 * @see field_ui_menu()
16
17
18
19
 */
function field_ui_fields_list() {
  $instances = field_info_instances();
  $field_types = field_info_field_types();
20
  $bundles = entity_get_bundles();
21
22
23

  $modules = system_rebuild_module_data();

24
25
26
27
28
  $header = array(
    t('Field name'),
    array('data' => t('Field type'), 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
    t('Used in'),
  );
29
  $rows = array();
30
  foreach ($instances as $entity_type => $type_bundles) {
31
32
    foreach ($type_bundles as $bundle => $bundle_instances) {
      foreach ($bundle_instances as $field_name => $instance) {
33
        $field = field_info_field($field_name);
34
35
36
37
38
39
40
41
42
43

        // Initialize the row if we encounter the field for the first time.
        if (!isset($rows[$field_name])) {
          $rows[$field_name]['class'] = $field['locked'] ? array('menu-disabled') : array('');
          $rows[$field_name]['data'][0] = $field['locked'] ? t('@field_name (Locked)', array('@field_name' => $field_name)) : $field_name;
          $module_name = $field_types[$field['type']]['module'];
          $rows[$field_name]['data'][1] = $field_types[$field['type']]['label'] . ' ' . t('(module: !module)', array('!module' => $modules[$module_name]->info['name']));
        }

        // Add the current instance.
44
        $admin_path = field_ui_bundle_admin_path($entity_type, $bundle);
45
        $rows[$field_name]['data'][2][] = $admin_path ? l($bundles[$entity_type][$bundle]['label'], $admin_path . '/fields') : $bundles[$entity_type][$bundle]['label'];
46
      }
47
48
49
50
51
52
    }
  }
  foreach ($rows as $field_name => $cell) {
    $rows[$field_name]['data'][2] = implode(', ', $cell['data'][2]);
  }
  if (empty($rows)) {
53
    $output = t('No fields have been defined yet.');
54
55
56
57
  }
  else {
    // Sort rows by field name.
    ksort($rows);
58
    $output = theme('table', array('header' => $header, 'rows' => $rows));
59
60
61
62
63
  }
  return $output;
}

/**
64
 * Displays a message listing the inactive fields of a given bundle.
65
 */
66
67
function field_ui_inactive_message($entity_type, $bundle) {
  $inactive_instances = field_ui_inactive_instances($entity_type, $bundle);
68
69
70
71
72
73
74
75
  if (!empty($inactive_instances)) {
    $field_types = field_info_field_types();
    $widget_types = field_info_widget_types();

    foreach ($inactive_instances as $field_name => $instance) {
      $list[] = t('%field (@field_name) field requires the %widget_type widget provided by %widget_module module', array(
      '%field' => $instance['label'],
      '@field_name' => $instance['field_name'],
76
      '%widget_type' => isset($widget_types[$instance['widget']['type']]) ? $widget_types[$instance['widget']['type']]['label'] : $instance['widget']['type'],
77
78
79
      '%widget_module' => $instance['widget']['module'],
      ));
    }
80
    drupal_set_message(t('Inactive fields are not shown unless their providing modules are enabled. The following fields are not enabled: !list', array('!list' => theme('item_list', array('items' => $list)))), 'error');
81
82
83
  }
}

84
/**
85
 * Determines the rendering order of an array representing a tree.
86
 *
87
 * Callback for array_reduce() within field_ui_table_pre_render().
88
89
 */
function _field_ui_reduce_order($array, $a) {
90
  $array = !isset($array) ? array() : $array;
91
92
93
94
95
96
97
98
99
100
101
  if ($a['name']) {
    $array[] = $a['name'];
  }
  if (!empty($a['children'])) {
    uasort($a['children'], 'drupal_sort_weight');
    $array = array_merge($array, array_reduce($a['children'], '_field_ui_reduce_order'));
  }
  return $array;
}

/**
102
 * Returns the region to which a row in the 'Manage fields' screen belongs.
103
 *
104
 * This function is used as a #region_callback in
105
 * Drupal\field_ui\DisplayOverview::form(). It is called during
106
 * field_ui_table_pre_render().
107
 */
108
109
110
111
function field_ui_field_overview_row_region($row) {
  switch ($row['#row_type']) {
    case 'field':
    case 'extra_field':
112
      return 'content';
113
114
115
    case 'add_new_field':
      // If no input in 'label', assume the row has not been dragged out of the
      // 'add new' section.
116
      return (!empty($row['label']['#value']) ? 'content' : 'hidden');
117
118
119
120
121
122
  }
}

/**
 * Returns the region to which a row in the 'Manage display' screen belongs.
 *
123
 * This function is used as a #region_callback in
124
 * Drupal\field_ui\FieldOverview::form(), and is called during
125
 * field_ui_table_pre_render().
126
127
128
129
130
 */
function field_ui_display_overview_row_region($row) {
  switch ($row['#row_type']) {
    case 'field':
    case 'extra_field':
131
      return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'content');
132
133
134
135
  }
}

/**
136
137
138
139
140
141
 * Render API callback: Performs pre-render tasks on field_ui_table elements.
 *
 * This function is assigned as a #pre_render callback in
 * field_ui_element_info().
 *
 * @see drupal_render().
142
143
144
 */
function field_ui_table_pre_render($elements) {
  $js_settings = array();
145

146
147
148
149
  // 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'];
150
  $tree = array('' => array('name' => '', 'children' => array()));
151
152
  $trees = array_fill_keys(array_keys($regions), $tree);

153
154
  $parents = array();
  $list = drupal_map_assoc(element_children($elements));
155

156
157
158
159
160
161
162
  // 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])) {
163
        // Grab parent, and remove the row from the next iteration.
164
165
166
        $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array();
        unset($list[$name]);

167
168
169
170
        // Determine the region for the row.
        $function = $row['#region_callback'];
        $region_name = $function($row);

171
        // Add the element in the tree.
172
        $target = &$trees[$region_name][''];
173
174
175
176
177
178
179
        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])) {
180
181
          $children = element_children($row);
          $cell = current($children);
182
183
          $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '');
        }
184
185
186
187

        // Add row id and associate JS settings.
        $id = drupal_html_class($name);
        $row['#attributes']['id'] = $id;
188
189
190
191
192
193
194
195
        if (isset($row['#js_settings'])) {
          $row['#js_settings'] += array(
            'rowHandler' => $row['#row_type'],
            'name' => $name,
            'region' => $region_name,
          );
          $js_settings[$id] = $row['#js_settings'];
        }
196
197
198
      }
    }
  }
199
200
201
202
  // Determine rendering order from the tree structure.
  foreach ($regions as $region_name => $region) {
    $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], '_field_ui_reduce_order');
  }
203

204
205
206
207
  $elements['#attached']['js'][] = array(
    'type' => 'setting',
    'data' => array('fieldUIRowsData' => $js_settings),
  );
208
209

  return $elements;
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
}

/**
 * Returns HTML for Field UI overview tables.
 *
 * @param $variables
 *   An associative array containing:
 *   - elements: An associative array containing a Form API structure to be
 *     rendered as a table.
 *
 * @ingroup themeable
 */
function theme_field_ui_table($variables) {
  $elements = $variables['elements'];
  $table = array();
225
  $js_settings = array();
226

227
  // Add table headers and attributes.
228
229
230
231
232
233
  foreach (array('header', 'attributes') as $key) {
    if (isset($elements["#$key"])) {
      $table[$key] = $elements["#$key"];
    }
  }

234
235
  // Determine the colspan to use for region rows, by checking the number of
  // columns in the headers.
236
  $columns_count = 0;
237
  foreach ($table['header'] as $header) {
238
    $columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1);
239
240
241
242
243
244
245
  }

  // Render rows, region by region.
  foreach ($elements['#regions'] as $region_name => $region) {
    $region_name_class = drupal_html_class($region_name);

    // Add region rows.
246
    if (isset($region['title']) && empty($region['invisible'])) {
247
248
249
250
      $table['rows'][] = array(
        'class' => array('region-title', 'region-' . $region_name_class . '-title'),
        'no_striping' => TRUE,
        'data' => array(
251
          array('data' => $region['title'], 'colspan' => $columns_count),
252
253
254
255
256
257
258
259
260
        ),
      );
    }
    if (isset($region['message'])) {
      $class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated');
      $table['rows'][] = array(
        'class' => array('region-message', 'region-' . $region_name_class . '-message', $class),
        'no_striping' => TRUE,
        'data' => array(
261
          array('data' => $region['message'], 'colspan' => $columns_count),
262
263
264
265
266
267
268
269
270
271
272
273
        ),
      );
    }

    // Add form rows, in the order determined at pre-render time.
    foreach ($region['rows_order'] as $name) {
      $element = $elements[$name];

      $row = array('data' => array());
      if (isset($element['#attributes'])) {
        $row += $element['#attributes'];
      }
274

275
      // Render children as table cells.
276
      foreach (element_children($element) as $cell_key) {
277
278
279
280
281
282
283
284
        $child = &$element[$cell_key];
        // Do not render a cell for children of #type 'value'.
        if (!(isset($child['#type']) && $child['#type'] == 'value')) {
          $cell = array('data' => drupal_render($child));
          if (isset($child['#cell_attributes'])) {
            $cell += $child['#cell_attributes'];
          }
          $row['data'][] = $cell;
285
        }
286
      }
287
      $table['rows'][] = $row;
288
289
290
291
292
293
    }
  }

  return theme('table', $table);
}

294
/**
295
 * Returns the built and processed 'Manage fields' form of a bundle.
296
 *
297
298
 * The resulting form allows fields and pseudo-fields to be re-ordered.
 *
299
300
301
302
303
304
305
306
 * @param string $entity_type
 *   The entity type for the fieldable entity.
 * @param string $bundle
 *   The bundle for the fieldable entity.
 *
 * @return
 *   The processed form for the given entity type and bundle.
 *
307
 * @see field_ui_menu()
308
309
 * @see Drupal\field_ui\FieldOverview::validate()
 * @see Drupal\field_ui\FieldOverview::submit()
310
 * @ingroup forms
311
 */
312
function field_ui_field_overview($entity_type, $bundle) {
313
314
  $bundle = field_extract_bundle($entity_type, $bundle);
  field_ui_inactive_message($entity_type, $bundle);
315

316
  $field_overview = new FieldOverview($entity_type, $bundle);
317

318
319
320
  $form_state = array();
  $form_state['build_info']['callback'] = array($field_overview, 'form');
  $form_state['build_info']['args'] = array($entity_type, $bundle);
321

322
  return drupal_build_form('field_ui_field_overview_form', $form_state);
323
324
}

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/**
 * Render API callback: Checks if a field machine name is taken.
 *
 * @param $value
 *   The machine name, not prefixed with 'field_'.
 *
 * @return
 *   Whether or not the field machine name is taken.
 */
function _field_ui_field_name_exists($value) {
  // Prefix with 'field_'.
  $field_name = 'field_' . $value;

  // We need to check inactive fields as well, so we can't use
  // field_info_fields().
  return (bool) field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
}

343
/**
344
 * Returns the built and processed 'Manage display' form of a bundle.
345
 *
346
 * The resulting form allows fields and pseudo-fields to be re-ordered.
347
 *
348
349
350
351
352
353
354
355
356
 * @param string $entity_type
 *   The entity type for the fieldable entity.
 * @param string $bundle
 *   The bundle for the fieldable entity.
 * @param string $view_mode
 *   The view mode for the fieldable entity.
 *
 * @return
 *   The processed form for the given entity type and bundle.
357
358
359
 *
 * @see field_ui_menu()
 * @see field_ui_display_overview_multistep_submit()
360
 * @see Drupal\field_ui\DisplayOverview::submit()
361
 * @ingroup forms
362
 */
363
function field_ui_display_overview($entity_type, $bundle, $view_mode) {
364
365
  $bundle = field_extract_bundle($entity_type, $bundle);
  field_ui_inactive_message($entity_type, $bundle);
366

367
  $display_overview = new DisplayOverview($entity_type, $bundle, $view_mode);
368

369
370
371
  $form_state = array();
  $form_state['build_info']['callback'] = array($display_overview, 'form');
  $form_state['build_info']['args'] = array($entity_type, $bundle, $view_mode);
372

373
  return drupal_build_form('field_ui_display_overview_form', $form_state);
374
375
376
}

/**
377
 * Returns an array of field_type options.
378
379
380
381
382
383
384
385
386
 */
function field_ui_field_type_options() {
  $options = &drupal_static(__FUNCTION__);

  if (!isset($options)) {
    $options = array();
    $field_types = field_info_field_types();
    $field_type_options = array();
    foreach ($field_types as $name => $field_type) {
387
388
389
      // Skip field types which have no widget types, or should not be add via
      // uesr interface.
      if (field_ui_widget_type_options($name) && empty($field_type['no_ui'])) {
390
391
392
393
394
395
396
397
398
        $options[$name] = $field_type['label'];
      }
    }
    asort($options);
  }
  return $options;
}

/**
399
 * Returns an array of widget type options for a field type.
400
401
402
403
404
405
406
407
408
409
 *
 * If no field type is provided, returns a nested array of all widget types,
 * keyed by field type human name.
 */
function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) {
  $options = &drupal_static(__FUNCTION__);

  if (!isset($options)) {
    $options = array();
    $field_types = field_info_field_types();
410
411
412
413
    $widget_types = field_info_widget_types();
    uasort($widget_types, 'drupal_sort_weight');
    foreach ($widget_types as $name => $widget_type) {
      foreach ($widget_type['field_types'] as $widget_field_type) {
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
        // Check that the field type exists.
        if (isset($field_types[$widget_field_type])) {
          $options[$widget_field_type][$name] = $widget_type['label'];
        }
      }
    }
  }

  if (isset($field_type)) {
    return !empty($options[$field_type]) ? $options[$field_type] : array();
  }
  if ($by_label) {
    $field_types = field_info_field_types();
    $options_by_label = array();
    foreach ($options as $field_type => $widgets) {
      $options_by_label[$field_types[$field_type]['label']] = $widgets;
    }
    return $options_by_label;
  }
  return $options;
}

/**
437
 * Returns an array of formatter options for a field type.
438
439
440
441
442
443
444
445
446
447
448
 *
 * If no field type is provided, returns a nested array of all formatters, keyed
 * by field type.
 */
function field_ui_formatter_options($field_type = NULL) {
  $options = &drupal_static(__FUNCTION__);

  if (!isset($options)) {
    $field_types = field_info_field_types();
    $options = array();
    foreach (field_info_formatter_types() as $name => $formatter) {
449
      foreach ($formatter['field_types'] as $formatter_field_type) {
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
        // Check that the field type exists.
        if (isset($field_types[$formatter_field_type])) {
          $options[$formatter_field_type][$name] = $formatter['label'];
        }
      }
    }
  }

  if ($field_type) {
    return !empty($options[$field_type]) ? $options[$field_type] : array();
  }
  return $options;
}

/**
465
 * Returns an array of existing fields to be added to a bundle.
466
 */
467
function field_ui_existing_field_options($entity_type, $bundle) {
468
  $info = array();
469
  $field_types = field_info_field_types();
470

471
  foreach (field_info_instances() as $existing_entity_type => $bundles) {
472
473
    foreach ($bundles as $existing_bundle => $instances) {
      // No need to look in the current bundle.
474
      if (!($existing_bundle == $bundle && $existing_entity_type == $entity_type)) {
475
476
        foreach ($instances as $instance) {
          $field = field_info_field($instance['field_name']);
477
478
479
          // Don't show
          // - locked fields,
          // - fields already in the current bundle,
480
          // - fields that cannot be added to the entity type,
481
          // - fields that should not be added via user interface.
482

483
          if (empty($field['locked'])
484
            && !field_info_instance($entity_type, $field['field_name'], $bundle)
485
486
            && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types']))
            && empty($field_types[$field['type']]['no_ui'])) {
487
488
489
490
            $info[$instance['field_name']] = array(
              'type' => $field['type'],
              'type_label' => $field_types[$field['type']]['label'],
              'field' => $field['field_name'],
491
              'label' => $instance['label'],
492
493
              'widget_type' => $instance['widget']['type'],
            );
494
          }
495
496
497
498
        }
      }
    }
  }
499
  return $info;
500
501
502
}

/**
503
504
505
506
507
 * Form constructor for the field settings edit page.
 *
 * @see field_ui_menu()
 * @see field_ui_settings_form_submit()
 * @ingroups forms
508
 */
509
510
511
512
function field_ui_field_settings_form($form, &$form_state, $instance) {
  $bundle = $instance['bundle'];
  $entity_type = $instance['entity_type'];
  $field = field_info_field($instance['field_name']);
513
514
515
  $form['#field'] = $field;
  $form['#entity_type'] = $entity_type;
  $form['#bundle'] = $bundle;
516

517
  drupal_set_title($instance['label']);
518
519
520
521
522

  $description = '<p>' . t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array('%field' => $instance['label'])) . '</p>';

  // Create a form structure for the field values.
  $form['field'] = array(
523
    '#prefix' => $description,
524
525
526
527
528
    '#tree' => TRUE,
  );

  // See if data already exists for this field.
  // If so, prevent changes to the field settings.
529
  $has_data = field_has_data($field);
530
  if ($has_data) {
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
    $form['field']['#prefix'] = '<div class="messages error">' . t('There is data for this field in the database. The field settings can no longer be changed.') . '</div>' . $form['field']['#prefix'];
  }

  // Build the configurable field values.
  $cardinality = $field['cardinality'];
  $form['field']['container'] = array(
    // We can't use the container element because it doesn't support the title
    // or description properties.
    '#type' => 'item',
    '#field_prefix' => '<div class="container-inline">',
    '#field_suffix' => '</div>',
    '#title' => t('Maximum number of values users can enter'),
  );
  $form['field']['container']['cardinality'] = array(
    '#type' => 'select',
    '#options' => drupal_map_assoc(range(1, 5)) + array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + array('other' => t('More')),
    '#default_value' => ($cardinality < 6) ? $cardinality : 'other',
  );
  // @todo Convert when http://drupal.org/node/1207060 gets in.
  $form['field']['container']['cardinality_other'] = array(
    '#type' => 'number',
    '#default_value' => $cardinality > 5 ? $cardinality : 6,
    '#min' => 1,
    '#title' => t('Custom value'),
    '#title_display' => 'invisible',
    '#states' => array(
      'visible' => array(
       ':input[name="field[container][cardinality]"]' => array('value' => 'other'),
      ),
    ),
  );
  if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
    $form['field']['container']['#description'] = t('%unlimited will provide an %add-more button so users can add as many values as they like.', array(
      '%unlimited' => t('Unlimited'),
      '%add-more' => t('Add another item'),
    ));
567
568
569
570
571
572
573
574
  }

  // Build the non-configurable field values.
  $form['field']['field_name'] = array('#type' => 'value', '#value' => $field['field_name']);
  $form['field']['type'] = array('#type' => 'value', '#value' => $field['type']);
  $form['field']['module'] = array('#type' => 'value', '#value' => $field['module']);
  $form['field']['active'] = array('#type' => 'value', '#value' => $field['active']);

575
576
577
  // Add settings provided by the field module. The field module is
  // responsible for not returning settings that cannot be changed if
  // the field already has data.
578
579
580
  $form['field']['settings'] = array(
    '#weight' => 10,
  );
581
  $additions = module_invoke($field['module'], 'field_settings_form', $field, $instance, $has_data);
582
  if (is_array($additions)) {
583
    $form['field']['settings'] += $additions;
584
585
  }

586
  $form['actions'] = array('#type' => 'actions');
587
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save field settings'));
588
589
590
  return $form;
}

591
592
593
594
595
596
597
598
599
600
601
602
603
604
/**
 * Form validation handler for field_ui_field_edit_form().
 *
 * @see field_ui_field_settings_form_submit().
 */
function field_ui_field_settings_form_validate($form, &$form_state) {
  // Validate field cardinality.
  $cardinality = $form_state['values']['field']['container']['cardinality'];
  $cardinality_other = $form_state['values']['field']['container']['cardinality_other'];
  if ($cardinality == 'other' && empty($cardinality_other)) {
    form_error($form['field']['container']['cardinality_other'], t('Number of values is required.'));
  }
}

605
/**
606
 * Form submission handler for field_ui_field_settings_form().
607
608
609
610
611
 */
function field_ui_field_settings_form_submit($form, &$form_state) {
  $form_values = $form_state['values'];
  $field_values = $form_values['field'];

612
613
614
615
616
617
618
619
620
621
  // Save field cardinality.
  $cardinality = $field_values['container']['cardinality'];
  $cardinality_other = $field_values['container']['cardinality_other'];
  $cardinality_other = $form_state['values']['field']['container']['cardinality_other'];
  if ($cardinality == 'other') {
    $cardinality = $cardinality_other;
  }
  $field_values['cardinality'] = $cardinality;
  unset($field_values['container']);

622
623
624
  // Merge incoming form values into the existing field.
  $field = field_info_field($field_values['field_name']);

625
  $entity_type = $form['#entity_type'];
626
  $bundle = $form['#bundle'];
627
  $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
628
629
630
631

  // Update the field.
  $field = array_merge($field, $field_values);

632
633
634
  try {
    field_update_field($field);
    drupal_set_message(t('Updated field %label field settings.', array('%label' => $instance['label'])));
635
    $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle);
636
  }
637
  catch (Exception $e) {
638
639
    drupal_set_message(t('Attempt to update field %label failed: %message.', array('%label' => $instance['label'], '%message' => $e->getMessage())), 'error');
  }
640
641
642
}

/**
643
644
645
646
647
 * Form constructor for the widget selection form.
 *
 * @see field_ui_menu()
 * @see field_ui_widget_type_form_submit()
 * @ingroup forms
648
 */
649
function field_ui_widget_type_form($form, &$form_state, FieldInstance $instance) {
650
651
  drupal_set_title($instance['label']);

652
653
  $bundle = $instance['bundle'];
  $entity_type = $instance['entity_type'];
654
  $field_name = $instance['field_name'];
655

656
  $field = field_info_field($field_name);
657
  $bundles = entity_get_bundles();
658
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
659

660
661
662
663
664
665
  $form = array(
    '#bundle' => $bundle,
    '#entity_type' => $entity_type,
    '#field_name' => $field_name,
  );

666
  $form['widget_type'] = array(
667
668
669
670
    '#type' => 'select',
    '#title' => t('Widget type'),
    '#required' => TRUE,
    '#options' => field_ui_widget_type_options($field['type']),
671
    '#default_value' => $instance->getWidget()->getPluginId(),
672
673
674
    '#description' => t('The type of form element you would like to present to the user when creating this field in the %type type.', array('%type' => $bundle_label)),
  );

675
676
  $form['actions'] = array('#type' => 'actions');
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Continue'));
677
678
679
680
681
682
683
684

  $form['#validate'] = array();
  $form['#submit'] = array('field_ui_widget_type_form_submit');

  return $form;
}

/**
685
 * Form submission handler for field_ui_widget_type_form().
686
687
688
 */
function field_ui_widget_type_form_submit($form, &$form_state) {
  $form_values = $form_state['values'];
689
690
691
692
693
694
  $bundle = $form['#bundle'];
  $entity_type = $form['#entity_type'];
  $field_name = $form['#field_name'];

  // Retrieve the stored instance settings to merge with the incoming values.
  $instance = field_read_instance($entity_type, $field_name, $bundle);
695
696
697
698
699
700
701

  // Set the right module information.
  $widget_type = field_info_widget_types($form_values['widget_type']);
  $widget_module = $widget_type['module'];

  $instance['widget']['type'] = $form_values['widget_type'];
  $instance['widget']['module'] = $widget_module;
702

703
704
705
706
  try {
    field_update_instance($instance);
    drupal_set_message(t('Changed the widget for field %label.', array('%label' => $instance['label'])));
  }
707
  catch (Exception $e) {
708
    drupal_set_message(t('There was a problem changing the widget for field %label.', array('%label' => $instance['label'])), 'error');
709
710
  }

711
  $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle);
712
713
714
}

/**
715
716
717
 * Form constructor for removing a field instance from a bundle.
 *
 * @see field_ui_menu()
718
 * @see field_ui_field_delete_form_submit()
719
 * @ingroup forms
720
 */
721
722
723
724
725
function field_ui_field_delete_form($form, &$form_state, $instance) {
  $bundle = $instance['bundle'];
  $entity_type = $instance['entity_type'];
  $field = field_info_field($instance['field_name']);

726
  $admin_path = field_ui_bundle_admin_path($entity_type, $bundle);
727

728
  $form['entity_type'] = array('#type' => 'value', '#value' => $entity_type);
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
  $form['bundle'] = array('#type' => 'value', '#value' => $bundle);
  $form['field_name'] = array('#type' => 'value', '#value' => $field['field_name']);

  $output = confirm_form($form,
    t('Are you sure you want to delete the field %field?', array('%field' => $instance['label'])),
    $admin_path . '/fields',
    t('If you have any content left in this field, it will be lost. This action cannot be undone.'),
    t('Delete'), t('Cancel'),
    'confirm'
  );

  if ($field['locked']) {
    unset($output['actions']['submit']);
    $output['description']['#markup'] = t('This field is <strong>locked</strong> and cannot be deleted.');
  }

  return $output;
}

/**
749
 * Form submission handler for field_ui_field_delete_form().
750
 *
751
752
 * Removes a field instance from a bundle. If the field has no more instances,
 * it will be marked as deleted too.
753
754
755
 */
function field_ui_field_delete_form_submit($form, &$form_state) {
  $form_values = $form_state['values'];
756
757
  $field_name = $form_values['field_name'];
  $bundle = $form_values['bundle'];
758
  $entity_type = $form_values['entity_type'];
759
760

  $field = field_info_field($field_name);
761
  $instance = field_info_instance($entity_type, $field_name, $bundle);
762
  $bundles = entity_get_bundles();
763
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
764
765

  if (!empty($bundle) && $field && !$field['locked'] && $form_values['confirm']) {
766
    field_delete_instance($instance);
767
768
769
    drupal_set_message(t('The field %field has been deleted from the %type content type.', array('%field' => $instance['label'], '%type' => $bundle_label)));
  }
  else {
770
    drupal_set_message(t('There was a problem removing the %field from the %type content type.', array('%field' => $instance['label'], '%type' => $bundle_label)), 'error');
771
772
  }

773
  $admin_path = field_ui_bundle_admin_path($entity_type, $bundle);
774
  $form_state['redirect'] = field_ui_get_destinations(array($admin_path . '/fields'));
775
776
777
778
779
780
781
782

  // Fields are purged on cron. However field module prevents disabling modules
  // when field types they provided are used in a field until it is fully
  // purged. In the case that a field has minimal or no content, a single call
  // to field_purge_batch() will remove it from the system. Call this with a
  // low batch limit to avoid administrators having to wait for cron runs when
  // removing instances that meet this criteria.
  field_purge_batch(10);
783
784
785
}

/**
786
787
788
789
790
791
792
 * Form constructor for the field instance settings form.
 *
 * @see field_ui_menu()
 * @see field_ui_field_edit_form_validate()
 * @see field_ui_field_edit_form_submit()
 * @see field_ui_field_edit_form_delete_submit()
 * @ingroup forms
793
 */
794
795
796
797
function field_ui_field_edit_form($form, &$form_state, $instance) {
  $bundle = $instance['bundle'];
  $entity_type = $instance['entity_type'];
  $field = field_info_field($instance['field_name']);
798
  $bundles = entity_get_bundles();
799

800
801
802
803
  drupal_set_title(t('%instance settings for %bundle', array(
    '%instance' => $instance['label'],
    '%bundle' => $bundles[$entity_type][$bundle]['label'],
  )), PASS_THROUGH);
804
805

  $form['#field'] = $field;
806
  $form['#instance'] = $instance;
807
808
809
810
  // Create an arbitrary entity object (used by the 'default value' widget).
  $ids = (object) array('entity_type' => $instance['entity_type'], 'bundle' => $instance['bundle'], 'entity_id' => NULL);
  $form['#entity'] = _field_create_entity_from_ids($ids);
  $form['#entity']->field_ui_default_value = TRUE;
811
812
813
814
815
816
817
818
819
820
821

  if (!empty($field['locked'])) {
    $form['locked'] = array(
      '#markup' => t('The field %field is locked and cannot be edited.', array('%field' => $instance['label'])),
    );
    return $form;
  }

  $widget_type = field_info_widget_types($instance['widget']['type']);

  // Create a form structure for the instance values.
822
  $form['instance'] = array(
823
    '#tree' => TRUE,
824
  );
825
826
827
828
829
830

  // Build the non-configurable instance values.
  $form['instance']['field_name'] = array(
    '#type' => 'value',
    '#value' => $instance['field_name'],
  );
831
  $form['instance']['entity_type'] = array(
832
    '#type' => 'value',
833
    '#value' => $entity_type,
834
  );
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
  $form['instance']['bundle'] = array(
    '#type' => 'value',
    '#value' => $bundle,
  );
  $form['instance']['widget']['weight'] = array(
    '#type' => 'value',
    '#value' => !empty($instance['widget']['weight']) ? $instance['widget']['weight'] : 0,
  );

  // Build the configurable instance values.
  $form['instance']['label'] = array(
    '#type' => 'textfield',
    '#title' => t('Label'),
    '#default_value' => !empty($instance['label']) ? $instance['label'] : $field['field_name'],
    '#required' => TRUE,
    '#weight' => -20,
  );

  $form['instance']['description'] = array(
    '#type' => 'textarea',
    '#title' => t('Help text'),
    '#default_value' => !empty($instance['description']) ? $instance['description'] : '',
    '#rows' => 5,
858
    '#description' => t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())) . '<br />' . t('This field supports tokens.'),
859
860
861
862
863
864
865
    '#weight' => -10,
  );

  $form['instance']['required'] = array(
    '#type' => 'checkbox',
    '#title' => t('Required field'),
    '#default_value' => !empty($instance['required']),
866
    '#weight' => -5,
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
  );

  // Build the widget component of the instance.
  $form['instance']['widget']['type'] = array(
    '#type' => 'value',
    '#value' => $instance['widget']['type'],
  );
  $form['instance']['widget']['module'] = array(
    '#type' => 'value',
    '#value' => $widget_type['module'],
  );
  $form['instance']['widget']['active'] = array(
    '#type' => 'value',
    '#value' => !empty($field['instance']['widget']['active']) ? 1 : 0,
  );

  // Add additional field instance settings from the field module.
884
  $additions = module_invoke($field['module'], 'field_instance_settings_form', $field, $instance, $form_state);
885
886
  if (is_array($additions)) {
    $form['instance']['settings'] = $additions;
887
    $form['instance']['settings']['#weight'] = 10;
888
889
  }

890
891
892
  // Add widget settings for the widget type.
  $additions = $instance->getWidget()->settingsForm($form, $form_state);
  $form['instance']['widget']['settings'] = $additions ? $additions : array('#type' => 'value', '#value' => array());
893
  $form['instance']['widget']['#weight'] = 20;
894
895

  // Add handling for default value if not provided by any other module.
896
  if (field_behaviors_widget('default_value', $instance) == FIELD_BEHAVIOR_DEFAULT && empty($instance['default_value_function'])) {
897
    $form['instance']['default_value_widget'] = field_ui_default_value_widget($field, $instance, $form, $form_state);
898
899
  }

900
  $form['actions'] = array('#type' => 'actions');
901
902
903
904
905
906
907
908
909
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save settings')
  );
  $form['actions']['delete'] = array(
    '#type' => 'submit',
    '#value' => t('Delete field'),
    '#submit' => array('field_ui_field_edit_form_delete_submit'),
  );
910
911
912
  return $form;
}

913
/**
914
 * Form submission handler for 'Delete' button in field_ui_field_edit_form().
915
916
917
918
919
920
921
922
923
924
925
 */
function field_ui_field_edit_form_delete_submit($form, &$form_state) {
  $destination = array();
  if (isset($_GET['destination'])) {
    $destination = drupal_get_destination();
    unset($_GET['destination']);
  }
  $instance = $form['#instance'];
  $form_state['redirect'] = array('admin/structure/types/manage/' . $instance['bundle'] . '/fields/' . $instance['field_name'] . '/delete', array('query' => $destination));
}

926
/**
927
 * Builds the default value widget for a given field instance.
928
929
 */
function field_ui_default_value_widget($field, $instance, &$form, &$form_state) {
930
  $field_name = $field['field_name'];
931
  $entity = $form['#entity'];
932
933

  $element = array(
934
    '#type' => 'details',
935
936
937
    '#title' => t('Default value'),
    '#tree' => TRUE,
    '#description' => t('The default value for this field, used when creating new content.'),
938
939
940
    // Stick to an empty 'parents' on this form in order not to breaks widgets
    // that do not use field_widget_[field|instance]() and still access
    // $form_state['field'] directly.
941
    '#parents' => array(),
942
943
  );

944
945
  // Adjust the instance definition used for the form element. We want a
  // non-required input and no description.
946
947
  $instance['required'] = FALSE;
  $instance['description'] = '';
948

949
950
951
952
  // Insert the widget. Since we do not use the "official" instance definition,
  // the whole flow cannot use field_invoke_method().
  $items = (array) $instance['default_value'];
  $element += $instance->getWidget()->form($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
953
954

  return $element;
955
956
957
}

/**
958
959
960
 * Form validation handler for field_ui_field_edit_form().
 *
 * @see field_ui_field_edit_form_submit().
961
962
 */
function field_ui_field_edit_form_validate($form, &$form_state) {
963
964
  // Take the incoming values as the $instance definition, so that the 'default
  // value' gets validated using the instance settings being submitted.
965
  $instance = $form['#instance'];
966
  $field_name = $instance['field_name'];
967
  $entity = $form['#entity'];
968

969
970
971
972
973
  if (isset($form['instance']['default_value_widget'])) {
    $element = $form['instance']['default_value_widget'];

    // Extract the 'default value'.
    $items = array();
974
    $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
975

976
977
978
979
980
    // Grab the field definition from $form_state.
    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state);
    $field = $field_state['field'];

    // Validate the value.
981
982
983
    $errors = array();
    $function = $field['module'] . '_field_validate';
    if (function_exists($function)) {
984
      $function(NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $errors);
985
    }
986
987

    // Report errors.
988
    if (isset($errors[$field_name][LANGUAGE_NOT_SPECIFIED])) {
989
      // Store reported errors in $form_state.
990
991
      $field_state['errors'] = $errors[$field_name][LANGUAGE_NOT_SPECIFIED];
      field_form_set_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state, $field_state);
992

993
      // Assign reported errors to the correct form element.
994
      $instance->getWidget()->flagErrors($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
995
    }
996
997
998
999
  }
}

/**
1000
1001
1002
 * Form submission handler for field_ui_field_edit_form().
 *
 * @see field_ui_field_edit_form_validate().
1003
1004
 */
function field_ui_field_edit_form_submit($form, &$form_state) {
1005
1006
1007
  $instance = $form['#instance'];
  $field = $form['#field'];
  $entity = $form['#entity'];
1008

1009
  // Handle the default value.
1010
1011
1012
1013
1014
  if (isset($form['instance']['default_value_widget'])) {
    $element = $form['instance']['default_value_widget'];

    // Extract field values.
    $items = array();
1015
    $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
1016
1017
1018

    $instance['default_value'] = $items ? $items : NULL;
  }
1019

1020
1021
1022
1023
  // Merge incoming values into the instance.
  foreach ($form_state['values']['instance'] as $key => $value) {
    $instance[$key] = $value;
  }
1024
1025
1026
1027
  field_update_instance($instance);

  drupal_set_message(t('Saved %label configuration.', array('%label' => $instance['label'])));

1028
  $form_state['redirect'] = field_ui_next_destination($instance['entity_type'], $instance['bundle']);
1029
1030
1031
}

/**
1032
1033
1034
 * Extracts next redirect path from an array of multiple destinations.
 *
 * @see field_ui_next_destination()
1035
1036
1037
 */
function field_ui_get_destinations($destinations) {
  $path = array_shift($destinations);
1038
  $options = drupal_parse_url($path);
1039
  if ($destinations) {
1040
    $options['query']['destinations'] = $destinations;
1041
  }
1042
  return array($options['path'], $options);
1043
1044
1045
}

/**
1046
 * Returns the next redirect path in a multipage sequence.
1047
 */
1048
function field_ui_next_destination($entity_type, $bundle) {
1049
1050
1051
1052
1053
  $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
  if (!empty($destinations)) {
    unset($_REQUEST['destinations']);
    return field_ui_get_destinations($destinations);
  }
1054
  $admin_path = field_ui_bundle_admin_path($entity_type, $bundle);
1055
1056
  return $admin_path . '/fields';
}