Projet

Général

Profil

Paste
Télécharger (77,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / field_ui / field_ui.admin.inc @ 01dfd3b5

1
<?php
2

    
3
/**
4
 * @file
5
 * Administrative interface for custom field type creation.
6
 */
7

    
8
/**
9
 * Menu callback; lists all defined fields for quick reference.
10
 */
11
function field_ui_fields_list() {
12
  $instances = field_info_instances();
13
  $field_types = field_info_field_types();
14
  $bundles = field_info_bundles();
15

    
16
  $modules = system_rebuild_module_data();
17

    
18
  $header = array(t('Field name'), t('Field type'), t('Used in'));
19
  $rows = array();
20
  foreach ($instances as $entity_type => $type_bundles) {
21
    foreach ($type_bundles as $bundle => $bundle_instances) {
22
      foreach ($bundle_instances as $field_name => $instance) {
23
        $field = field_info_field($field_name);
24

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

    
33
        // Add the current instance.
34
        $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
35
        $rows[$field_name]['data'][2][] = $admin_path ? l($bundles[$entity_type][$bundle]['label'], $admin_path . '/fields') : $bundles[$entity_type][$bundle]['label'];
36
      }
37
    }
38
  }
39
  foreach ($rows as $field_name => $cell) {
40
    $rows[$field_name]['data'][2] = implode(', ', $cell['data'][2]);
41
  }
42
  if (empty($rows)) {
43
    $output = t('No fields have been defined yet.');
44
  }
45
  else {
46
    // Sort rows by field name.
47
    ksort($rows);
48
    $output = theme('table', array('header' => $header, 'rows' => $rows));
49
  }
50
  return $output;
51
}
52

    
53
/**
54
 * Displays a message listing the inactive fields of a given bundle.
55
 */
56
function field_ui_inactive_message($entity_type, $bundle) {
57
  $inactive_instances = field_ui_inactive_instances($entity_type, $bundle);
58
  if (!empty($inactive_instances)) {
59
    $field_types = field_info_field_types();
60
    $widget_types = field_info_widget_types();
61

    
62
    foreach ($inactive_instances as $field_name => $instance) {
63
      $list[] = t('%field (@field_name) field requires the %widget_type widget provided by %widget_module module', array(
64
      '%field' => $instance['label'],
65
      '@field_name' => $instance['field_name'],
66
      '%widget_type' => isset($widget_types[$instance['widget']['type']]) ? $widget_types[$instance['widget']['type']]['label'] : $instance['widget']['type'],
67
      '%widget_module' => $instance['widget']['module'],
68
      ));
69
    }
70
    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');
71
  }
72
}
73

    
74
/**
75
 * Determines the rendering order of an array representing a tree.
76
 *
77
 * Callback for array_reduce() within field_ui_table_pre_render().
78
 */
79
function _field_ui_reduce_order($array, $a) {
80
  $array = !isset($array) ? array() : $array;
81
  if ($a['name']) {
82
    $array[] = $a['name'];
83
  }
84
  if (!empty($a['children'])) {
85
    uasort($a['children'], 'drupal_sort_weight');
86
    $array = array_merge($array, array_reduce($a['children'], '_field_ui_reduce_order'));
87
  }
88
  return $array;
89
}
90

    
91
/**
92
 * Returns the region to which a row in the 'Manage fields' screen belongs.
93
 *
94
 * This function is used as a #region_callback in
95
 * field_ui_field_overview_form(). It is called during
96
 * field_ui_table_pre_render().
97
 */
98
function field_ui_field_overview_row_region($row) {
99
  switch ($row['#row_type']) {
100
    case 'field':
101
    case 'extra_field':
102
      return 'main';
103
    case 'add_new_field':
104
      // If no input in 'label', assume the row has not been dragged out of the
105
      // 'add new' section.
106
      return (!empty($row['label']['#value']) ? 'main' : 'add_new');
107
  }
108
}
109

    
110
/**
111
 * Returns the region to which a row in the 'Manage display' screen belongs.
112
 *
113
 * This function is used as a #region_callback in
114
 * field_ui_field_overview_form(), and is called during
115
 * field_ui_table_pre_render().
116
 */
117
function field_ui_display_overview_row_region($row) {
118
  switch ($row['#row_type']) {
119
    case 'field':
120
    case 'extra_field':
121
      return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'visible');
122
  }
123
}
124

    
125
/**
126
 * Pre-render callback for field_ui_table elements.
127
 */
128
function field_ui_table_pre_render($elements) {
129
  $js_settings = array();
130

    
131
  // For each region, build the tree structure from the weight and parenting
132
  // data contained in the flat form structure, to determine row order and
133
  // indentation.
134
  $regions = $elements['#regions'];
135
  $tree = array('' => array('name' => '', 'children' => array()));
136
  $trees = array_fill_keys(array_keys($regions), $tree);
137

    
138
  $parents = array();
139
  $list = drupal_map_assoc(element_children($elements));
140

    
141
  // Iterate on rows until we can build a known tree path for all of them.
142
  while ($list) {
143
    foreach ($list as $name) {
144
      $row = &$elements[$name];
145
      $parent = $row['parent_wrapper']['parent']['#value'];
146
      // Proceed if parent is known.
147
      if (empty($parent) || isset($parents[$parent])) {
148
        // Grab parent, and remove the row from the next iteration.
149
        $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array();
150
        unset($list[$name]);
151

    
152
        // Determine the region for the row.
153
        $function = $row['#region_callback'];
154
        $region_name = $function($row);
155

    
156
        // Add the element in the tree.
157
        $target = &$trees[$region_name][''];
158
        foreach ($parents[$name] as $key) {
159
          $target = &$target['children'][$key];
160
        }
161
        $target['children'][$name] = array('name' => $name, 'weight' => $row['weight']['#value']);
162

    
163
        // Add tabledrag indentation to the first row cell.
164
        if ($depth = count($parents[$name])) {
165
          $children = element_children($row);
166
          $cell = current($children);
167
          $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '');
168
        }
169

    
170
        // Add row id and associate JS settings.
171
        $id = drupal_html_class($name);
172
        $row['#attributes']['id'] = $id;
173
        if (isset($row['#js_settings'])) {
174
          $row['#js_settings'] += array(
175
            'rowHandler' => $row['#row_type'],
176
            'name' => $name,
177
            'region' => $region_name,
178
          );
179
          $js_settings[$id] = $row['#js_settings'];
180
        }
181
      }
182
    }
183
  }
184
  // Determine rendering order from the tree structure.
185
  foreach ($regions as $region_name => $region) {
186
    $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], '_field_ui_reduce_order');
187
  }
188

    
189
  $elements['#attached']['js'][] = array(
190
    'type' => 'setting',
191
    'data' => array('fieldUIRowsData' => $js_settings),
192
  );
193

    
194
  return $elements;
195
}
196

    
197
/**
198
 * Returns HTML for Field UI overview tables.
199
 *
200
 * @param $variables
201
 *   An associative array containing:
202
 *   - elements: An associative array containing a Form API structure to be
203
 *     rendered as a table.
204
 *
205
 * @ingroup themeable
206
 */
207
function theme_field_ui_table($variables) {
208
  $elements = $variables['elements'];
209
  $table = array();
210
  $js_settings = array();
211

    
212
  // Add table headers and attributes.
213
  foreach (array('header', 'attributes') as $key) {
214
    if (isset($elements["#$key"])) {
215
      $table[$key] = $elements["#$key"];
216
    }
217
  }
218

    
219
  // Determine the colspan to use for region rows, by checking the number of
220
  // columns in the headers.
221
  $columns_count = 0;
222
  foreach ($table['header'] as $header) {
223
    $columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1);
224
  }
225

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

    
230
    // Add region rows.
231
    if (isset($region['title'])) {
232
      $table['rows'][] = array(
233
        'class' => array('region-title', 'region-' . $region_name_class . '-title'),
234
        'no_striping' => TRUE,
235
        'data' => array(
236
          array('data' => $region['title'], 'colspan' => $columns_count),
237
        ),
238
      );
239
    }
240
    if (isset($region['message'])) {
241
      $class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated');
242
      $table['rows'][] = array(
243
        'class' => array('region-message', 'region-' . $region_name_class . '-message', $class),
244
        'no_striping' => TRUE,
245
        'data' => array(
246
          array('data' => $region['message'], 'colspan' => $columns_count),
247
        ),
248
      );
249
    }
250

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

    
255
      $row = array('data' => array());
256
      if (isset($element['#attributes'])) {
257
        $row += $element['#attributes'];
258
      }
259

    
260
      // Render children as table cells.
261
      foreach (element_children($element) as $cell_key) {
262
        $child = &$element[$cell_key];
263
        // Do not render a cell for children of #type 'value'.
264
        if (!(isset($child['#type']) && $child['#type'] == 'value')) {
265
          $cell = array('data' => drupal_render($child));
266
          if (isset($child['#cell_attributes'])) {
267
            $cell += $child['#cell_attributes'];
268
          }
269
          $row['data'][] = $cell;
270
        }
271
      }
272
      $table['rows'][] = $row;
273
    }
274
  }
275

    
276
  return theme('table', $table);
277
}
278

    
279
/**
280
 * Form constructor for the 'Manage fields' form of a bundle.
281
 *
282
 * Allows fields and pseudo-fields to be re-ordered.
283
 *
284
 * @see field_ui_field_overview_form_validate()
285
 * @see field_ui_field_overview_form_submit()
286
 * @ingroup forms
287
 */
288
function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle) {
289
  $bundle = field_extract_bundle($entity_type, $bundle);
290

    
291
  field_ui_inactive_message($entity_type, $bundle);
292
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
293

    
294
  // When displaying the form, make sure the list of fields is up-to-date.
295
  if (empty($form_state['post'])) {
296
    field_info_cache_clear();
297
  }
298

    
299
  // Gather bundle information.
300
  $instances = field_info_instances($entity_type, $bundle);
301
  $field_types = field_info_field_types();
302
  $widget_types = field_info_widget_types();
303

    
304
  $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form');
305

    
306
  $form += array(
307
    '#entity_type' => $entity_type,
308
    '#bundle' => $bundle,
309
    '#fields' => array_keys($instances),
310
    '#extra' => array_keys($extra_fields),
311
  );
312

    
313
  $table = array(
314
    '#type' => 'field_ui_table',
315
    '#tree' => TRUE,
316
    '#header' => array(
317
      t('Label'),
318
      t('Weight'),
319
      t('Parent'),
320
      t('Machine name'),
321
      t('Field type'),
322
      t('Widget'),
323
      array('data' => t('Operations'), 'colspan' => 2),
324
    ),
325
    '#parent_options' => array(),
326
    '#regions' => array(
327
      'main' => array('message' => t('No fields are present yet.')),
328
      'add_new' => array('title' => '&nbsp;'),
329
    ),
330
    '#attributes' => array(
331
      'class' => array('field-ui-overview'),
332
      'id' => 'field-overview',
333
    ),
334
  );
335

    
336
  // Fields.
337
  foreach ($instances as $name => $instance) {
338
    $field = field_info_field($instance['field_name']);
339
    $admin_field_path = $admin_path . '/fields/' . $instance['field_name'];
340
    $table[$name] = array(
341
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
342
      '#row_type' => 'field',
343
      '#region_callback' => 'field_ui_field_overview_row_region',
344
      'label' => array(
345
        '#markup' => check_plain($instance['label']),
346
      ),
347
      'weight' => array(
348
        '#type' => 'textfield',
349
        '#title' => t('Weight for @title', array('@title' => $instance['label'])),
350
        '#title_display' => 'invisible',
351
        '#default_value' => $instance['widget']['weight'],
352
        '#size' => 3,
353
        '#attributes' => array('class' => array('field-weight')),
354
       ),
355
      'parent_wrapper' => array(
356
        'parent' => array(
357
          '#type' => 'select',
358
          '#title' => t('Parent for @title', array('@title' => $instance['label'])),
359
          '#title_display' => 'invisible',
360
          '#options' => $table['#parent_options'],
361
          '#empty_value' => '',
362
          '#attributes' => array('class' => array('field-parent')),
363
          '#parents' => array('fields', $name, 'parent'),
364
        ),
365
        'hidden_name' => array(
366
          '#type' => 'hidden',
367
          '#default_value' => $name,
368
          '#attributes' => array('class' => array('field-name')),
369
        ),
370
      ),
371
      'field_name' => array(
372
        '#markup' => $instance['field_name'],
373
      ),
374
      'type' => array(
375
        '#type' => 'link',
376
        '#title' => t($field_types[$field['type']]['label']),
377
        '#href' => $admin_field_path . '/field-settings',
378
        '#options' => array('attributes' => array('title' => t('Edit field settings.'))),
379
      ),
380
      'widget_type' => array(
381
        '#type' => 'link',
382
        '#title' => t($widget_types[$instance['widget']['type']]['label']),
383
        '#href' => $admin_field_path . '/widget-type',
384
        '#options' => array('attributes' => array('title' => t('Change widget type.'))),
385
      ),
386
      'edit' => array(
387
        '#type' => 'link',
388
        '#title' => t('edit'),
389
        '#href' => $admin_field_path,
390
        '#options' => array('attributes' => array('title' => t('Edit instance settings.'))),
391
      ),
392
      'delete' => array(
393
        '#type' => 'link',
394
        '#title' => t('delete'),
395
        '#href' => $admin_field_path . '/delete',
396
        '#options' => array('attributes' => array('title' => t('Delete instance.'))),
397
      ),
398
    );
399

    
400
    if (!empty($instance['locked'])) {
401
      $table[$name]['edit'] = array('#value' => t('Locked'));
402
      $table[$name]['delete'] = array();
403
      $table[$name]['#attributes']['class'][] = 'menu-disabled';
404
    }
405
  }
406

    
407
  // Non-field elements.
408
  foreach ($extra_fields as $name => $extra_field) {
409
    $table[$name] = array(
410
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
411
      '#row_type' => 'extra_field',
412
      '#region_callback' => 'field_ui_field_overview_row_region',
413
      'label' => array(
414
        '#markup' => check_plain($extra_field['label']),
415
      ),
416
      'weight' => array(
417
        '#type' => 'textfield',
418
        '#default_value' => $extra_field['weight'],
419
        '#size' => 3,
420
        '#attributes' => array('class' => array('field-weight')),
421
        '#title_display' => 'invisible',
422
        '#title' => t('Weight for @title', array('@title' => $extra_field['label'])),
423
      ),
424
      'parent_wrapper' => array(
425
        'parent' => array(
426
          '#type' => 'select',
427
          '#title' => t('Parent for @title', array('@title' => $extra_field['label'])),
428
          '#title_display' => 'invisible',
429
          '#options' => $table['#parent_options'],
430
          '#empty_value' => '',
431
          '#attributes' => array('class' => array('field-parent')),
432
          '#parents' => array('fields', $name, 'parent'),
433
        ),
434
        'hidden_name' => array(
435
          '#type' => 'hidden',
436
          '#default_value' => $name,
437
          '#attributes' => array('class' => array('field-name')),
438
        ),
439
      ),
440
      'field_name' => array(
441
        '#markup' => $name,
442
      ),
443
      'type' => array(
444
        '#markup' => isset($extra_field['description']) ? $extra_field['description'] : '',
445
        '#cell_attributes' => array('colspan' => 2),
446
      ),
447
      'edit' => array(
448
        '#markup' => isset($extra_field['edit']) ? $extra_field['edit'] : '',
449
      ),
450
      'delete' => array(
451
        '#markup' => isset($extra_field['delete']) ? $extra_field['delete'] : '',
452
      ),
453
    );
454
  }
455

    
456
  // Additional row: add new field.
457
  $max_weight = field_info_max_weight($entity_type, $bundle, 'form');
458
  $field_type_options = field_ui_field_type_options();
459
  $widget_type_options = field_ui_widget_type_options(NULL, TRUE);
460
  if ($field_type_options && $widget_type_options) {
461
    $name = '_add_new_field';
462
    $table[$name] = array(
463
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')),
464
      '#row_type' => 'add_new_field',
465
      '#region_callback' => 'field_ui_field_overview_row_region',
466
      'label' => array(
467
        '#type' => 'textfield',
468
        '#title' => t('New field label'),
469
        '#title_display' => 'invisible',
470
        '#size' => 15,
471
        '#description' => t('Label'),
472
        '#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . t('Add new field') .'</div>',
473
        '#suffix' => '</div>',
474
      ),
475
      'weight' => array(
476
        '#type' => 'textfield',
477
        '#default_value' => $max_weight + 1,
478
        '#size' => 3,
479
        '#title_display' => 'invisible',
480
        '#title' => t('Weight for new field'),
481
        '#attributes' => array('class' => array('field-weight')),
482
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
483
      ),
484
      'parent_wrapper' => array(
485
        'parent' => array(
486
          '#type' => 'select',
487
          '#title' => t('Parent for new field'),
488
          '#title_display' => 'invisible',
489
          '#options' => $table['#parent_options'],
490
          '#empty_value' => '',
491
          '#attributes' => array('class' => array('field-parent')),
492
          '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
493
          '#parents' => array('fields', $name, 'parent'),
494
        ),
495
        'hidden_name' => array(
496
          '#type' => 'hidden',
497
          '#default_value' => $name,
498
          '#attributes' => array('class' => array('field-name')),
499
        ),
500
      ),
501
      'field_name' => array(
502
        '#type' => 'machine_name',
503
        '#title' => t('New field name'),
504
        '#title_display' => 'invisible',
505
        // This field should stay LTR even for RTL languages.
506
        '#field_prefix' => '<span dir="ltr">field_',
507
        '#field_suffix' => '</span>&lrm;',
508
        '#size' => 15,
509
        '#description' => t('A unique machine-readable name containing letters, numbers, and underscores.'),
510
        // 32 characters minus the 'field_' prefix.
511
        '#maxlength' => 26,
512
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
513
        '#machine_name' => array(
514
          'source' => array('fields', $name, 'label'),
515
          'exists' => '_field_ui_field_name_exists',
516
          'standalone' => TRUE,
517
          'label' => '',
518
        ),
519
        '#required' => FALSE,
520
      ),
521
      'type' => array(
522
        '#type' => 'select',
523
        '#title' => t('Type of new field'),
524
        '#title_display' => 'invisible',
525
        '#options' => $field_type_options,
526
        '#empty_option' => t('- Select a field type -'),
527
        '#description' => t('Type of data to store.'),
528
        '#attributes' => array('class' => array('field-type-select')),
529
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
530
      ),
531
      'widget_type' => array(
532
        '#type' => 'select',
533
        '#title' => t('Widget for new field'),
534
        '#title_display' => 'invisible',
535
        '#options' => $widget_type_options,
536
        '#empty_option' => t('- Select a widget -'),
537
        '#description' => t('Form element to edit the data.'),
538
        '#attributes' => array('class' => array('widget-type-select')),
539
        '#cell_attributes' => array('colspan' => 3),
540
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
541
      ),
542
      // Place the 'translatable' property as an explicit value so that contrib
543
      // modules can form_alter() the value for newly created fields.
544
      'translatable' => array(
545
        '#type' => 'value',
546
        '#value' => FALSE,
547
      ),
548
    );
549
  }
550

    
551
  // Additional row: add existing field.
552
  $existing_fields = field_ui_existing_field_options($entity_type, $bundle);
553
  if ($existing_fields && $widget_type_options) {
554
    // Build list of options.
555
    $existing_field_options = array();
556
    foreach ($existing_fields as $field_name => $info) {
557
      $text = t('@type: @field (@label)', array(
558
        '@type' => $info['type_label'],
559
        '@label' => $info['label'],
560
        '@field' => $info['field'],
561
      ));
562
      $existing_field_options[$field_name] = truncate_utf8($text, 80, FALSE, TRUE);
563
    }
564
    asort($existing_field_options);
565
    $name = '_add_existing_field';
566
    $table[$name] = array(
567
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')),
568
      '#row_type' => 'add_new_field',
569
      '#region_callback' => 'field_ui_field_overview_row_region',
570
      'label' => array(
571
        '#type' => 'textfield',
572
        '#title' => t('Existing field label'),
573
        '#title_display' => 'invisible',
574
        '#size' => 15,
575
        '#description' => t('Label'),
576
        '#attributes' => array('class' => array('label-textfield')),
577
        '#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . t('Add existing field') .'</div>',
578
        '#suffix' => '</div>',
579
      ),
580
      'weight' => array(
581
        '#type' => 'textfield',
582
        '#default_value' => $max_weight + 2,
583
        '#size' => 3,
584
        '#title_display' => 'invisible',
585
        '#title' => t('Weight for added field'),
586
        '#attributes' => array('class' => array('field-weight')),
587
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
588
      ),
589
      'parent_wrapper' => array(
590
        'parent' => array(
591
          '#type' => 'select',
592
          '#title' => t('Parent for existing field'),
593
          '#title_display' => 'invisible',
594
          '#options' => $table['#parent_options'],
595
          '#empty_value' => '',
596
          '#attributes' => array('class' => array('field-parent')),
597
          '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
598
          '#parents' => array('fields', $name, 'parent'),
599
        ),
600
        'hidden_name' => array(
601
          '#type' => 'hidden',
602
          '#default_value' => $name,
603
          '#attributes' => array('class' => array('field-name')),
604
        ),
605
      ),
606
      'field_name' => array(
607
        '#type' => 'select',
608
        '#title' => t('Existing field to share'),
609
        '#title_display' => 'invisible',
610
        '#options' => $existing_field_options,
611
        '#empty_option' => t('- Select an existing field -'),
612
        '#description' => t('Field to share'),
613
        '#attributes' => array('class' => array('field-select')),
614
        '#cell_attributes' => array('colspan' => 2),
615
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
616
      ),
617
      'widget_type' => array(
618
        '#type' => 'select',
619
        '#title' => t('Widget for existing field'),
620
        '#title_display' => 'invisible',
621
        '#options' => $widget_type_options,
622
        '#empty_option' => t('- Select a widget -'),
623
        '#description' => t('Form element to edit the data.'),
624
        '#attributes' => array('class' => array('widget-type-select')),
625
        '#cell_attributes' => array('colspan' => 3),
626
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
627
      ),
628
    );
629
  }
630
  $form['fields'] = $table;
631

    
632
  $form['actions'] = array('#type' => 'actions');
633
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
634

    
635
  $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css';
636
  $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js';
637

    
638
  // Add settings for the update selects behavior.
639
  $js_fields = array();
640
  foreach ($existing_fields as $field_name => $info) {
641
    $js_fields[$field_name] = array('label' => $info['label'], 'type' => $info['type'], 'widget' => $info['widget_type']);
642
  }
643

    
644
  $form['#attached']['js'][] = array(
645
    'type' => 'setting',
646
    'data' => array('fields' => $js_fields, 'fieldWidgetTypes' => field_ui_widget_type_options()),
647
  );
648

    
649
  // Add tabledrag behavior.
650
  $form['#attached']['drupal_add_tabledrag'][] = array('field-overview', 'order', 'sibling', 'field-weight');
651
  $form['#attached']['drupal_add_tabledrag'][] = array('field-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name');
652

    
653
  return $form;
654
}
655

    
656
/**
657
 * Form validation handler for field_ui_field_overview_form().
658
 *
659
 * @see field_ui_field_overview_form_submit()
660
 */
661
function field_ui_field_overview_form_validate($form, &$form_state) {
662
  _field_ui_field_overview_form_validate_add_new($form, $form_state);
663
  _field_ui_field_overview_form_validate_add_existing($form, $form_state);
664
}
665

    
666
/**
667
 * Validates the 'add new field' row of field_ui_field_overview_form().
668
 *
669
 * @see field_ui_field_overview_form_validate()
670
 */
671
function _field_ui_field_overview_form_validate_add_new($form, &$form_state) {
672
  $field = $form_state['values']['fields']['_add_new_field'];
673

    
674
  // Validate if any information was provided in the 'add new field' row.
675
  if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) {
676
    // Missing label.
677
    if (!$field['label']) {
678
      form_set_error('fields][_add_new_field][label', t('Add new field: you need to provide a label.'));
679
    }
680

    
681
    // Missing field name.
682
    if (!$field['field_name']) {
683
      form_set_error('fields][_add_new_field][field_name', t('Add new field: you need to provide a field name.'));
684
    }
685
    // Field name validation.
686
    else {
687
      $field_name = $field['field_name'];
688

    
689
      // Add the 'field_' prefix.
690
      $field_name = 'field_' . $field_name;
691
      form_set_value($form['fields']['_add_new_field']['field_name'], $field_name, $form_state);
692
    }
693

    
694
    // Missing field type.
695
    if (!$field['type']) {
696
      form_set_error('fields][_add_new_field][type', t('Add new field: you need to select a field type.'));
697
    }
698

    
699
    // Missing widget type.
700
    if (!$field['widget_type']) {
701
      form_set_error('fields][_add_new_field][widget_type', t('Add new field: you need to select a widget.'));
702
    }
703
    // Wrong widget type.
704
    elseif ($field['type']) {
705
      $widget_types = field_ui_widget_type_options($field['type']);
706
      if (!isset($widget_types[$field['widget_type']])) {
707
        form_set_error('fields][_add_new_field][widget_type', t('Add new field: invalid widget.'));
708
      }
709
    }
710
  }
711
}
712

    
713
/**
714
 * Render API callback: Checks if a field machine name is taken.
715
 *
716
 * @param $value
717
 *   The machine name, not prefixed with 'field_'.
718
 *
719
 * @return
720
 *   Whether or not the field machine name is taken.
721
 */
722
function _field_ui_field_name_exists($value) {
723
  // Prefix with 'field_'.
724
  $field_name = 'field_' . $value;
725

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

    
731
/**
732
 * Validates the 'add existing field' row of field_ui_field_overview_form().
733
 *
734
 * @see field_ui_field_overview_form_validate()
735
 */
736
function _field_ui_field_overview_form_validate_add_existing($form, &$form_state) {
737
  // The form element might be absent if no existing fields can be added to
738
  // this bundle.
739
  if (isset($form_state['values']['fields']['_add_existing_field'])) {
740
    $field = $form_state['values']['fields']['_add_existing_field'];
741

    
742
    // Validate if any information was provided in the 'add existing field' row.
743
    if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) {
744
      // Missing label.
745
      if (!$field['label']) {
746
        form_set_error('fields][_add_existing_field][label', t('Add existing field: you need to provide a label.'));
747
      }
748

    
749
      // Missing existing field name.
750
      if (!$field['field_name']) {
751
        form_set_error('fields][_add_existing_field][field_name', t('Add existing field: you need to select a field.'));
752
      }
753

    
754
      // Missing widget type.
755
      if (!$field['widget_type']) {
756
        form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: you need to select a widget.'));
757
      }
758
      // Wrong widget type.
759
      elseif ($field['field_name'] && ($existing_field = field_info_field($field['field_name']))) {
760
        $widget_types = field_ui_widget_type_options($existing_field['type']);
761
        if (!isset($widget_types[$field['widget_type']])) {
762
          form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: invalid widget.'));
763
        }
764
      }
765
    }
766
  }
767
}
768

    
769
/**
770
 * Form submission handler for field_ui_field_overview_form().
771
 *
772
 * @see field_ui_field_overview_form_validate()
773
 */
774
function field_ui_field_overview_form_submit($form, &$form_state) {
775
  $form_values = $form_state['values']['fields'];
776
  $entity_type = $form['#entity_type'];
777
  $bundle = $form['#bundle'];
778
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
779

    
780
  $bundle_settings = field_bundle_settings($entity_type, $bundle);
781

    
782
  // Update field weights.
783
  foreach ($form_values as $key => $values) {
784
    if (in_array($key, $form['#fields'])) {
785
      $instance = field_read_instance($entity_type, $key, $bundle);
786
      $instance['widget']['weight'] = $values['weight'];
787
      field_update_instance($instance);
788
    }
789
    elseif (in_array($key, $form['#extra'])) {
790
      $bundle_settings['extra_fields']['form'][$key]['weight'] = $values['weight'];
791
    }
792
  }
793

    
794
  field_bundle_settings($entity_type, $bundle, $bundle_settings);
795

    
796
  $destinations = array();
797

    
798
  // Create new field.
799
  $field = array();
800
  if (!empty($form_values['_add_new_field']['field_name'])) {
801
    $values = $form_values['_add_new_field'];
802

    
803
    $field = array(
804
      'field_name' => $values['field_name'],
805
      'type' => $values['type'],
806
      'translatable' => $values['translatable'],
807
    );
808
    $instance = array(
809
      'field_name' => $field['field_name'],
810
      'entity_type' => $entity_type,
811
      'bundle' => $bundle,
812
      'label' => $values['label'],
813
      'widget' => array(
814
        'type' => $values['widget_type'],
815
        'weight' => $values['weight'],
816
      ),
817
    );
818

    
819
    // Create the field and instance.
820
    try {
821
      field_create_field($field);
822
      field_create_instance($instance);
823

    
824
      $destinations[] = $admin_path . '/fields/' . $field['field_name'] . '/field-settings';
825
      $destinations[] = $admin_path . '/fields/' . $field['field_name'];
826

    
827
      // Store new field information for any additional submit handlers.
828
      $form_state['fields_added']['_add_new_field'] = $field['field_name'];
829
    }
830
    catch (Exception $e) {
831
      drupal_set_message(t('There was a problem creating field %label: !message', array('%label' => $instance['label'], '!message' => $e->getMessage())), 'error');
832
    }
833
  }
834

    
835
  // Add existing field.
836
  if (!empty($form_values['_add_existing_field']['field_name'])) {
837
    $values = $form_values['_add_existing_field'];
838
    $field = field_info_field($values['field_name']);
839
    if (!empty($field['locked'])) {
840
      drupal_set_message(t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error');
841
    }
842
    else {
843
      $instance = array(
844
        'field_name' => $field['field_name'],
845
        'entity_type' => $entity_type,
846
        'bundle' => $bundle,
847
        'label' => $values['label'],
848
        'widget' => array(
849
          'type' => $values['widget_type'],
850
          'weight' => $values['weight'],
851
        ),
852
      );
853

    
854
      try {
855
        field_create_instance($instance);
856
        $destinations[] = $admin_path . '/fields/' . $instance['field_name'] . '/edit';
857
        // Store new field information for any additional submit handlers.
858
        $form_state['fields_added']['_add_existing_field'] = $instance['field_name'];
859
      }
860
      catch (Exception $e) {
861
        drupal_set_message(t('There was a problem creating field instance %label: @message.', array('%label' => $instance['label'], '@message' => $e->getMessage())), 'error');
862
      }
863
    }
864
  }
865

    
866
  if ($destinations) {
867
    $destination = drupal_get_destination();
868
    $destinations[] = $destination['destination'];
869
    unset($_GET['destination']);
870
    $form_state['redirect'] = field_ui_get_destinations($destinations);
871
  }
872
  else {
873
    drupal_set_message(t('Your settings have been saved.'));
874
  }
875
}
876

    
877
/**
878
 * Form constructor for the field display settings for a given view mode.
879
 *
880
 * @see field_ui_display_overview_multistep_submit()
881
 * @see field_ui_display_overview_form_submit()
882
 * @ingroup forms
883
 */
884
function field_ui_display_overview_form($form, &$form_state, $entity_type, $bundle, $view_mode) {
885
  $bundle = field_extract_bundle($entity_type, $bundle);
886

    
887
  field_ui_inactive_message($entity_type, $bundle);
888
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
889

    
890
  // Gather type information.
891
  $instances = field_info_instances($entity_type, $bundle);
892
  $field_types = field_info_field_types();
893
  $extra_fields = field_info_extra_fields($entity_type, $bundle, 'display');
894

    
895
  $form_state += array(
896
    'formatter_settings_edit' => NULL,
897
  );
898

    
899
  $form += array(
900
    '#entity_type' => $entity_type,
901
    '#bundle' => $bundle,
902
    '#view_mode' => $view_mode,
903
    '#fields' => array_keys($instances),
904
    '#extra' => array_keys($extra_fields),
905
  );
906

    
907
  if (empty($instances) && empty($extra_fields)) {
908
    drupal_set_message(t('There are no fields yet added. You can add new fields on the <a href="@link">Manage fields</a> page.', array('@link' => url($admin_path . '/fields'))), 'warning');
909
    return $form;
910
  }
911

    
912
  $table = array(
913
    '#type' => 'field_ui_table',
914
    '#tree' => TRUE,
915
    '#header' => array(
916
      t('Field'),
917
      t('Weight'),
918
      t('Parent'),
919
      t('Label'),
920
      array('data' => t('Format'), 'colspan' => 3),
921
    ),
922
    '#regions' => array(
923
      'visible' => array('message' => t('No field is displayed.')),
924
      'hidden' => array('title' => t('Hidden'), 'message' => t('No field is hidden.')),
925
    ),
926
    '#parent_options' => array(),
927
    '#attributes' => array(
928
      'class' => array('field-ui-overview'),
929
      'id' => 'field-display-overview',
930
    ),
931
    // Add Ajax wrapper.
932
    '#prefix' => '<div id="field-display-overview-wrapper">',
933
    '#suffix' => '</div>',
934
  );
935

    
936
  $field_label_options = array(
937
    'above' => t('Above'),
938
    'inline' => t('Inline'),
939
    'hidden' => '<' . t('Hidden') . '>',
940
  );
941
  $extra_visibility_options = array(
942
    'visible' => t('Visible'),
943
    'hidden' => t('Hidden'),
944
  );
945

    
946
  // Field rows.
947
  foreach ($instances as $name => $instance) {
948
    $field = field_info_field($instance['field_name']);
949
    $display = $instance['display'][$view_mode];
950
    $table[$name] = array(
951
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
952
      '#row_type' => 'field',
953
      '#region_callback' => 'field_ui_display_overview_row_region',
954
      '#js_settings' => array(
955
        'rowHandler' => 'field',
956
        'defaultFormatter' => $field_types[$field['type']]['default_formatter'],
957
      ),
958
      'human_name' => array(
959
        '#markup' => check_plain($instance['label']),
960
      ),
961
      'weight' => array(
962
        '#type' => 'textfield',
963
        '#title' => t('Weight for @title', array('@title' => $instance['label'])),
964
        '#title_display' => 'invisible',
965
        '#default_value' => $display['weight'],
966
        '#size' => 3,
967
        '#attributes' => array('class' => array('field-weight')),
968
      ),
969
      'parent_wrapper' => array(
970
        'parent' => array(
971
          '#type' => 'select',
972
          '#title' => t('Label display for @title', array('@title' => $instance['label'])),
973
          '#title_display' => 'invisible',
974
          '#options' => $table['#parent_options'],
975
          '#empty_value' => '',
976
          '#attributes' => array('class' => array('field-parent')),
977
          '#parents' => array('fields', $name, 'parent'),
978
        ),
979
        'hidden_name' => array(
980
          '#type' => 'hidden',
981
          '#default_value' => $name,
982
          '#attributes' => array('class' => array('field-name')),
983
        ),
984
      ),
985
      'label' => array(
986
        '#type' => 'select',
987
        '#title' => t('Label display for @title', array('@title' => $instance['label'])),
988
        '#title_display' => 'invisible',
989
        '#options' => $field_label_options,
990
        '#default_value' => $display['label'],
991
      ),
992
    );
993

    
994
    $formatter_options = field_ui_formatter_options($field['type']);
995
    $formatter_options['hidden'] = '<' . t('Hidden') . '>';
996
    $table[$name]['format'] = array(
997
      'type' => array(
998
        '#type' => 'select',
999
        '#title' => t('Formatter for @title', array('@title' => $instance['label'])),
1000
        '#title_display' => 'invisible',
1001
        '#options' => $formatter_options,
1002
        '#default_value' => $display['type'],
1003
        '#parents' => array('fields', $name, 'type'),
1004
        '#attributes' => array('class' => array('field-formatter-type')),
1005
      ),
1006
      'settings_edit_form' => array(),
1007
    );
1008

    
1009
    // Formatter settings.
1010

    
1011
    // Check the currently selected formatter, and merge persisted values for
1012
    // formatter settings.
1013
    if (isset($form_state['values']['fields'][$name]['type'])) {
1014
      $formatter_type = $form_state['values']['fields'][$name]['type'];
1015
    }
1016
    else {
1017
      $formatter_type = $display['type'];
1018
    }
1019
    if (isset($form_state['formatter_settings'][$name])) {
1020
      $settings = $form_state['formatter_settings'][$name];
1021
    }
1022
    else {
1023
      $settings = $display['settings'];
1024
    }
1025
    $settings += field_info_formatter_settings($formatter_type);
1026

    
1027
    $instance['display'][$view_mode]['type'] = $formatter_type;
1028
    $formatter = field_info_formatter_types($formatter_type);
1029
    // For hidden fields, $formatter will be NULL, but we expect an array later.
1030
    // To maintain BC, but avoid PHP 7.4 Notices, ensure $formatter is an array
1031
    // with a 'module' element.
1032
    $formatter['module'] = isset($formatter['module']) ? $formatter['module'] : '';
1033
    $instance['display'][$view_mode]['module'] = $formatter['module'];
1034
    $instance['display'][$view_mode]['settings'] = $settings;
1035

    
1036
    // Base button element for the various formatter settings actions.
1037
    $base_button = array(
1038
      '#submit' => array('field_ui_display_overview_multistep_submit'),
1039
      '#ajax' => array(
1040
        'callback' => 'field_ui_display_overview_multistep_js',
1041
        'wrapper' => 'field-display-overview-wrapper',
1042
        'effect' => 'fade',
1043
      ),
1044
      '#field_name' => $name,
1045
    );
1046

    
1047
    if ($form_state['formatter_settings_edit'] == $name) {
1048
      // We are currently editing this field's formatter settings. Display the
1049
      // settings form and submit buttons.
1050
      $table[$name]['format']['settings_edit_form'] = array();
1051

    
1052
      $settings_form = array();
1053
      $function = $formatter['module'] . '_field_formatter_settings_form';
1054
      if (function_exists($function)) {
1055
        $settings_form = $function($field, $instance, $view_mode, $form, $form_state);
1056
      }
1057

    
1058
      if ($settings_form) {
1059
        $table[$name]['format']['#cell_attributes'] = array('colspan' => 3);
1060
        $table[$name]['format']['settings_edit_form'] = array(
1061
          '#type' => 'container',
1062
          '#attributes' => array('class' => array('field-formatter-settings-edit-form')),
1063
          '#parents' => array('fields', $name, 'settings_edit_form'),
1064
          'label' => array(
1065
            '#markup' => t('Format settings:') . ' <span class="formatter-name">' . $formatter['label'] . '</span>',
1066
          ),
1067
          'settings' => $settings_form,
1068
          'actions' => array(
1069
            '#type' => 'actions',
1070
            'save_settings' => $base_button + array(
1071
              '#type' => 'submit',
1072
              '#name' => $name . '_formatter_settings_update',
1073
              '#value' => t('Update'),
1074
              '#op' => 'update',
1075
            ),
1076
            'cancel_settings' => $base_button + array(
1077
              '#type' => 'submit',
1078
              '#name' => $name . '_formatter_settings_cancel',
1079
              '#value' => t('Cancel'),
1080
              '#op' => 'cancel',
1081
              // Do not check errors for the 'Cancel' button, but make sure we
1082
              // get the value of the 'formatter type' select.
1083
              '#limit_validation_errors' => array(array('fields', $name, 'type')),
1084
            ),
1085
          ),
1086
        );
1087
        $table[$name]['#attributes']['class'][] = 'field-formatter-settings-editing';
1088
      }
1089
    }
1090
    else {
1091
      // Display a summary of the current formatter settings.
1092
      $summary = module_invoke($formatter['module'], 'field_formatter_settings_summary', $field, $instance, $view_mode);
1093
      $table[$name]['settings_summary'] = array();
1094
      $table[$name]['settings_edit'] = array();
1095
      if ($summary) {
1096
        $table[$name]['settings_summary'] = array(
1097
          '#markup' => '<div class="field-formatter-summary">' . $summary . '</div>',
1098
          '#cell_attributes' => array('class' => array('field-formatter-summary-cell')),
1099
        );
1100
        $table[$name]['settings_edit'] = $base_button + array(
1101
          '#type' => 'image_button',
1102
          '#name' => $name . '_formatter_settings_edit',
1103
          '#src' => 'misc/configure.png',
1104
          '#attributes' => array('class' => array('field-formatter-settings-edit'), 'alt' => t('Edit')),
1105
          '#op' => 'edit',
1106
          // Do not check errors for the 'Edit' button, but make sure we get
1107
          // the value of the 'formatter type' select.
1108
          '#limit_validation_errors' => array(array('fields', $name, 'type')),
1109
          '#prefix' => '<div class="field-formatter-settings-edit-wrapper">',
1110
          '#suffix' => '</div>',
1111
        );
1112
      }
1113
    }
1114
  }
1115

    
1116
  // Non-field elements.
1117
  foreach ($extra_fields as $name => $extra_field) {
1118
    $display = $extra_field['display'][$view_mode];
1119
    $table[$name] = array(
1120
      '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
1121
      '#row_type' => 'extra_field',
1122
      '#region_callback' => 'field_ui_display_overview_row_region',
1123
      '#js_settings' => array('rowHandler' => 'field'),
1124
      'human_name' => array(
1125
        '#markup' => check_plain($extra_field['label']),
1126
      ),
1127
      'weight' => array(
1128
        '#type' => 'textfield',
1129
        '#title' => t('Weight for @title', array('@title' => $extra_field['label'])),
1130
        '#title_display' => 'invisible',
1131
        '#default_value' => $display['weight'],
1132
        '#size' => 3,
1133
        '#attributes' => array('class' => array('field-weight')),
1134
      ),
1135
      'parent_wrapper' => array(
1136
        'parent' => array(
1137
          '#type' => 'select',
1138
          '#title' => t('Parents for @title', array('@title' => $extra_field['label'])),
1139
          '#title_display' => 'invisible',
1140
          '#options' => $table['#parent_options'],
1141
          '#empty_value' => '',
1142
          '#attributes' => array('class' => array('field-parent')),
1143
          '#parents' => array('fields', $name, 'parent'),
1144
        ),
1145
        'hidden_name' => array(
1146
          '#type' => 'hidden',
1147
          '#default_value' => $name,
1148
          '#attributes' => array('class' => array('field-name')),
1149
        ),
1150
      ),
1151
      'empty_cell' => array(
1152
        '#markup' => '&nbsp;',
1153
      ),
1154
      'format' => array(
1155
        'type' => array(
1156
          '#type' => 'select',
1157
          '#title' => t('Visibility for @title', array('@title' => $extra_field['label'])),
1158
          '#title_display' => 'invisible',
1159
          '#options' => $extra_visibility_options,
1160
          '#default_value' => $display['visible'] ? 'visible' : 'hidden',
1161
          '#parents' => array('fields', $name, 'type'),
1162
          '#attributes' => array('class' => array('field-formatter-type')),
1163
        ),
1164
      ),
1165
      'settings_summary' => array(),
1166
      'settings_edit' => array(),
1167
    );
1168
  }
1169

    
1170
  $form['fields'] = $table;
1171

    
1172
  // Custom display settings.
1173
  if ($view_mode == 'default') {
1174
    $form['modes'] = array(
1175
      '#type' => 'fieldset',
1176
      '#title' => t('Custom display settings'),
1177
      '#collapsible' => TRUE,
1178
      '#collapsed' => TRUE,
1179
    );
1180
    // Collect options and default values for the 'Custom display settings'
1181
    // checkboxes.
1182
    $options = array();
1183
    $default = array();
1184
    $entity_info = entity_get_info($entity_type);
1185
    $view_modes = $entity_info['view modes'];
1186
    $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
1187
    foreach ($view_modes as $view_mode_name => $view_mode_info) {
1188
      $options[$view_mode_name] = $view_mode_info['label'];
1189
      if (!empty($view_mode_settings[$view_mode_name]['custom_settings'])) {
1190
        $default[] = $view_mode_name;
1191
      }
1192
    }
1193
    $form['modes']['view_modes_custom'] = array(
1194
      '#type' => 'checkboxes',
1195
      '#title' => t('Use custom display settings for the following view modes'),
1196
      '#options' => $options,
1197
      '#default_value' => $default,
1198
    );
1199
  }
1200

    
1201
  // In overviews involving nested rows from contributed modules (i.e
1202
  // field_group), the 'format type' selects can trigger a series of changes in
1203
  // child rows. The #ajax behavior is therefore not attached directly to the
1204
  // selects, but triggered by the client-side script through a hidden #ajax
1205
  // 'Refresh' button. A hidden 'refresh_rows' input tracks the name of
1206
  // affected rows.
1207
  $form['refresh_rows'] = array('#type' => 'hidden');
1208
  $form['refresh'] = array(
1209
    '#type' => 'submit',
1210
    '#value' => t('Refresh'),
1211
    '#op' => 'refresh_table',
1212
    '#submit' => array('field_ui_display_overview_multistep_submit'),
1213
    '#ajax' => array(
1214
      'callback' => 'field_ui_display_overview_multistep_js',
1215
      'wrapper' => 'field-display-overview-wrapper',
1216
      'effect' => 'fade',
1217
      // The button stays hidden, so we hide the Ajax spinner too. Ad-hoc
1218
      // spinners will be added manually by the client-side script.
1219
      'progress' => 'none',
1220
    ),
1221
  );
1222

    
1223
  $form['actions'] = array('#type' => 'actions');
1224
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
1225

    
1226
  $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js';
1227
  $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css';
1228

    
1229
  // Add tabledrag behavior.
1230
  $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'order', 'sibling', 'field-weight');
1231
  $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name');
1232

    
1233
  return $form;
1234
}
1235

    
1236

    
1237
/**
1238
 * Form submission handler for buttons in field_ui_display_overview_form().
1239
 */
1240
function field_ui_display_overview_multistep_submit($form, &$form_state) {
1241
  $trigger = $form_state['triggering_element'];
1242
  $op = $trigger['#op'];
1243

    
1244
  switch ($op) {
1245
    case 'edit':
1246
      // Store the field whose settings are currently being edited.
1247
      $field_name = $trigger['#field_name'];
1248
      $form_state['formatter_settings_edit'] = $field_name;
1249
      break;
1250

    
1251
    case 'update':
1252
      // Store the saved settings, and set the field back to 'non edit' mode.
1253
      $field_name = $trigger['#field_name'];
1254
      $values = $form_state['values']['fields'][$field_name]['settings_edit_form']['settings'];
1255
      $form_state['formatter_settings'][$field_name] = $values;
1256
      unset($form_state['formatter_settings_edit']);
1257
      break;
1258

    
1259
    case 'cancel':
1260
      // Set the field back to 'non edit' mode.
1261
      unset($form_state['formatter_settings_edit']);
1262
      break;
1263

    
1264
    case 'refresh_table':
1265
      // If the currently edited field is one of the rows to be refreshed, set
1266
      // it back to 'non edit' mode.
1267
      $updated_rows = explode(' ', $form_state['values']['refresh_rows']);
1268
      if (isset($form_state['formatter_settings_edit']) && in_array($form_state['formatter_settings_edit'], $updated_rows)) {
1269
        unset($form_state['formatter_settings_edit']);
1270
      }
1271
      break;
1272
  }
1273

    
1274
  $form_state['rebuild'] = TRUE;
1275
}
1276

    
1277
/**
1278
 * Ajax handler for multistep buttons on the 'Manage display' screen.
1279
 */
1280
function field_ui_display_overview_multistep_js($form, &$form_state) {
1281
  $trigger = $form_state['triggering_element'];
1282
  $op = $trigger['#op'];
1283

    
1284
  // Pick the elements that need to receive the ajax-new-content effect.
1285
  switch ($op) {
1286
    case 'edit':
1287
      $updated_rows = array($trigger['#field_name']);
1288
      $updated_columns = array('format');
1289
      break;
1290

    
1291
    case 'update':
1292
    case 'cancel':
1293
      $updated_rows = array($trigger['#field_name']);
1294
      $updated_columns = array('format', 'settings_summary', 'settings_edit');
1295
      break;
1296

    
1297
    case 'refresh_table':
1298
      $updated_rows = array_values(explode(' ', $form_state['values']['refresh_rows']));
1299
      $updated_columns = array('settings_summary', 'settings_edit');
1300
      break;
1301
  }
1302

    
1303
  foreach ($updated_rows as $name) {
1304
    foreach ($updated_columns as $key) {
1305
      $element = &$form['fields'][$name][$key];
1306
      $element['#prefix'] = '<div class="ajax-new-content">' . (isset($element['#prefix']) ? $element['#prefix'] : '');
1307
      $element['#suffix'] = (isset($element['#suffix']) ? $element['#suffix'] : '') . '</div>';
1308
    }
1309
  }
1310

    
1311
  // Return the whole table.
1312
  return $form['fields'];
1313
}
1314

    
1315
/**
1316
 * Form submission handler for field_ui_display_overview_form().
1317
 */
1318
function field_ui_display_overview_form_submit($form, &$form_state) {
1319
  $form_values = $form_state['values'];
1320
  $entity_type = $form['#entity_type'];
1321
  $bundle = $form['#bundle'];
1322
  $view_mode = $form['#view_mode'];
1323

    
1324
  // Save data for 'regular' fields.
1325
  foreach ($form['#fields'] as $field_name) {
1326
    // Retrieve the stored instance settings to merge with the incoming values.
1327
    $instance = field_read_instance($entity_type, $field_name, $bundle);
1328
    $values = $form_values['fields'][$field_name];
1329
    // Get formatter settings. They lie either directly in submitted form
1330
    // values (if the whole form was submitted while some formatter
1331
    // settings were being edited), or have been persisted in
1332
    // $form_state.
1333
    $settings = array();
1334
    if (isset($values['settings_edit_form']['settings'])) {
1335
      $settings = $values['settings_edit_form']['settings'];
1336
    }
1337
    elseif (isset($form_state['formatter_settings'][$field_name])) {
1338
      $settings = $form_state['formatter_settings'][$field_name];
1339
    }
1340
    elseif (isset($instance['display'][$view_mode]['settings'])) {
1341
      $settings = $instance['display'][$view_mode]['settings'];
1342
    }
1343

    
1344
    // Only save settings actually used by the selected formatter.
1345
    $default_settings = field_info_formatter_settings($values['type']);
1346
    $settings = array_intersect_key($settings, $default_settings);
1347

    
1348
    $instance['display'][$view_mode] = array(
1349
      'label' => $values['label'],
1350
      'type' => $values['type'],
1351
      'weight' => $values['weight'],
1352
      'settings' => $settings,
1353
    );
1354
    field_update_instance($instance);
1355
  }
1356

    
1357
  // Get current bundle settings.
1358
  $bundle_settings = field_bundle_settings($entity_type, $bundle);
1359

    
1360
  // Save data for 'extra' fields.
1361
  foreach ($form['#extra'] as $name) {
1362
    $bundle_settings['extra_fields']['display'][$name][$view_mode] = array(
1363
      'weight' => $form_values['fields'][$name]['weight'],
1364
      'visible' => $form_values['fields'][$name]['type'] == 'visible',
1365
    );
1366
  }
1367

    
1368
  // Save view modes data.
1369
  if ($view_mode == 'default') {
1370
    $entity_info = entity_get_info($entity_type);
1371
    foreach ($form_values['view_modes_custom'] as $view_mode_name => $value) {
1372
      // Display a message for each view mode newly configured to use custom
1373
      // settings.
1374
      $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
1375
      if (!empty($value) && empty($view_mode_settings[$view_mode_name]['custom_settings'])) {
1376
        $view_mode_label = $entity_info['view modes'][$view_mode_name]['label'];
1377
        $path = _field_ui_bundle_admin_path($entity_type, $bundle) . "/display/$view_mode_name";
1378
        drupal_set_message(t('The %view_mode mode now uses custom display settings. You might want to <a href="@url">configure them</a>.', array('%view_mode' => $view_mode_label, '@url' => url($path))));
1379
        // Initialize the newly customized view mode with the display settings
1380
        // from the default view mode.
1381
        _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_mode_name, $bundle_settings);
1382
      }
1383
      $bundle_settings['view_modes'][$view_mode_name]['custom_settings'] = !empty($value);
1384
    }
1385
  }
1386

    
1387
  // Save updated bundle settings.
1388
  field_bundle_settings($entity_type, $bundle, $bundle_settings);
1389

    
1390
  drupal_set_message(t('Your settings have been saved.'));
1391
}
1392

    
1393
/**
1394
 * Populates display settings for a new view mode from the default view mode.
1395
 *
1396
 * When an administrator decides to use custom display settings for a view mode,
1397
 * that view mode needs to be initialized with the display settings for the
1398
 * 'default' view mode, which it was previously using. This helper function
1399
 * adds the new custom display settings to this bundle's instances, and saves
1400
 * them. It also modifies the passed-in $settings array, which the caller can
1401
 * then save using field_bundle_settings().
1402
 *
1403
 * @param $entity_type
1404
 *   The bundle's entity type.
1405
 * @param $bundle
1406
 *   The bundle whose view mode is being customized.
1407
 * @param $view_mode
1408
 *   The view mode that the administrator has set to use custom settings.
1409
 * @param $settings
1410
 *   An associative array of bundle settings, as expected by
1411
 *   field_bundle_settings().
1412
 *
1413
 * @see field_ui_display_overview_form_submit().
1414
 * @see field_bundle_settings()
1415
 */
1416
function _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_mode, &$settings) {
1417
  // Update display settings for field instances.
1418
  $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle));
1419
  foreach ($instances as $instance) {
1420
    // If this field instance has display settings defined for this view mode,
1421
    // respect those settings.
1422
    if (!isset($instance['display'][$view_mode])) {
1423
      // The instance doesn't specify anything for this view mode, so use the
1424
      // default display settings.
1425
      $instance['display'][$view_mode] = $instance['display']['default'];
1426
      field_update_instance($instance);
1427
    }
1428
  }
1429

    
1430
  // Update display settings for 'extra fields'.
1431
  foreach (array_keys($settings['extra_fields']['display']) as $name) {
1432
    if (!isset($settings['extra_fields']['display'][$name][$view_mode])) {
1433
      $settings['extra_fields']['display'][$name][$view_mode] = $settings['extra_fields']['display'][$name]['default'];
1434
    }
1435
  }
1436
}
1437

    
1438
/**
1439
 * Returns an array of field_type options.
1440
 */
1441
function field_ui_field_type_options() {
1442
  $options = &drupal_static(__FUNCTION__);
1443

    
1444
  if (!isset($options)) {
1445
    $options = array();
1446
    $field_types = field_info_field_types();
1447
    $field_type_options = array();
1448
    foreach ($field_types as $name => $field_type) {
1449
      // Skip field types which have no widget types, or should not be add via
1450
      // uesr interface.
1451
      if (field_ui_widget_type_options($name) && empty($field_type['no_ui'])) {
1452
        $options[$name] = $field_type['label'];
1453
      }
1454
    }
1455
    asort($options);
1456
  }
1457
  return $options;
1458
}
1459

    
1460
/**
1461
 * Returns an array of widget type options for a field type.
1462
 *
1463
 * If no field type is provided, returns a nested array of all widget types,
1464
 * keyed by field type human name.
1465
 */
1466
function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) {
1467
  $options = &drupal_static(__FUNCTION__);
1468

    
1469
  if (!isset($options)) {
1470
    $options = array();
1471
    $field_types = field_info_field_types();
1472
    foreach (field_info_widget_types() as $name => $widget_type) {
1473
      foreach ($widget_type['field types'] as $widget_field_type) {
1474
        // Check that the field type exists.
1475
        if (isset($field_types[$widget_field_type])) {
1476
          $options[$widget_field_type][$name] = $widget_type['label'];
1477
        }
1478
      }
1479
    }
1480
  }
1481

    
1482
  if (isset($field_type)) {
1483
    return !empty($options[$field_type]) ? $options[$field_type] : array();
1484
  }
1485
  if ($by_label) {
1486
    $field_types = field_info_field_types();
1487
    $options_by_label = array();
1488
    foreach ($options as $field_type => $widgets) {
1489
      $options_by_label[$field_types[$field_type]['label']] = $widgets;
1490
    }
1491
    return $options_by_label;
1492
  }
1493
  return $options;
1494
}
1495

    
1496
/**
1497
 * Returns an array of formatter options for a field type.
1498
 *
1499
 * If no field type is provided, returns a nested array of all formatters, keyed
1500
 * by field type.
1501
 */
1502
function field_ui_formatter_options($field_type = NULL) {
1503
  $options = &drupal_static(__FUNCTION__);
1504

    
1505
  if (!isset($options)) {
1506
    $field_types = field_info_field_types();
1507
    $options = array();
1508
    foreach (field_info_formatter_types() as $name => $formatter) {
1509
      foreach ($formatter['field types'] as $formatter_field_type) {
1510
        // Check that the field type exists.
1511
        if (isset($field_types[$formatter_field_type])) {
1512
          $options[$formatter_field_type][$name] = $formatter['label'];
1513
        }
1514
      }
1515
    }
1516
  }
1517

    
1518
  if ($field_type) {
1519
    return !empty($options[$field_type]) ? $options[$field_type] : array();
1520
  }
1521
  return $options;
1522
}
1523

    
1524
/**
1525
 * Returns an array of existing fields to be added to a bundle.
1526
 */
1527
function field_ui_existing_field_options($entity_type, $bundle) {
1528
  $info = array();
1529
  $field_types = field_info_field_types();
1530

    
1531
  foreach (field_info_instances() as $existing_entity_type => $bundles) {
1532
    foreach ($bundles as $existing_bundle => $instances) {
1533
      // No need to look in the current bundle.
1534
      if (!($existing_bundle == $bundle && $existing_entity_type == $entity_type)) {
1535
        foreach ($instances as $instance) {
1536
          $field = field_info_field($instance['field_name']);
1537
          // Don't show
1538
          // - locked fields,
1539
          // - fields already in the current bundle,
1540
          // - fields that cannot be added to the entity type,
1541
          // - fields that should not be added via user interface.
1542

    
1543
          if (empty($field['locked'])
1544
            && !field_info_instance($entity_type, $field['field_name'], $bundle)
1545
            && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types']))
1546
            && empty($field_types[$field['type']]['no_ui'])) {
1547
            $info[$instance['field_name']] = array(
1548
              'type' => $field['type'],
1549
              'type_label' => $field_types[$field['type']]['label'],
1550
              'field' => $field['field_name'],
1551
              'label' => $instance['label'],
1552
              'widget_type' => $instance['widget']['type'],
1553
            );
1554
          }
1555
        }
1556
      }
1557
    }
1558
  }
1559
  return $info;
1560
}
1561

    
1562
/**
1563
 * Form constructor for the field settings edit page.
1564
 *
1565
 * @see field_ui_field_settings_form_submit()
1566
 * @ingroup forms
1567
 */
1568
function field_ui_field_settings_form($form, &$form_state, $instance) {
1569
  $bundle = $instance['bundle'];
1570
  $entity_type = $instance['entity_type'];
1571
  $field = field_info_field($instance['field_name']);
1572

    
1573
  drupal_set_title($instance['label']);
1574

    
1575
  $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>';
1576

    
1577
  // Create a form structure for the field values.
1578
  $form['field'] = array(
1579
    '#type' => 'fieldset',
1580
    '#title' => t('Field settings'),
1581
    '#description' => $description,
1582
    '#tree' => TRUE,
1583
  );
1584

    
1585
  // See if data already exists for this field.
1586
  // If so, prevent changes to the field settings.
1587
  $has_data = field_has_data($field);
1588
  if ($has_data) {
1589
    $form['field']['#description'] = '<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']['#description'];
1590
  }
1591

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

    
1598
  // Add settings provided by the field module. The field module is
1599
  // responsible for not returning settings that cannot be changed if
1600
  // the field already has data.
1601
  $form['field']['settings'] = array();
1602
  $additions = module_invoke($field['module'], 'field_settings_form', $field, $instance, $has_data);
1603
  if (is_array($additions)) {
1604
    $form['field']['settings'] = $additions;
1605
  }
1606
  if (empty($form['field']['settings'])) {
1607
    $form['field']['settings'] = array(
1608
      '#markup' => t('%field has no field settings.', array('%field' => $instance['label'])),
1609
    );
1610
  }
1611
  $form['#entity_type'] = $entity_type;
1612
  $form['#bundle'] = $bundle;
1613

    
1614
  $form['actions'] = array('#type' => 'actions');
1615
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save field settings'));
1616
  return $form;
1617
}
1618

    
1619
/**
1620
 * Form submission handler for field_ui_field_settings_form().
1621
 */
1622
function field_ui_field_settings_form_submit($form, &$form_state) {
1623
  $form_values = $form_state['values'];
1624
  $field_values = $form_values['field'];
1625

    
1626
  // Merge incoming form values into the existing field.
1627
  $field = field_info_field($field_values['field_name']);
1628

    
1629
  $entity_type = $form['#entity_type'];
1630
  $bundle = $form['#bundle'];
1631
  $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
1632

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

    
1636
  try {
1637
    field_update_field($field);
1638
    drupal_set_message(t('Updated field %label field settings.', array('%label' => $instance['label'])));
1639
    $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle);
1640
  }
1641
  catch (Exception $e) {
1642
    drupal_set_message(t('Attempt to update field %label failed: %message.', array('%label' => $instance['label'], '%message' => $e->getMessage())), 'error');
1643
  }
1644
}
1645

    
1646
/**
1647
 * Form constructor for the widget selection form.
1648
 *
1649
 * Path: BUNDLE_ADMIN_PATH/fields/%field/widget-type, where BUNDLE_ADMIN_PATH is
1650
 * the path stored in the ['admin']['info'] property in the return value of
1651
 * hook_entity_info().
1652
 *
1653
 * @see field_ui_menu()
1654
 * @see field_ui_widget_type_form_submit()
1655
 * @ingroup forms
1656
 */
1657
function field_ui_widget_type_form($form, &$form_state, $instance) {
1658
  drupal_set_title($instance['label']);
1659

    
1660
  $bundle = $instance['bundle'];
1661
  $entity_type = $instance['entity_type'];
1662
  $field_name = $instance['field_name'];
1663

    
1664
  $field = field_info_field($field_name);
1665
  $field_type = field_info_field_types($field['type']);
1666
  $widget_type = field_info_widget_types($instance['widget']['type']);
1667
  $bundles = field_info_bundles();
1668
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
1669

    
1670
  $form = array(
1671
    '#bundle' => $bundle,
1672
    '#entity_type' => $entity_type,
1673
    '#field_name' => $field_name,
1674
  );
1675

    
1676
  $form['basic'] = array(
1677
    '#type' => 'fieldset',
1678
    '#title' => t('Change widget'),
1679
  );
1680
  $form['basic']['widget_type'] = array(
1681
    '#type' => 'select',
1682
    '#title' => t('Widget type'),
1683
    '#required' => TRUE,
1684
    '#options' => field_ui_widget_type_options($field['type']),
1685
    '#default_value' => $instance['widget']['type'],
1686
    '#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)),
1687
  );
1688

    
1689
  $form['actions'] = array('#type' => 'actions');
1690
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Continue'));
1691

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

    
1695
  return $form;
1696
}
1697

    
1698
/**
1699
 * Form submission handler for field_ui_widget_type_form().
1700
 */
1701
function field_ui_widget_type_form_submit($form, &$form_state) {
1702
  $form_values = $form_state['values'];
1703
  $bundle = $form['#bundle'];
1704
  $entity_type = $form['#entity_type'];
1705
  $field_name = $form['#field_name'];
1706

    
1707
  // Retrieve the stored instance settings to merge with the incoming values.
1708
  $instance = field_read_instance($entity_type, $field_name, $bundle);
1709

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

    
1714
  $instance['widget']['type'] = $form_values['widget_type'];
1715
  $instance['widget']['module'] = $widget_module;
1716

    
1717
  try {
1718
    field_update_instance($instance);
1719
    drupal_set_message(t('Changed the widget for field %label.', array('%label' => $instance['label'])));
1720
  }
1721
  catch (Exception $e) {
1722
    drupal_set_message(t('There was a problem changing the widget for field %label.', array('%label' => $instance['label'])), 'error');
1723
  }
1724

    
1725
  $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle);
1726
}
1727

    
1728
/**
1729
 * Form constructor for removing a field instance from a bundle.
1730
 *
1731
 * @see field_ui_field_delete_form_submit()
1732
 * @ingroup forms
1733
 */
1734
function field_ui_field_delete_form($form, &$form_state, $instance) {
1735
  $bundle = $instance['bundle'];
1736
  $entity_type = $instance['entity_type'];
1737
  $field = field_info_field($instance['field_name']);
1738

    
1739
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
1740

    
1741
  $form['entity_type'] = array('#type' => 'value', '#value' => $entity_type);
1742
  $form['bundle'] = array('#type' => 'value', '#value' => $bundle);
1743
  $form['field_name'] = array('#type' => 'value', '#value' => $field['field_name']);
1744

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

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

    
1758
  return $output;
1759
}
1760

    
1761
/**
1762
 * Form submission handler for field_ui_field_delete_form().
1763
 *
1764
 * Removes a field instance from a bundle. If the field has no more instances,
1765
 * it will be marked as deleted too.
1766
 */
1767
function field_ui_field_delete_form_submit($form, &$form_state) {
1768
  $form_values = $form_state['values'];
1769
  $field_name = $form_values['field_name'];
1770
  $bundle = $form_values['bundle'];
1771
  $entity_type = $form_values['entity_type'];
1772

    
1773
  $field = field_info_field($field_name);
1774
  $instance = field_info_instance($entity_type, $field_name, $bundle);
1775
  $bundles = field_info_bundles();
1776
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
1777

    
1778
  if (!empty($bundle) && $field && !$field['locked'] && $form_values['confirm']) {
1779
    field_delete_instance($instance);
1780
    drupal_set_message(t('The field %field has been deleted from the %type content type.', array('%field' => $instance['label'], '%type' => $bundle_label)));
1781
  }
1782
  else {
1783
    drupal_set_message(t('There was a problem removing the %field from the %type content type.', array('%field' => $instance['label'], '%type' => $bundle_label)), 'error');
1784
  }
1785

    
1786
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
1787
  $form_state['redirect'] = field_ui_get_destinations(array($admin_path . '/fields'));
1788

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

    
1798
/**
1799
 * Form constructor for the field instance settings form.
1800
 *
1801
 * @see field_ui_field_edit_form_validate()
1802
 * @see field_ui_field_edit_form_submit()
1803
 * @ingroup forms
1804
 */
1805
function field_ui_field_edit_form($form, &$form_state, $instance) {
1806
  $bundle = $instance['bundle'];
1807
  $entity_type = $instance['entity_type'];
1808
  $field = field_info_field($instance['field_name']);
1809

    
1810
  drupal_set_title($instance['label']);
1811

    
1812
  $form['#field'] = $field;
1813
  $form['#instance'] = $instance;
1814

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

    
1822
  $field_type = field_info_field_types($field['type']);
1823
  $widget_type = field_info_widget_types($instance['widget']['type']);
1824
  $bundles = field_info_bundles();
1825

    
1826
  // Create a form structure for the instance values.
1827
  $form['instance'] = array(
1828
    '#tree' => TRUE,
1829
    '#type' => 'fieldset',
1830
    '#title' => t('%type settings', array('%type' => $bundles[$entity_type][$bundle]['label'])),
1831
    '#description' => t('These settings apply only to the %field field when used in the %type type.', array(
1832
      '%field' => $instance['label'],
1833
      '%type' => $bundles[$entity_type][$bundle]['label'],
1834
    )),
1835
    // Ensure field_ui_field_edit_instance_pre_render() gets called in addition
1836
    // to, not instead of, the #pre_render function(s) needed by all fieldsets.
1837
    '#pre_render' => array_merge(array('field_ui_field_edit_instance_pre_render'), element_info_property('fieldset', '#pre_render', array())),
1838
  );
1839

    
1840
  // Build the non-configurable instance values.
1841
  $form['instance']['field_name'] = array(
1842
    '#type' => 'value',
1843
    '#value' => $instance['field_name'],
1844
  );
1845
  $form['instance']['entity_type'] = array(
1846
    '#type' => 'value',
1847
    '#value' => $entity_type,
1848
  );
1849
  $form['instance']['bundle'] = array(
1850
    '#type' => 'value',
1851
    '#value' => $bundle,
1852
  );
1853
  $form['instance']['widget']['weight'] = array(
1854
    '#type' => 'value',
1855
    '#value' => !empty($instance['widget']['weight']) ? $instance['widget']['weight'] : 0,
1856
  );
1857

    
1858
  // Build the configurable instance values.
1859
  $form['instance']['label'] = array(
1860
    '#type' => 'textfield',
1861
    '#title' => t('Label'),
1862
    '#default_value' => !empty($instance['label']) ? $instance['label'] : $field['field_name'],
1863
    '#required' => TRUE,
1864
    '#weight' => -20,
1865
  );
1866
  $form['instance']['required'] = array(
1867
    '#type' => 'checkbox',
1868
    '#title' => t('Required field'),
1869
    '#default_value' => !empty($instance['required']),
1870
    '#weight' => -10,
1871
  );
1872

    
1873
  $form['instance']['description'] = array(
1874
    '#type' => 'textarea',
1875
    '#title' => t('Help text'),
1876
    '#default_value' => !empty($instance['description']) ? $instance['description'] : '',
1877
    '#rows' => 5,
1878
    '#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())),
1879
    '#weight' => -5,
1880
  );
1881

    
1882
  // Build the widget component of the instance.
1883
  $form['instance']['widget']['type'] = array(
1884
    '#type' => 'value',
1885
    '#value' => $instance['widget']['type'],
1886
  );
1887
  $form['instance']['widget']['module'] = array(
1888
    '#type' => 'value',
1889
    '#value' => $widget_type['module'],
1890
  );
1891
  $form['instance']['widget']['active'] = array(
1892
    '#type' => 'value',
1893
    '#value' => !empty($field['instance']['widget']['active']) ? 1 : 0,
1894
  );
1895

    
1896
  // Add additional field instance settings from the field module.
1897
  $additions = module_invoke($field['module'], 'field_instance_settings_form', $field, $instance);
1898
  if (is_array($additions)) {
1899
    $form['instance']['settings'] = $additions;
1900
  }
1901

    
1902
  // Add additional widget settings from the widget module.
1903
  $additions = module_invoke($widget_type['module'], 'field_widget_settings_form', $field, $instance);
1904
  if (is_array($additions)) {
1905
    $form['instance']['widget']['settings'] = $additions;
1906
    $form['instance']['widget']['active']['#value'] = 1;
1907
  }
1908

    
1909
  // Add handling for default value if not provided by any other module.
1910
  if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && empty($instance['default_value_function'])) {
1911
    $form['instance']['default_value_widget'] = field_ui_default_value_widget($field, $instance, $form, $form_state);
1912
  }
1913

    
1914
  $has_data = field_has_data($field);
1915
  if ($has_data) {
1916
    $description = '<p>' . t('These settings apply to the %field field everywhere it is used. Because the field already has data, some settings can no longer be changed.', array('%field' => $instance['label'])) . '</p>';
1917
  }
1918
  else {
1919
    $description = '<p>' . t('These settings apply to the %field field everywhere it is used.', array('%field' => $instance['label'])) . '</p>';
1920
  }
1921

    
1922
  // Create a form structure for the field values.
1923
  $form['field'] = array(
1924
    '#type' => 'fieldset',
1925
    '#title' => t('%field field settings', array('%field' => $instance['label'])),
1926
    '#description' => $description,
1927
    '#tree' => TRUE,
1928
  );
1929

    
1930
  // Build the configurable field values.
1931
  $description = t('Maximum number of values users can enter for this field.');
1932
  if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
1933
    $description .= '<br/>' . t("'Unlimited' will provide an 'Add more' button so the users can add as many values as they like.");
1934
  }
1935
  $form['field']['cardinality'] = array(
1936
    '#type' => 'select',
1937
    '#title' => t('Number of values'),
1938
    '#options' => array(FIELD_CARDINALITY_UNLIMITED => t('Unlimited')) + drupal_map_assoc(range(1, 10)),
1939
    '#default_value' => $field['cardinality'],
1940
    '#description' => $description,
1941
  );
1942

    
1943
  // Add additional field type settings. The field type module is
1944
  // responsible for not returning settings that cannot be changed if
1945
  // the field already has data.
1946
  $additions = module_invoke($field['module'], 'field_settings_form', $field, $instance, $has_data);
1947
  if (is_array($additions)) {
1948
    $form['field']['settings'] = $additions;
1949
  }
1950

    
1951
  $form['actions'] = array('#type' => 'actions');
1952
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
1953
  return $form;
1954
}
1955

    
1956
/**
1957
 * Pre-render function for field instance settings.
1958
 *
1959
 * Combines the instance, widget, and other settings into a single fieldset so
1960
 * that elements within each group can be shown at different weights as if they
1961
 * all had the same parent.
1962
 */
1963
function field_ui_field_edit_instance_pre_render($element) {
1964
  // Merge the widget settings into the main form.
1965
  if (isset($element['widget']['settings'])) {
1966
    foreach (element_children($element['widget']['settings']) as $key) {
1967
      $element['widget_' . $key] = $element['widget']['settings'][$key];
1968
    }
1969
    unset($element['widget']['settings']);
1970
  }
1971

    
1972
  // Merge the instance settings into the main form.
1973
  if (isset($element['settings'])) {
1974
    foreach (element_children($element['settings']) as $key) {
1975
      $element['instance_' . $key] = $element['settings'][$key];
1976
    }
1977
    unset($element['settings']);
1978
  }
1979

    
1980
  return $element;
1981
}
1982

    
1983
/**
1984
 * Builds the default value fieldset for a given field instance.
1985
 */
1986
function field_ui_default_value_widget($field, $instance, &$form, &$form_state) {
1987
  $field_name = $field['field_name'];
1988

    
1989
  $element = array(
1990
    '#type' => 'fieldset',
1991
    '#title' => t('Default value'),
1992
    '#collapsible' => FALSE,
1993
    '#tree' => TRUE,
1994
    '#description' => t('The default value for this field, used when creating new content.'),
1995
    // Stick to an empty 'parents' on this form in order not to breaks widgets
1996
    // that do not use field_widget_[field|instance]() and still access
1997
    // $form_state['field'] directly.
1998
    '#parents' => array(),
1999
  );
2000

    
2001
  // Insert the widget.
2002
  $items = $instance['default_value'];
2003
  $instance['required'] = FALSE;
2004
  $instance['description'] = '';
2005

    
2006
  // @todo Allow multiple values (requires more work on 'add more' JS handler).
2007
  $element += field_default_form($instance['entity_type'], NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state, 0);
2008

    
2009
  return $element;
2010
}
2011

    
2012
/**
2013
 * Form validation handler for field_ui_field_edit_form().
2014
 *
2015
 * @see field_ui_field_edit_form_submit().
2016
 */
2017
function field_ui_field_edit_form_validate($form, &$form_state) {
2018
  // Take the incoming values as the $instance definition, so that the 'default
2019
  // value' gets validated using the instance settings being submitted.
2020
  $instance = $form_state['values']['instance'];
2021
  $field_name = $instance['field_name'];
2022

    
2023
  if (isset($form['instance']['default_value_widget'])) {
2024
    $element = $form['instance']['default_value_widget'];
2025

    
2026
    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NONE, $form_state);
2027
    $field = $field_state['field'];
2028

    
2029
    // Extract the 'default value'.
2030
    $items = array();
2031
    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2032

    
2033
    // Validate the value and report errors.
2034
    $errors = array();
2035
    $function = $field['module'] . '_field_validate';
2036
    if (function_exists($function)) {
2037
      $function(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $errors);
2038
    }
2039
    if (isset($errors[$field_name][LANGUAGE_NONE])) {
2040
      // Store reported errors in $form_state.
2041
      $field_state['errors'] = $errors[$field_name][LANGUAGE_NONE];
2042
      field_form_set_state($element['#parents'], $field_name, LANGUAGE_NONE, $form_state, $field_state);
2043
      // Assign reported errors to the correct form element.
2044
      field_default_form_errors(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2045
    }
2046
  }
2047
}
2048

    
2049
/**
2050
 * Form submission handler for field_ui_field_edit_form().
2051
 *
2052
 * @see field_ui_field_edit_form_validate().
2053
 */
2054
function field_ui_field_edit_form_submit($form, &$form_state) {
2055
  $instance = $form_state['values']['instance'];
2056
  $field = $form_state['values']['field'];
2057

    
2058
  // Update any field settings that have changed.
2059
  $field_source = field_info_field($instance['field_name']);
2060
  $field = array_merge($field_source, $field);
2061
  try {
2062
    field_update_field($field);
2063
  }
2064
  catch (Exception $e) {
2065
    drupal_set_message(t('Attempt to update field %label failed: %message.', array('%label' => $instance['label'], '%message' => $e->getMessage())), 'error');
2066
    return;
2067
  }
2068

    
2069
  // Handle the default value.
2070
  if (isset($form['instance']['default_value_widget'])) {
2071
    $element = $form['instance']['default_value_widget'];
2072

    
2073
    // Extract field values.
2074
    $items = array();
2075
    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2076
    field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2077

    
2078
    $instance['default_value'] = $items ? $items : NULL;
2079
  }
2080

    
2081
  // Retrieve the stored instance settings to merge with the incoming values.
2082
  $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
2083
  $instance = array_merge($instance_source, $instance);
2084
  field_update_instance($instance);
2085

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

    
2088
  $form_state['redirect'] = field_ui_next_destination($instance['entity_type'], $instance['bundle']);
2089
}
2090

    
2091
/**
2092
 * Extracts next redirect path from an array of multiple destinations.
2093
 *
2094
 * @see field_ui_next_destination()
2095
 */
2096
function field_ui_get_destinations($destinations) {
2097
  $path = array_shift($destinations);
2098
  $options = drupal_parse_url($path);
2099
  if ($destinations) {
2100
    $options['query']['destinations'] = $destinations;
2101
  }
2102
  return array($options['path'], $options);
2103
}
2104

    
2105
/**
2106
 * Returns the next redirect path in a multipage sequence.
2107
 */
2108
function field_ui_next_destination($entity_type, $bundle) {
2109
  $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
2110
  if (!empty($destinations)) {
2111
    unset($_REQUEST['destinations']);
2112
  }
2113
  // Remove any external URLs.
2114
  $destinations = array_diff($destinations, array_filter($destinations, 'url_is_external'));
2115
  if ($destinations) {
2116
    return field_ui_get_destinations($destinations);
2117
  }
2118
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
2119
  return $admin_path . '/fields';
2120
}