Projet

Général

Profil

Paste
Télécharger (39,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / field_group / field_group.field_ui.inc @ 87dbc3bf

1
<?php
2

    
3
/**
4
 * @file
5
 * Field_group.field_ui.inc is a file that contains most functions
6
 * needed on the Fields UI Manage forms (display and fields).
7
 */
8

    
9
/**
10
 * Helper function to get the form parameters to use while
11
 * building the fields and display overview form.
12
 */
13
function field_group_field_ui_form_params($form, $display_overview) {
14

    
15
  $params = new stdClass();
16
  $params->entity_type = $form['#entity_type'];
17
  $params->bundle = $form['#bundle'];
18
  $params->admin_path = _field_ui_bundle_admin_path($params->entity_type, $params->bundle);
19
  $params->display_overview = $display_overview;
20

    
21
  if ($display_overview) {
22
    $params->region_callback = 'field_group_display_overview_row_region';
23
    $params->mode = $form['#view_mode'];
24
  }
25
  else {
26
    $params->region_callback = 'field_group_field_overview_row_region';
27
    $params->mode = 'form';
28
  }
29

    
30
  $params->groups = field_group_info_groups($params->entity_type, $params->bundle, $params->mode, TRUE);
31

    
32
  // Gather parenting data.
33
  $params->parents = array();
34
  foreach ($params->groups as $name => $group) {
35
    foreach ($group->children as $child) {
36
      $params->parents[$child] = $name;
37
    }
38
  }
39

    
40
  return $params;
41
}
42

    
43
/**
44
 * Function to alter the fields overview and display overview screen.
45
 */
46
function field_group_field_ui_overview_form_alter(&$form, &$form_state, $display_overview = FALSE) {
47

    
48
  // Only start altering the form if we need to.
49
  if ($display_overview && empty($form['#fields']) && empty($form['#extra'])) {
50
    return;
51
  }
52

    
53
  $params = field_group_field_ui_form_params($form, $display_overview);
54

    
55
  // Add some things to be able to preserve synced usage of field_ui.
56
  if (!$display_overview) {
57
    // This key is used to store the current updated field.
58
    $form_state += array(
59
      'formatter_settings_edit' => NULL,
60
    );
61
    // Add AJAX wrapper.
62
    $form['fields']['#prefix'] = '<div id="field-display-overview-wrapper">';
63
    $form['fields']['#suffix'] = '</div>';
64
  }
65
  $form['#groups'] = array_keys($params->groups);
66

    
67
  $table = &$form['fields'];
68

    
69
  // Add a region for 'add_new' rows, but only when fields are
70
  // available and thus regions.
71
  if (isset($table['#regions'])) {
72
    $table['#regions'] += array(
73
      'add_new' => array('title' => '&nbsp;'),
74
    );
75
  }
76

    
77
  // Extend available parenting options.
78
  foreach ($params->groups as $name => $group) {
79
    $table['#parent_options'][$name] = $group->label;
80
  }
81
  $table['#parent_options']['_add_new_group'] = t('Add new group');
82

    
83
  // Update existing rows accordingly to the parents.
84
  foreach (element_children($table) as $name) {
85
    $table[$name]['parent_wrapper']['parent']['#options'] = $table['#parent_options'];
86
    // Inherit the value of the parent when default value is empty.
87
    if (empty($table[$name]['parent_wrapper']['parent']['#default_value'])) {
88
      $table[$name]['parent_wrapper']['parent']['#default_value'] = isset($params->parents[$name]) ? $params->parents[$name] : '';
89
    }
90
  }
91

    
92
  $formatter_options = field_group_field_formatter_options($display_overview ? 'display' : 'form');
93

    
94
  $refresh_rows = isset($form_state['values']['refresh_rows']) ? $form_state['values']['refresh_rows'] : (isset($form_state['input']['refresh_rows']) ? $form_state['input']['refresh_rows'] : NULL);
95

    
96
  // Create the group rows and check actions.
97
  foreach (array_keys($params->groups) as $name) {
98

    
99
    // Play around with form_state so we only need to hold things
100
    // between requests, until the save button was hit.
101
    if (isset($form_state['field_group'][$name])) {
102
      $group = & $form_state['field_group'][$name];
103
    }
104
    else {
105
      $group = & $params->groups[$name];
106
    }
107

    
108
    // Check the currently selected formatter, and merge persisted values for
109
    // formatter settings for the group.
110
    // This needs to be done first, so all fields are updated before creating form elements.
111
    if (isset($refresh_rows) && $refresh_rows == $name) {
112
      $settings = isset($form_state['values']['fields'][$name]) ? $form_state['values']['fields'][$name] : (isset($form_state['input']['fields'][$name]) ? $form_state['input']['fields'][$name] : NULL);
113
      if (array_key_exists('settings_edit', $settings)) {
114
        //$group->format_type = $form_state['field_group'][$name]->format_type;
115
        $group = $form_state['field_group'][$name];
116
      }
117
      field_group_formatter_row_update($group, $settings);
118
    }
119

    
120
    // Save the group when the configuration is submitted.
121
    if (!empty($form_state['values'][$name . '_formatter_settings_update'])) {
122
      field_group_formatter_settings_update($group, $form_state['values']['fields'][$name]);
123
    }
124
    // After all updates are finished, let the form_state know.
125
    $form_state['field_group'][$name] = $group;
126

    
127
    $settings = field_group_format_settings_form($group);
128

    
129
    $id = strtr($name, '_', '-');
130
    $js_rows_data[$id] = array('type' => 'group', 'name' => $name);
131
    // A group cannot be selected as its own parent.
132
    $parent_options = $table['#parent_options'];
133
    unset($parent_options[$name]);
134
    $table[$name] = array(
135
      '#attributes' => array('class' => array('draggable', 'field-group'), 'id' => $id),
136
      '#row_type' => 'group',
137
      '#region_callback' => $params->region_callback,
138
      '#js_settings' => array('rowHandler' => 'group'),
139
      'human_name' => array(
140
        '#markup' => check_plain(t($group->label)),
141
        '#prefix' => '<span class="group-label">',
142
        '#suffix' => '</span>',
143
      ),
144
      'weight' => array(
145
        '#type' => 'textfield',
146
        '#default_value' => $group->weight,
147
        '#size' => 3,
148
        '#attributes' => array('class' => array('field-weight')),
149
      ),
150
      'parent_wrapper' => array(
151
        'parent' => array(
152
          '#type' => 'select',
153
          '#options' =>  $parent_options,
154
          '#empty_value' => '',
155
          '#default_value' => isset($params->parents[$name]) ? $params->parents[$name] : '',
156
          '#attributes' => array('class' => array('field-parent')),
157
          '#parents' => array('fields', $name, 'parent'),
158
        ),
159
        'hidden_name' => array(
160
          '#type' => 'hidden',
161
          '#default_value' => $name,
162
          '#attributes' => array('class' => array('field-name')),
163
        ),
164
      ),
165
    );
166

    
167
    $table[$name] += array(
168
      'group_name' => array(
169
        '#markup' => check_plain($name),
170
      ),
171
      'format' => array(
172
        'type' => array(
173
          '#type' => 'select',
174
          '#options' => $formatter_options,
175
          '#default_value' => $group->format_type,
176
          '#attributes' => array('class' => array('field-group-type')),
177
        ),
178
      ),
179
    );
180

    
181
    $base_button = array(
182
      '#submit' => array('field_ui_display_overview_multistep_submit'),
183
      '#ajax' => array(
184
        'callback' => 'field_ui_display_overview_multistep_js',
185
        'wrapper' => 'field-display-overview-wrapper',
186
        'effect' => 'fade',
187
      ),
188
      '#field_name' => $name,
189
    );
190

    
191
    if ($form_state['formatter_settings_edit'] == $name) {
192
      $table[$name]['format']['#cell_attributes'] = array('colspan' => $display_overview ? 3 : 3);
193
      $table[$name]['format']['format_settings'] = array(
194
        '#type' => 'container',
195
        '#attributes' => array('class' => array('field-formatter-settings-edit-form')),
196
        '#parents' => array('fields', $name, 'format_settings'),
197
        '#weight' => -5,
198
        'label' => array(
199
          '#markup' => t('Field group format:') . ' <span class="formatter-name">' . $group->format_type . '</span>',
200
        ),
201
        // Create a settings form where hooks can pick in.
202
        'settings' => $settings,
203
        'actions' => array(
204
          '#type' => 'actions',
205
          'save_settings' => $base_button + array(
206
            '#type' => 'submit',
207
            '#name' => $name . '_formatter_settings_update',
208
            '#value' => t('Update'),
209
            '#op' => 'update',
210
          ),
211
          'cancel_settings' => $base_button + array(
212
            '#type' => 'submit',
213
            '#name' => $name . '_formatter_settings_cancel',
214
            '#value' => t('Cancel'),
215
            '#op' => 'cancel',
216
            // Do not check errors for the 'Cancel' button.
217
            '#limit_validation_errors' => array(),
218
          ),
219
        ),
220
      );
221
      $table[$name]['#attributes']['class'][] = 'field-formatter-settings-editing';
222
      $table[$name]['format']['type']['#attributes']['class'] = array('element-invisible');
223
    }
224
    else {
225
      // After saving, the settings are updated here aswell. First we create
226
      // the element for the table cell.
227
      $table[$name]['settings_summary'] = array('#markup' => '');
228
      if (!empty($group->format_settings)) {
229
        $table[$name]['settings_summary'] = field_group_format_settings_summary($name, $group);
230
      }
231
      // Add the configure button.
232
      $table[$name]['settings_edit'] = $base_button + array(
233
        '#type' => 'image_button',
234
        '#name' => $name . '_group_settings_edit',
235
        '#src' => 'misc/configure.png',
236
        '#attributes' => array('class' => array('field-formatter-settings-edit'), 'alt' => t('Edit')),
237
        '#op' => 'edit',
238
        // Do not check errors for the 'Edit' button.
239
        '#limit_validation_errors' => array(),
240
        '#prefix' => '<div class="field-formatter-settings-edit-wrapper">',
241
        '#suffix' => '</div>',
242
      );
243
      if ($display_overview) {
244
        $table[$name]['settings_edit']['#suffix'] .= l(t('delete'), $params->admin_path . '/groups/' . $name . '/delete/' . $params->mode);
245
      }
246
    }
247

    
248
    if (!$display_overview) {
249
      $table[$name] += array(
250
        'delete' => array(
251
          '#markup' => l(t('delete'), $params->admin_path . '/groups/' . $name . '/delete/form'),
252
        ),
253
      );
254
    }
255
  }
256

    
257
  // Additional row: add new group.
258
  $parent_options = $table['#parent_options'];
259
  unset($parent_options['_add_new_group']);
260
  $table['_add_new_group'] = field_group_add_row('_add_new_group', $parent_options, $params);
261

    
262
  $table['_add_new_group'] += array(
263
    'format' => array(
264
      'type' => array(
265
        '#type' => 'select',
266
        '#options' => $formatter_options,
267
        '#default_value' => 'fieldset',
268
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
269
      ),
270
      '#cell_attributes' => array('colspan' => 3),
271
    ),
272
  );
273

    
274
  if (!$display_overview) {
275
    // See field_ui.admin.inc for more details on refresh rows.
276
    $form['refresh_rows'] = array('#type' => 'hidden');
277
    $form['refresh'] = array(
278
      '#type' => 'submit',
279
      '#value' => t('Refresh'),
280
      '#op' => 'refresh_table',
281
      '#submit' => array('field_ui_display_overview_multistep_submit'),
282
      '#ajax' => array(
283
        'callback' => 'field_ui_display_overview_multistep_js',
284
        'wrapper' => 'field-display-overview-wrapper',
285
        'effect' => 'fade',
286
        // The button stays hidden, so we hide the AJAX spinner too. Ad-hoc
287
        // spinners will be added manually by the client-side script.
288
        'progress' => 'none',
289
      ),
290
    );
291
  }
292

    
293
  $form['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.field_ui.css';
294
  $form['#attached']['js'][] = drupal_get_path('module', 'field_group') . '/field_group.field_ui.js';
295

    
296
  $form['#validate'][] = 'field_group_field_overview_validate';
297
  $form['#submit'][] = 'field_group_field_overview_submit';
298

    
299
  // Create the settings for fieldgroup as vertical tabs (merged with DS).
300
  field_group_field_ui_create_vertical_tabs($form, $form_state, $params);
301

    
302
  // Show a warning if the user has not set up required containers
303
  if ($form['#groups']) {
304

    
305
    $parent_requirements = array(
306
      'multipage' => array(
307
        'parent' => 'multipage-group',
308
        'message' => 'Each Multipage element needs to have a parent Multipage group element.',
309
      ),
310
      'htab' => array(
311
        'parent' => 'htabs',
312
        'message' => 'Each Horizontal tab element needs to have a parent Horizontal tabs group element.',
313
      ),
314
      'accordion-item' => array(
315
        'parent' => 'accordion',
316
        'message' => 'Each Accordion item element needs to have a parent Accordion group element.',
317
      ),
318
    );
319

    
320
    // On display overview tabs need to be checked.
321
    if ($display_overview) {
322
      $parent_requirements['tab'] = array(
323
        'parent' => 'tabs',
324
        'message' => 'Each Vertical tab element needs to have a parent Vertical tabs group element.',
325
      );
326
    }
327

    
328
    foreach ($form['#groups'] as $group_name) {
329
      $group_check = field_group_load_field_group($group_name, $params->entity_type, $params->bundle, $params->mode);
330
      if (isset($parent_requirements[$group_check->format_type])) {
331
        if (!$group_check->parent_name || field_group_load_field_group($group_check->parent_name, $params->entity_type, $params->bundle, $params->mode)->format_type != $parent_requirements[$group_check->format_type]['parent']) {
332
          drupal_set_message(t($parent_requirements[$group_check->format_type]['message']), 'warning', FALSE);
333
        }
334
      }
335
    }
336
  }
337
}
338

    
339
/**
340
 * Return an array of field_group_formatter options.
341
 */
342
function field_group_field_formatter_options($type) {
343
  $options = &drupal_static(__FUNCTION__);
344

    
345
  if (!isset($options)) {
346
    $options = array();
347
    $field_group_types = field_group_formatter_info();
348
    foreach ($field_group_types[$type] as $name => $field_group_type) {
349
      $options[$name] = $field_group_type['label'];
350
    }
351
  }
352
  return $options;
353
}
354

    
355
/**
356
 * Helper function to add a row in the overview forms.
357
 */
358
function field_group_add_row($name, $parent_options, $params) {
359
  return array(
360
    '#attributes' => array('class' => array('draggable', 'field-group', 'add-new')),
361
    '#row_type' => 'add_new_group',
362
    '#js_settings' => array('rowHandler' => 'group'),
363
    '#region_callback' => $params->region_callback,
364
    'label' => array(
365
      '#title_display' => 'invisible',
366
      '#title' => t('Label for new group'),
367
      '#type' => 'textfield',
368
      '#size' => 15,
369
      '#description' => t('Label'),
370
      '#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . t('Add new group') . '</div>',
371
      '#suffix' => '</div>',
372
    ),
373
    'weight' => array(
374
      '#type' => 'textfield',
375
      '#default_value' => field_info_max_weight($params->entity_type, $params->bundle, $params->mode) + 3,
376
      '#size' => 3,
377
      '#title_display' => 'invisible',
378
      '#title' => t('Weight for new group'),
379
      '#attributes' => array('class' => array('field-weight')),
380
      '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
381
    ),
382
    'parent_wrapper' => array(
383
      'parent' => array(
384
        '#title_display' => 'invisible',
385
        '#title' => t('Parent for new group'),
386
        '#type' => 'select',
387
        '#options' => $parent_options,
388
        '#empty_value' => '',
389
        '#attributes' => array('class' => array('field-parent')),
390
        '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
391
        '#parents' => array('fields', $name, 'parent'),
392
      ),
393
      'hidden_name' => array(
394
        '#type' => 'hidden',
395
        '#default_value' => $name,
396
        '#attributes' => array('class' => array('field-name')),
397
      ),
398
    ),
399
    'group_name' => array(
400
      '#type' => 'textfield',
401
      '#title_display' => 'invisible',
402
      '#title' => t('Machine name for new group'),
403
      // This field should stay LTR even for RTL languages.
404
      '#field_prefix' => '<span dir="ltr">group_',
405
      '#field_suffix' => '</span>&lrm;',
406
      '#attributes' => array('dir' => 'ltr'),
407
      '#size' => 15,
408
      '#description' => t('Group name (a-z, 0-9, _)'),
409
      '#prefix' => '<div class="add-new-placeholder">&nbsp;</div>',
410
      '#cell_attributes' => array('colspan' => $params->display_overview ? 1 : 2),
411
    ),
412
  );
413
}
414

    
415
/**
416
 * Creates a form for field_group formatters.
417
 * @param Object $group The FieldGroup object.
418
 */
419
function field_group_format_settings_form(&$group) {
420
  $form = array();
421
  $form['label'] = array(
422
    '#type' => 'textfield',
423
    '#title' => t('Field group label'),
424
    '#default_value' => $group->label,
425
    '#weight' => -5,
426
    '#element_validate' => array('field_group_format_settings_label_validate'),
427
  );
428
  $addition = module_invoke_all('field_group_format_settings', $group);
429
  $form += $addition;
430
  // Give fieldgroup implementations the chance to alter the description.
431
  if (!empty($addition['label']['#description'])) {
432
    $form['label']['#description'] = $addition['label']['#description'];
433
  }
434
  $form['#validate'] = array('field_group_format_settings_form_validate');
435
  return $form;
436
}
437

    
438
/**
439
 * Validate the label. Label is required for fieldsets that are collapsible.
440
 */
441
function field_group_format_settings_label_validate($element, &$form_state) {
442

    
443
  $group = $form_state['values']['fields'][$element['#parents'][1]];
444
  $settings = $group['format_settings']['settings'];
445
  $name = $form_state['formatter_settings_edit'];
446
  $form_state['values']['fields'][$name]['settings_edit_form']['settings'] = $settings;
447
  if ($group['format']['type'] == 'fieldset' && ($settings['formatter'] == 'collapsed' || $settings['formatter'] == 'collapsible') && empty($settings['label'])) {
448
    form_error($element, t('The label is required when formatter is collapsible or collapsed'));
449
  }
450

    
451
}
452

    
453
/**
454
 * Update the row so that the group variables are updated.
455
 * The rendering of the elements needs the updated defaults.
456
 * @param Object $group
457
 * @param array $settings
458
 */
459
function field_group_formatter_row_update(& $group, $settings) {
460
  // if the row has changed formatter type, update the group object
461
  if (!empty($settings['format']['type']) && $settings['format']['type'] != $group->format_type) {
462
    $group->format_type = $settings['format']['type'];
463
    field_group_formatter_settings_update($group, $settings);
464
  }
465
}
466

    
467
/**
468
 * Update handler for field_group configuration settings.
469
 * @param Object $group The group object
470
 * @param Array $settings Configuration settings
471
 */
472
function field_group_formatter_settings_update(& $group, $settings) {
473

    
474
  // for format changes we load the defaults.
475
  if (empty($settings['format_settings']['settings'])) {
476
    $mode = $group->mode == 'form' ? 'form' : 'display';
477
    $group->format_settings = _field_group_get_default_formatter_settings($group->format_type, $mode);
478
  }
479
  else {
480
    $group->format_type = $settings['format']['type'];
481
    $group->label = $settings['format_settings']['settings']['label'];
482
    $group->format_settings = $settings['format_settings']['settings'];
483
  }
484
}
485

    
486
/**
487
 * Creates a summary for the field format configuration summary.
488
 * @param String $group_name The name of the group
489
 * @param Object $group The group object
490
 * @return Array ready to be rendered.
491
 */
492
function field_group_format_settings_summary($group_name, $group) {
493
  $summary = implode('<br />', module_invoke_all('field_group_format_summary', $group));
494
  return array(
495
    '#markup' => '<div class="field-formatter-summary">' . $summary . '</div>',
496
    '#cell_attributes' => array('class' => array('field-formatter-summary-cell')),
497
  );
498
}
499

    
500
/**
501
 * Returns the region to which a row in the 'Manage fields' screen belongs.
502
 * @param Array $row A field or field_group row
503
 * @return String the current region.
504
 */
505
function field_group_field_overview_row_region($row) {
506
  switch ($row['#row_type']) {
507
    case 'group':
508
      return 'main';
509
    case 'add_new_group':
510
      // If no input in 'label', assume the row has not been dragged out of the
511
      // 'add new' section.
512
      if (empty($row['label']['#value'])) {
513
        return 'add_new';
514
      }
515
      return 'main';
516
  }
517
}
518

    
519
/**
520
 * Returns the region to which a row in the 'Manage display' screen belongs.
521
 * @param Array $row A field or field_group row
522
 * @return String the current region.
523
 */
524
function field_group_display_overview_row_region($row) {
525
  switch ($row['#row_type']) {
526
    case 'group':
527
      return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'visible');
528
    case 'add_new_group':
529
      // If no input in 'label', assume the row has not been dragged out of the
530
      // 'add new' section.
531
      if (empty($row['label']['#value'])) {
532
        return 'add_new';
533
      }
534
      return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'visible');
535
  }
536
}
537

    
538
/**
539
 * Validate handler for the overview screens.
540
 * @param Array $form The complete form.
541
 * @param Array $form_state The state of the form.
542
 */
543
function field_group_field_overview_validate($form, &$form_state) {
544
  $form_values = $form_state['values']['fields'];
545
  $entity_type = $form['#entity_type'];
546
  $bundle = $form['#bundle'];
547
  $mode = (isset($form['#view_mode']) ? $form['#view_mode'] : 'form');
548

    
549
  $group = $form_values['_add_new_group'];
550

    
551
  // Validate if any information was provided in the 'add new group' row.
552
  if (array_filter(array($group['label'], $group['group_name']))) {
553

    
554
    // Missing group name.
555
    if (!$group['group_name']) {
556
      form_set_error('fields][_add_new_group][group_name', t('Add new group: you need to provide a group name.'));
557
    }
558
    // Group name validation.
559
    else {
560
      $group_name = $group['group_name'];
561

    
562
      // Add the 'group_' prefix.
563
      if (drupal_substr($group_name, 0, 6) != 'group_') {
564
        $group_name = 'group_' . $group_name;
565
        form_set_value($form['fields']['_add_new_group']['group_name'], $group_name, $form_state);
566
      }
567

    
568
      // Invalid group name.
569
      if (!preg_match('!^group_[a-z0-9_]+$!', $group_name)) {
570
        form_set_error('fields][_add_new_group][group_name', t('Add new group: the group name %group_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%group_name' => $group_name)));
571
      }
572
      if (drupal_strlen($group_name) > 32) {
573
        form_set_error('fields][_add_new_group][group_name', t("Add new group: the group name %group_name is too long. The name is limited to 32 characters, including the 'group_' prefix.", array('%group_name' => $group_name)));
574
      }
575

    
576
      // Group name already exists.
577
      if (field_group_exists($group_name, $entity_type, $bundle, $mode)) {
578
        form_set_error('fields][_add_new_group][group_name', t('Add new group: the group name %group_name already exists.', array('%group_name' => $group_name)));
579
      }
580
    }
581
  }
582
}
583

    
584
/**
585
 * Submit handler for the overview screens.
586
 * @param Array $form The complete form.
587
 * @param Array $form_state The state of the form.
588
 */
589
function field_group_field_overview_submit($form, &$form_state) {
590

    
591
  $form_values = $form_state['values']['fields'];
592
  $entity_type = $form['#entity_type'];
593
  $bundle = $form['#bundle'];
594
  $mode = (isset($form['#view_mode']) ? $form['#view_mode'] : 'form');
595

    
596
  // Collect children.
597
  $children = array_fill_keys($form['#groups'], array());
598
  foreach ($form_values as $name => $value) {
599
    if (!empty($value['parent'])) {
600
      // Substitute newly added fields, in case they were dragged
601
      // directly in a group.
602
      if ($name == '_add_new_field' && isset($form_state['fields_added']['_add_new_field'])) {
603
        $name = $form_state['fields_added']['_add_new_field'];
604
      }
605
      elseif ($name == '_add_existing_field' && isset($form_state['fields_added']['_add_existing_field'])) {
606
        $name = $form_state['fields_added']['_add_existing_field'];
607
      }
608
      $children[$value['parent']][$name] = $name;
609
    }
610
  }
611

    
612
  // Prepare storage with ctools.
613
  ctools_include('export');
614

    
615
  // Create new group.
616
  if (!empty($form_values['_add_new_group']['group_name'])) {
617
    $values = $form_values['_add_new_group'];
618

    
619
    $field_group_types = field_group_formatter_info();
620
    $formatter = $field_group_types[($mode == 'form' ? 'form' : 'display')][$values['format']['type']];
621

    
622
    $new_group = (object) array(
623
      'identifier' => $values['group_name'] . '|' . $entity_type . '|' . $bundle . '|' . $mode,
624
      'group_name' => $values['group_name'],
625
      'entity_type' => $entity_type,
626
      'bundle' => $bundle,
627
      'mode' => $mode,
628
      'children' => isset($children['_add_new_group']) ? array_keys($children['_add_new_group']) : array(),
629
      'parent_name' => $values['parent'],
630
      'weight' => $values['weight'],
631
      'label' => $values['label'],
632
      'format_type' => $values['format']['type'],
633
      'disabled' => FALSE,
634
    );
635
    $new_group->format_settings = array('formatter' => isset($formatter['default_formatter']) ? $formatter['default_formatter'] : '');
636
    if (isset($formatter['instance_settings'])) {
637
      $new_group->format_settings['instance_settings'] = $formatter['instance_settings'];
638
    }
639

    
640
    $classes = _field_group_get_html_classes($new_group);
641
    $new_group->format_settings['instance_settings']['classes'] = implode(' ', $classes->optional);
642

    
643
    // Save and enable it in ctools.
644
    ctools_export_crud_save('field_group', $new_group);
645
    ctools_export_crud_enable('field_group', $new_group->identifier);
646

    
647
    // Store new group information for any additional submit handlers.
648
    $form_state['groups_added']['_add_new_group'] = $new_group->group_name;
649
    drupal_set_message(t('New group %label successfully created.', array('%label' => $new_group->label)));
650

    
651
    // Replace the newly created group in the $children array, in case it was
652
    // dragged directly in an existing field.
653
    foreach (array_keys($children) as $parent) {
654
      if (isset($children[$parent]['_add_new_group'])) {
655
        unset($children[$parent]['_add_new_group']);
656
        $children[$parent][$new_group->group_name] = $new_group->group_name;
657
      }
658
    }
659
  }
660

    
661
  // Update existing groups.
662
  $groups = field_group_info_groups($entity_type, $bundle, $mode, TRUE);
663
  foreach ($form['#groups'] as $group_name) {
664
    $group = $groups[$group_name];
665
    $group->label = $form_state['field_group'][$group_name]->label;
666
    $group->children = array_keys($children[$group_name]);
667
    $group->parent_name = $form_values[$group_name]['parent'];
668
    $group->weight = $form_values[$group_name]['weight'];
669

    
670
    $old_format_type = $group->format_type;
671
    $group->format_type = isset($form_values[$group_name]['format']['type']) ? $form_values[$group_name]['format']['type'] : 'visible';
672
    if (isset($form_state['field_group'][$group_name]->format_settings)) {
673
      $group->format_settings = $form_state['field_group'][$group_name]->format_settings;
674
    }
675

    
676
    // If the format type is changed, make sure we have all required format settings.
677
    if ($group->format_type != $old_format_type) {
678
      $mode = $group->mode == 'form' ? 'form' : 'display';
679
      $default_formatter_settings = _field_group_get_default_formatter_settings($group->format_type, $mode);
680
      $group->format_settings += $default_formatter_settings;
681
      $group->format_settings['instance_settings'] += $default_formatter_settings['instance_settings'];
682
    }
683

    
684
    ctools_export_crud_save('field_group', $group);
685
  }
686

    
687
  cache_clear_all('field_groups', 'cache_field');
688
}
689

    
690
/**
691
 * Validate the entered css class from the submitted format settings.
692
 * @param Array $element The validated element
693
 * @param Array $form_state The state of the form.
694
 */
695
function field_group_validate_css_class($element, &$form_state) {
696
  if (!empty($form_state['values']['fields'][$form_state['formatter_settings_edit']]['format_settings']['settings']['instance_settings']['classes']) && !preg_match('!^[A-Za-z0-9-_ ]+$!', $form_state['values']['fields'][$form_state['formatter_settings_edit']]['format_settings']['settings']['instance_settings']['classes'])) {
697
    form_error($element, t('The css class must include only letters, numbers, underscores and dashes.'));
698
  }
699
}
700

    
701
/**
702
 * Validate the entered id attribute from the submitted format settings.
703
 * @param Array $element The validated element
704
 * @param Array $form_state The state of the form.
705
 */
706
function field_group_validate_id($element, &$form_state) {
707
  if (!empty($form_state['values']['fields'][$form_state['formatter_settings_edit']]['format_settings']['settings']['instance_settings']['id']) && !preg_match('!^[A-Za-z0-9-_]+$!', $form_state['values']['fields'][$form_state['formatter_settings_edit']]['format_settings']['settings']['instance_settings']['id'])) {
708
    form_error($element, t('The id must include only letters, numbers, underscores and dashes.'));
709
  }
710
}
711

    
712
/**
713
 * Implements hook_field_info_max_weight().
714
 */
715
function field_group_field_info_max_weight($entity_type, $bundle, $context) {
716
  $weights = array();
717
  foreach (field_group_info_groups($entity_type, $bundle, $context) as $group) {
718
    $weights[] = $group->weight;
719
  }
720
  return $weights ? max($weights) : NULL;
721
}
722

    
723
/**
724
 * Menu callback; present a form for removing a group.
725
 */
726
function field_group_delete_form($form, &$form_state, $group, $view_mode = 'form') {
727

    
728
  $form['#group'] = $group;
729
  $admin_path = _field_ui_bundle_admin_path($group->entity_type, $group->bundle);
730
  if ($view_mode == 'form') {
731
    $admin_path .= '/fields';
732
  }
733
  else {
734
    $admin_path .= '/display/' . $view_mode;
735
  }
736
  $form['#redirect'] = array($admin_path);
737
  $output = confirm_form($form,
738
    t('Are you sure you want to delete the group %group?', array('%group' => t($group->label))),
739
    $admin_path,
740
    t('This action cannot be undone.'),
741
    t('Delete'), t('Cancel'),
742
    'confirm'
743
  );
744
  return $output;
745
}
746

    
747
/**
748
 * Remove group from bundle.
749
 *
750
 * @todo we'll have to reset all view mode settings - that will be fun :)
751
 */
752
function field_group_delete_form_submit($form, &$form_state) {
753

    
754
  $group = $form['#group'];
755
  $bundle = $group->bundle;
756
  $entity_type = $group->entity_type;
757
  $group->mode = $form_state['build_info']['args'][1];
758

    
759
  $bundles = field_info_bundles();
760
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
761

    
762
  ctools_include('export');
763
  field_group_group_export_delete($group, FALSE);
764

    
765
  drupal_set_message(t('The group %group has been deleted from the %type content type.', array('%group' => t($group->label), '%type' => $bundle_label)));
766

    
767
  // Redirect.
768
  $form_state['redirect'] = $form['#redirect'];
769
}
770

    
771
/**
772
 * Menu callback; present a form for re-enabling a group.
773
 */
774
function field_group_enable_form($form, &$form_state, $group, $view_mode = 'form') {
775

    
776
  $form['#group'] = $group;
777
  $admin_path = _field_ui_bundle_admin_path($group->entity_type, $group->bundle);
778
  if ($view_mode == 'form') {
779
    $admin_path .= '/fields';
780
  }
781
  else {
782
    $admin_path .= '/display/' . $view_mode;
783
  }
784
  $form['#redirect'] = array($admin_path);
785
  $output = confirm_form($form,
786
    t('Are you sure you want to enable the group %group?', array('%group' => t($group->label))),
787
    $admin_path,
788
    '',
789
    t('Enable'), t('Cancel'),
790
    'confirm'
791
  );
792
  return $output;
793
}
794

    
795
/**
796
 * Re-enable the group on a bundle.
797
 */
798
function field_group_enable_form_submit($form, &$form_state) {
799

    
800
  $group = $form['#group'];
801
  $bundle = $group->bundle;
802
  $entity_type = $group->entity_type;
803
  $group->mode = $form_state['build_info']['args'][1];
804

    
805
  $bundles = field_info_bundles();
806
  $bundle_label = $bundles[$entity_type][$bundle]['label'];
807

    
808
  ctools_include('export');
809
  ctools_export_crud_enable('field_group', $group->identifier);
810

    
811
  drupal_set_message(t('The group %group has been enabled on the %type content type.', array('%group' => t($group->label), '%type' => $bundle_label)));
812

    
813
  // Redirect.
814
  $form_state['redirect'] = $form['#redirect'];
815
}
816

    
817
/**
818
 * Create vertical tabs.
819
 */
820
function field_group_field_ui_create_vertical_tabs(&$form, &$form_state, $params) {
821

    
822
  $form_state['field_group_params'] = $params;
823

    
824
  $entity_info = entity_get_info($params->entity_type);
825
  $view_modes = array();
826
  if ($params->mode != 'default') {
827
    $view_modes['default'] = t('Default');
828
  }
829
  if ($params->mode != 'form') {
830
    $view_modes['0'] = t('Form');
831
  }
832
  foreach ($entity_info['view modes'] as $view_mode => $data) {
833
    if ($data['custom settings'] && $params->mode != $view_mode) {
834
      $view_modes[$view_mode] = $data['label'];
835
    }
836
  }
837

    
838
  // Add additional settings vertical tab.
839
  if (!isset($form['additional_settings'])) {
840
    $form['additional_settings'] = array(
841
      '#type' => 'vertical_tabs',
842
      '#theme_wrappers' => array('vertical_tabs'),
843
      '#prefix' => '<div>',
844
      '#suffix' => '</div>',
845
      '#tree' => TRUE,
846
    );
847
    $form['#attached']['js'][] = 'misc/form.js';
848
    $form['#attached']['js'][] = 'misc/collapse.js';
849
  }
850

    
851
  // Add extra guidelines for webmaster.
852
  $form['additional_settings']['field_group'] = array(
853
    '#type' => 'fieldset',
854
    '#title' => t('Fieldgroups'),
855
    '#description' => t('<p class="fieldgroup-help">Fields can be dragged into groups with unlimited nesting. Each fieldgroup format comes with a configuration form, specific for that format type.<br />Note that some formats come in pair. These types have a html wrapper to nest its fieldgroup children. E.g. Place accordion items into the accordion, vertical tabs in vertical tab group and horizontal tabs in the horizontal tab group. There is one exception to this rule, you can use a vertical tab without a wrapper when the additional settings tabs are available. E.g. node forms.</p>'),
856
    '#collapsible' => TRUE,
857
    '#collapsed' => FALSE,
858
    '#parents' => array('additional_settings'),
859
  );
860
  $form['additional_settings']['field_group']['fieldgroup_clone'] = array(
861
    '#title' => t('Select source view mode or form'),
862
    '#description' => t('Clone fieldgroups from selected view mode to the current display'),
863
    '#type' => 'select',
864
    '#options' => $view_modes,
865
    '#default_value' => 'none'
866
  );
867
  $form['additional_settings']['field_group']['fieldgroup_submit'] = array(
868
    '#type' => 'submit',
869
    '#value' => t('Clone'),
870
    '#validate' => array('field_group_field_ui_clone_field_groups_validate'),
871
    '#submit' => array('field_group_field_ui_clone_field_groups')
872
  );
873

    
874
  $disabled_groups = field_group_read_groups(array(), FALSE);
875

    
876
  // Show disabled fieldgroups, and make it possible to enable them again.
877
  if ($disabled_groups && isset($disabled_groups[$params->entity_type][$params->bundle][$params->mode])) {
878
    $form['additional_settings']['disabled_field_groups'] = array(
879

    
880
      '#type' => 'fieldset',
881

    
882
      '#title' => t('Disabled fieldgroups'),
883

    
884
      '#collapsible' => TRUE,
885

    
886
      '#collapsed' => FALSE,
887

    
888
      '#parents' => array('additional_settings'),
889

    
890
    );
891
    $form['additional_settings']['disabled_field_groups']['overview'] = field_group_disabled_groups_overview($disabled_groups[$params->entity_type][$params->bundle][$params->mode], $entity_info, $params);
892
  }
893

    
894
}
895

    
896
/**
897
 * Validate handler to validate saving existing fieldgroups from one view mode or form to another.
898
 */
899
function field_group_field_ui_clone_field_groups_validate($form, &$form_state) {
900

    
901
  $source_mode = $form_state['#source_mode'] = $form_state['values']['additional_settings']['fieldgroup_clone'] == '0' ? 'form' : $form_state['values']['additional_settings']['fieldgroup_clone'];
902
  $groups_to_clone = $form_state['#groups_to_clone'] = field_group_read_groups(array('bundle' => $form_state['field_group_params']->bundle, 'entity_type' => $form_state['field_group_params']->entity_type));
903

    
904
  $form_state['#source_groups'] = array();
905
  if (!empty($groups_to_clone) && isset($groups_to_clone[$form_state['field_group_params']->entity_type], $groups_to_clone[$form_state['field_group_params']->entity_type][$form_state['field_group_params']->bundle], $groups_to_clone[$form_state['field_group_params']->entity_type][$form_state['field_group_params']->bundle][$source_mode])) {
906
    $form_state['#source_groups'] = $groups_to_clone[$form_state['field_group_params']->entity_type][$form_state['field_group_params']->bundle][$source_mode];
907
  }
908

    
909
  // Check for types are not known in current mode.
910
  if ($form_state['field_group_params']->mode != 'form') {
911
    $non_existing_types = array('multipage', 'multipage-group');
912
  }
913
  else {
914
    $non_existing_types = array('div');
915
  }
916

    
917
  foreach ($form_state['#source_groups'] as $key => $group) {
918
    if (in_array($group->format_type, $non_existing_types)) {
919
      unset($form_state['#source_groups'][$key]);
920
      drupal_set_message(t('Skipping @group because this type does not exist in current mode', array('@group' => $group->label)), 'warning');
921
    }
922
  }
923

    
924
  if (empty($form_state['#source_groups'])) {
925
    // Report error found with selection.
926
    form_set_error('additional_settings][fieldgroup_clone', t('No field groups were found in selected view mode.'));
927
    return;
928
  }
929

    
930
}
931

    
932
/**
933
 * Submit handler to save existing fieldgroups from one view mode or form to another.
934
 */
935
function field_group_field_ui_clone_field_groups($form, &$form_state) {
936

    
937
  $source_mode = $form_state['#source_mode'];
938
  $groups_to_clone = $form_state['#groups_to_clone'];
939

    
940
  $fields = array_keys($form_state['values']['fields']);
941
  if (!empty($form_state['#source_groups'])) {
942

    
943
    foreach ($form_state['#source_groups'] as $source_group) {
944
      if (in_array($source_group->group_name, $fields)) {
945
        drupal_set_message(t('Fieldgroup @group is not cloned since a group already exists with the same name.', array('@group' => $source_group->group_name)), 'warning');
946
        continue;
947
      }
948

    
949
      // Recreate the identifier and reset the id.
950
      $source_group->id = NULL;
951
      $source_group->mode = $form_state['field_group_params']->mode;
952
      $source_group->identifier = $source_group->group_name . '|' . $source_group->entity_type . '|' . $source_group->bundle . '|' . $form_state['field_group_params']->mode;
953
      $source_group->disabled = FALSE;
954
      $source_group->children = array();
955
      unset($source_group->export_type, $source_group->type, $source_group->table);
956

    
957
      // Save and enable it in ctools.
958
      ctools_include('export');
959
      ctools_export_crud_save('field_group', $source_group);
960
      ctools_export_crud_enable('field_group', $source_group->identifier);
961

    
962
      drupal_set_message(t('Fieldgroup @group cloned successfully.', array('@group' => $source_group->group_name)));
963

    
964
    }
965
  }
966

    
967
}
968

    
969
/**
970
 * Show an overview of all the disabled fieldgroups, and make it possible to activate them again.
971
 * @param $disabled_groups Array with all disabled groups.
972
 */
973
function field_group_disabled_groups_overview($disabled_groups, $entity_info, $params) {
974

    
975
  $formatter_options = field_group_field_formatter_options($params->mode != 'form' ? 'display' : 'form');
976

    
977
  $table = array(
978
    '#theme' => 'table',
979
    '#header' => array(
980
      t('Label'),
981
      t('Machine name'),
982
      t('Field'),
983
      t('Widget'),
984
      t('Operations'),
985
    ),
986
    '#attributes' => array(
987
      'class' => array('field-ui-overview'),
988
    ),
989
    '#rows' => array(),
990
  );
991

    
992
  // Add all of the disabled groups as a row on the table.
993
  foreach ($disabled_groups as $group) {
994

    
995
    $summary = field_group_format_settings_summary($group->group_name, $group);
996

    
997
    $row = array();
998
    $row[] = $group->label;
999
    $row[] = $group->group_name;
1000
    $row[] = $formatter_options[$group->format_type];
1001
    $row[] = render($summary);
1002
    $path = (isset($entity_info['bundles'][$params->bundle]['admin']['real path']) ? $entity_info['bundles'][$params->bundle]['admin']['real path'] : $entity_info['bundles'][$params->bundle]['admin']['path']);
1003
    $row[] = l(t('Enable'), $path . '/groups/' . $group->group_name . '/enable/' . $group->mode);
1004

    
1005
    $table['#rows'][] = $row;
1006
  }
1007

    
1008
  return $table;
1009

    
1010
}
1011
/**
1012
 * eof().
1013
 */