Projet

Général

Profil

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

root / drupal7 / modules / field_ui / field_ui.admin.inc @ db2d93dd

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
    $instance['display'][$view_mode]['module'] = $formatter['module'];
1030
    $instance['display'][$view_mode]['settings'] = $settings;
1031

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

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

    
1048
      $settings_form = array();
1049
      $function = $formatter['module'] . '_field_formatter_settings_form';
1050
      if (function_exists($function)) {
1051
        $settings_form = $function($field, $instance, $view_mode, $form, $form_state);
1052
      }
1053

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

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

    
1166
  $form['fields'] = $table;
1167

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

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

    
1219
  $form['actions'] = array('#type' => 'actions');
1220
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
1221

    
1222
  $form['#attached']['js'][] = drupal_get_path('module', 'field_ui') . '/field_ui.js';
1223
  $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css';
1224

    
1225
  // Add tabledrag behavior.
1226
  $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'order', 'sibling', 'field-weight');
1227
  $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name');
1228

    
1229
  return $form;
1230
}
1231

    
1232

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

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

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

    
1255
    case 'cancel':
1256
      // Set the field back to 'non edit' mode.
1257
      unset($form_state['formatter_settings_edit']);
1258
      break;
1259

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

    
1270
  $form_state['rebuild'] = TRUE;
1271
}
1272

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

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

    
1287
    case 'update':
1288
    case 'cancel':
1289
      $updated_rows = array($trigger['#field_name']);
1290
      $updated_columns = array('format', 'settings_summary', 'settings_edit');
1291
      break;
1292

    
1293
    case 'refresh_table':
1294
      $updated_rows = array_values(explode(' ', $form_state['values']['refresh_rows']));
1295
      $updated_columns = array('settings_summary', 'settings_edit');
1296
      break;
1297
  }
1298

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

    
1307
  // Return the whole table.
1308
  return $form['fields'];
1309
}
1310

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

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

    
1340
    // Only save settings actually used by the selected formatter.
1341
    $default_settings = field_info_formatter_settings($values['type']);
1342
    $settings = array_intersect_key($settings, $default_settings);
1343

    
1344
    $instance['display'][$view_mode] = array(
1345
      'label' => $values['label'],
1346
      'type' => $values['type'],
1347
      'weight' => $values['weight'],
1348
      'settings' => $settings,
1349
    );
1350
    field_update_instance($instance);
1351
  }
1352

    
1353
  // Get current bundle settings.
1354
  $bundle_settings = field_bundle_settings($entity_type, $bundle);
1355

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

    
1364
  // Save view modes data.
1365
  if ($view_mode == 'default') {
1366
    $entity_info = entity_get_info($entity_type);
1367
    foreach ($form_values['view_modes_custom'] as $view_mode_name => $value) {
1368
      // Display a message for each view mode newly configured to use custom
1369
      // settings.
1370
      $view_mode_settings = field_view_mode_settings($entity_type, $bundle);
1371
      if (!empty($value) && empty($view_mode_settings[$view_mode_name]['custom_settings'])) {
1372
        $view_mode_label = $entity_info['view modes'][$view_mode_name]['label'];
1373
        $path = _field_ui_bundle_admin_path($entity_type, $bundle) . "/display/$view_mode_name";
1374
        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))));
1375
        // Initialize the newly customized view mode with the display settings
1376
        // from the default view mode.
1377
        _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_mode_name, $bundle_settings);
1378
      }
1379
      $bundle_settings['view_modes'][$view_mode_name]['custom_settings'] = !empty($value);
1380
    }
1381
  }
1382

    
1383
  // Save updated bundle settings.
1384
  field_bundle_settings($entity_type, $bundle, $bundle_settings);
1385

    
1386
  drupal_set_message(t('Your settings have been saved.'));
1387
}
1388

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

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

    
1434
/**
1435
 * Returns an array of field_type options.
1436
 */
1437
function field_ui_field_type_options() {
1438
  $options = &drupal_static(__FUNCTION__);
1439

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

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

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

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

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

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

    
1514
  if ($field_type) {
1515
    return !empty($options[$field_type]) ? $options[$field_type] : array();
1516
  }
1517
  return $options;
1518
}
1519

    
1520
/**
1521
 * Returns an array of existing fields to be added to a bundle.
1522
 */
1523
function field_ui_existing_field_options($entity_type, $bundle) {
1524
  $info = array();
1525
  $field_types = field_info_field_types();
1526

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

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

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

    
1569
  drupal_set_title($instance['label']);
1570

    
1571
  $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>';
1572

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

    
1581
  // See if data already exists for this field.
1582
  // If so, prevent changes to the field settings.
1583
  $has_data = field_has_data($field);
1584
  if ($has_data) {
1585
    $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'];
1586
  }
1587

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

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

    
1610
  $form['actions'] = array('#type' => 'actions');
1611
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save field settings'));
1612
  return $form;
1613
}
1614

    
1615
/**
1616
 * Form submission handler for field_ui_field_settings_form().
1617
 */
1618
function field_ui_field_settings_form_submit($form, &$form_state) {
1619
  $form_values = $form_state['values'];
1620
  $field_values = $form_values['field'];
1621

    
1622
  // Merge incoming form values into the existing field.
1623
  $field = field_info_field($field_values['field_name']);
1624

    
1625
  $entity_type = $form['#entity_type'];
1626
  $bundle = $form['#bundle'];
1627
  $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
1628

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

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

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

    
1656
  $bundle = $instance['bundle'];
1657
  $entity_type = $instance['entity_type'];
1658
  $field_name = $instance['field_name'];
1659

    
1660
  $field = field_info_field($field_name);
1661
  $field_type = field_info_field_types($field['type']);
1662
  $widget_type = field_info_widget_types($instance['widget']['type']);
1663
  $bundles = field_info_bundles();
1664
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
1665

    
1666
  $form = array(
1667
    '#bundle' => $bundle,
1668
    '#entity_type' => $entity_type,
1669
    '#field_name' => $field_name,
1670
  );
1671

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

    
1685
  $form['actions'] = array('#type' => 'actions');
1686
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Continue'));
1687

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

    
1691
  return $form;
1692
}
1693

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

    
1703
  // Retrieve the stored instance settings to merge with the incoming values.
1704
  $instance = field_read_instance($entity_type, $field_name, $bundle);
1705

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

    
1710
  $instance['widget']['type'] = $form_values['widget_type'];
1711
  $instance['widget']['module'] = $widget_module;
1712

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

    
1721
  $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle);
1722
}
1723

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

    
1735
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
1736

    
1737
  $form['entity_type'] = array('#type' => 'value', '#value' => $entity_type);
1738
  $form['bundle'] = array('#type' => 'value', '#value' => $bundle);
1739
  $form['field_name'] = array('#type' => 'value', '#value' => $field['field_name']);
1740

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

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

    
1754
  return $output;
1755
}
1756

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

    
1769
  $field = field_info_field($field_name);
1770
  $instance = field_info_instance($entity_type, $field_name, $bundle);
1771
  $bundles = field_info_bundles();
1772
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
1773

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

    
1782
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
1783
  $form_state['redirect'] = field_ui_get_destinations(array($admin_path . '/fields'));
1784

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

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

    
1806
  drupal_set_title($instance['label']);
1807

    
1808
  $form['#field'] = $field;
1809
  $form['#instance'] = $instance;
1810

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

    
1818
  $field_type = field_info_field_types($field['type']);
1819
  $widget_type = field_info_widget_types($instance['widget']['type']);
1820
  $bundles = field_info_bundles();
1821

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

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

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

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

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

    
1892
  // Add additional field instance settings from the field module.
1893
  $additions = module_invoke($field['module'], 'field_instance_settings_form', $field, $instance);
1894
  if (is_array($additions)) {
1895
    $form['instance']['settings'] = $additions;
1896
  }
1897

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

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

    
1910
  $has_data = field_has_data($field);
1911
  if ($has_data) {
1912
    $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>';
1913
  }
1914
  else {
1915
    $description = '<p>' . t('These settings apply to the %field field everywhere it is used.', array('%field' => $instance['label'])) . '</p>';
1916
  }
1917

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

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

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

    
1947
  $form['actions'] = array('#type' => 'actions');
1948
  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
1949
  return $form;
1950
}
1951

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

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

    
1976
  return $element;
1977
}
1978

    
1979
/**
1980
 * Builds the default value fieldset for a given field instance.
1981
 */
1982
function field_ui_default_value_widget($field, $instance, &$form, &$form_state) {
1983
  $field_name = $field['field_name'];
1984

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

    
1997
  // Insert the widget.
1998
  $items = $instance['default_value'];
1999
  $instance['required'] = FALSE;
2000
  $instance['description'] = '';
2001

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

    
2005
  return $element;
2006
}
2007

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

    
2019
  if (isset($form['instance']['default_value_widget'])) {
2020
    $element = $form['instance']['default_value_widget'];
2021

    
2022
    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NONE, $form_state);
2023
    $field = $field_state['field'];
2024

    
2025
    // Extract the 'default value'.
2026
    $items = array();
2027
    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2028

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

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

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

    
2065
  // Handle the default value.
2066
  if (isset($form['instance']['default_value_widget'])) {
2067
    $element = $form['instance']['default_value_widget'];
2068

    
2069
    // Extract field values.
2070
    $items = array();
2071
    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2072
    field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
2073

    
2074
    $instance['default_value'] = $items ? $items : NULL;
2075
  }
2076

    
2077
  // Retrieve the stored instance settings to merge with the incoming values.
2078
  $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
2079
  $instance = array_merge($instance_source, $instance);
2080
  field_update_instance($instance);
2081

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

    
2084
  $form_state['redirect'] = field_ui_next_destination($instance['entity_type'], $instance['bundle']);
2085
}
2086

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

    
2101
/**
2102
 * Returns the next redirect path in a multipage sequence.
2103
 */
2104
function field_ui_next_destination($entity_type, $bundle) {
2105
  $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
2106
  if (!empty($destinations)) {
2107
    unset($_REQUEST['destinations']);
2108
    return field_ui_get_destinations($destinations);
2109
  }
2110
  $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
2111
  return $admin_path . '/fields';
2112
}