Projet

Général

Profil

Paste
Télécharger (75,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / field_group / field_group.module @ 64ad485a

1
<?php
2

    
3
/**
4
 * @file
5
 * Fieldgroup module.
6
 *
7
 * For an overview of all php and javascript hooks, see field_group.api.php.
8
 *
9
 */
10

    
11
/**
12
 * Implements hook_menu().
13
 */
14
function field_group_menu() {
15
  $items = array();
16

    
17
  // Ensure the following is not executed until field_bundles is working and
18
  // tables are updated. Needed to avoid errors on initial installation.
19
  if (defined('MAINTENANCE_MODE')) {
20
    return $items;
21
  }
22

    
23
  // Create tabs for all possible bundles.
24
  foreach (entity_get_info() as $entity_type => $entity_info) {
25
    if (isset($entity_info['fieldable']) && $entity_info['fieldable']) {
26
      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
27
        if (isset($bundle_info['admin'])) {
28
          // Extract path information from the bundle.
29
          $path = $bundle_info['admin']['path'];
30
          // Different bundles can appear on the same path (e.g. %node_type and
31
          // %comment_node_type). To allow field_group_menu_load() to extract the
32
          // actual bundle object from the translated menu router path
33
          // arguments, we need to identify the argument position of the bundle
34
          // name string ('bundle argument') and pass that position to the menu
35
          // loader. The position needs to be casted into a string; otherwise it
36
          // would be replaced with the bundle name string.
37
          if (isset($bundle_info['admin']['bundle argument'])) {
38
            $bundle_arg = $bundle_info['admin']['bundle argument'];
39
            $bundle_pos = (string) $bundle_arg;
40
          }
41
          else {
42
            $bundle_arg = $bundle_name;
43
            $bundle_pos = '0';
44
          }
45

    
46
          // This is the position of the %field_group_menu placeholder in the
47
          // items below.
48
          $group_position = count(explode('/', $path)) + 1;
49

    
50
          // Extract access information, providing defaults.
51
          $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
52
          $access += array(
53
            'access callback' => 'user_access',
54
            'access arguments' => array('administer site configuration'),
55
          );
56

    
57
          $items["$path/groups/%field_group_menu/delete"] = array(
58
            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
59
            'title' => 'Delete',
60
            'page callback' => 'drupal_get_form',
61
            'page arguments' => array('field_group_delete_form', $group_position),
62
            'type' => MENU_CALLBACK,
63
            'file' => 'field_group.field_ui.inc',
64
          ) + $access;
65

    
66
          $items["$path/groups/%field_group_menu/enable"] = array(
67
            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
68
            'title' => 'Enable',
69
            'page callback' => 'drupal_get_form',
70
            'page arguments' => array('field_group_enable_form', $group_position),
71
            'type' => MENU_CALLBACK,
72
            'file' => 'field_group.field_ui.inc',
73
          ) + $access;
74

    
75
        }
76
      }
77
    }
78
  }
79

    
80
  return $items;
81
}
82

    
83
/**
84
 * Implements hook_permission().
85
 */
86
function field_group_permission() {
87
  return array(
88
    'administer fieldgroups' => array(
89
      'title' => t('Administer fieldgroups'),
90
      'description' => t('Display the administration for fieldgroups.'),
91
    ),
92
  );
93
}
94

    
95
/**
96
 * Menu Wildcard loader function to load group definitions.
97
 *
98
 * @param $group_name
99
 *   The name of the group, as contained in the path.
100
 * @param $entity_type
101
 *   The name of the entity.
102
 * @param $bundle_name
103
 *   The name of the bundle, as contained in the path.
104
 * @param $bundle_pos
105
 *   The position of $bundle_name in $map.
106
 * @param $map
107
 *   The translated menu router path argument map.
108
 */
109
function field_group_menu_load($group_name, $entity_type, $bundle_name, $bundle_pos, $map) {
110

    
111
  if ($bundle_pos > 0) {
112
    $bundle = $map[$bundle_pos];
113
    $bundle_name = field_extract_bundle($entity_type, $bundle);
114
  }
115

    
116
  $args = func_get_args();
117
  $args_pop = array_pop($args);
118
  $mode = array_pop($args_pop);
119

    
120
  $group = field_group_load_field_group($group_name, $entity_type, $bundle_name, $mode);
121

    
122
  return empty($group) ? FALSE : $group;
123
}
124

    
125
/**
126
 * Loads a group definition.
127
 *
128
 * @param $group_name
129
 *   The name of the group.
130
 * @param $entity_type
131
 *   The name of the entity.
132
 * @param $bundle_name
133
 *   The name of the bundle.
134
 * @param $mode
135
 *   The view mode to load.
136
 */
137
function field_group_load_field_group($group_name, $entity_type, $bundle_name, $mode) {
138

    
139
  ctools_include('export');
140
  $objects = ctools_export_load_object('field_group', 'conditions', array(
141
    'group_name' => $group_name,
142
    'entity_type' => $entity_type,
143
    'bundle' => $bundle_name,
144
    'mode' => $mode,
145
  ));
146
  $object = array_shift($objects);
147

    
148
  if ($object && isset($object->data)) {
149
    return field_group_unpack($object);
150
  }
151

    
152
  return $object;
153
}
154

    
155
/**
156
 * Implements hook_ctools_plugin_api().
157
 */
158
function field_group_ctools_plugin_api($owner, $api) {
159
  if ($owner == 'field_group' && $api == 'field_group') {
160
    return array('version' => 1);
161
  }
162
}
163

    
164
/**
165
 * Implements hook_theme().
166
 */
167
function field_group_theme() {
168
  return array(
169
    'horizontal_tabs' => array(
170
      'render element' => 'element',
171
    ),
172
    'multipage' => array(
173
      'render element' => 'element',
174
    ),
175
    'multipage_pane' => array(
176
      'render element' => 'element',
177
    ),
178
  );
179
}
180

    
181
/**
182
 * Implements hook_theme_registry_alter().
183
 */
184
function field_group_theme_registry_alter(&$theme_registry) {
185

    
186
  // Inject field_group_build_entity_groups in all entity theming functions.
187
  $entity_info = entity_get_info();
188
  $entities = array();
189
  foreach ($entity_info as $entity => $info) {
190
    if (isset($entity_info[$entity]['fieldable']) && $entity_info[$entity]['fieldable']) {
191
      // User uses user_profile for theming.
192
      if ($entity == 'user') $entity = 'user_profile';
193
      $entities[] = $entity;
194
    }
195
  }
196

    
197
  // Support for File Entity.
198
  if (isset($theme_registry['file_entity'])) {
199
    $entities[] = 'file_entity';
200
  }
201

    
202
  // Support for Entity API.
203
  if (isset($theme_registry['entity'])) {
204
    $entities[] = 'entity';
205
  }
206

    
207
  foreach ($entities as $entity) {
208
    if (isset($theme_registry[$entity])) {
209
      $theme_registry[$entity]['preprocess functions'][] = 'field_group_build_entity_groups';
210
      // DS support, make sure it comes after field_group.
211
      if ($key = array_search('ds_entity_variables', $theme_registry[$entity]['preprocess functions'])) {
212
        unset($theme_registry[$entity]['preprocess functions'][$key]);
213
        $theme_registry[$entity]['preprocess functions'][] = 'ds_entity_variables';
214
      }
215
    }
216
  }
217

    
218
}
219

    
220
/**
221
 * Implements hook_field_attach_delete_bundle().
222
 *
223
 * @param String $entity_type
224
 * @param String $bundle
225
 */
226
function field_group_field_attach_delete_bundle($entity_type, $bundle) {
227

    
228
  ctools_include('export');
229
  $list = field_group_read_groups(array('bundle' => $bundle, 'entity_type' => $entity_type));
230

    
231
  // Delete the entity's entry from field_group of all entities.
232
  // We fetch the field groups first to assign the removal task to ctools.
233
  if (isset($list[$entity_type], $list[$entity_type][$bundle])) {
234
    foreach ($list[$entity_type][$bundle] as $group_mode => $groups) {
235
      foreach ($groups as $group) {
236
        ctools_export_crud_delete('field_group', $group);
237
      }
238
    }
239
  }
240

    
241
}
242

    
243
/**
244
 * Implements hook_field_attach_form().
245
 */
246
function field_group_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
247
  $form['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.field_ui.css';
248
  field_group_attach_groups($form, 'form', $form_state);
249
  $form['#pre_render'][] = 'field_group_form_pre_render';
250
}
251

    
252
/**
253
 * Implements hook_form_FORM_ID_alter().
254
 * Using hook_form_field_ui_field_overview_form_alter.
255
 */
256
function field_group_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
257
  form_load_include($form_state, 'inc', 'field_group', 'field_group.field_ui');
258
  field_group_field_ui_overview_form_alter($form, $form_state);
259
}
260

    
261
/**
262
 * Implements hook_form_FORM_ID_alter().
263
 * Using hook_form_field_ui_display_overview_form_alter.
264
 */
265
function field_group_form_field_ui_display_overview_form_alter(&$form, &$form_state) {
266
  form_load_include($form_state, 'inc', 'field_group', 'field_group.field_ui');
267
  field_group_field_ui_overview_form_alter($form, $form_state, TRUE);
268
}
269

    
270
/**
271
 * Implements hook_field_attach_view_alter().
272
 */
273
function field_group_field_attach_view_alter(&$element, $context) {
274
  // Check whether the view mode uses custom display settings or the 'default' mode.
275
  $actual_mode = 'default';
276
  if (isset($element['#entity_type']) && isset($element['#bundle'])) {
277
    $view_mode_settings = field_view_mode_settings($element['#entity_type'], $element['#bundle']);
278
    $view_mode = $context['view_mode'];
279
    $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default');
280
    field_group_attach_groups($element, $actual_mode);
281
  }
282
}
283

    
284
/**
285
 * Implements hook_field_group_formatter_info().
286
 */
287
function field_group_field_group_formatter_info() {
288

    
289
  return array(
290
    'form' => array(
291
      'html-element' => array(
292
        'label' => t('HTML element'),
293
        'description' => t('This fieldgroup renders the inner content in a HTML element with classes and attributes.'),
294
        'instance_settings' => array('element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1),
295
      ),
296
      'div' => array(
297
        'label' => t('Div'),
298
        'description' => t('This fieldgroup renders the inner content in a simple div with the title as legend.'),
299
        'format_types' => array('open', 'collapsible', 'collapsed'),
300
        'instance_settings' => array('description' => '', 'show_label' => 1, 'label_element' => 'h3', 'effect' => 'none', 'speed' => 'fast', 'classes' => '', 'required_fields' => 1, 'id' => ''),
301
        'default_formatter' => 'open',
302
      ),
303
      'html5' => array(
304
        'label' => t('HTML5'),
305
        'description' => t('This fieldgroup renders the inner content in a semantic HTML5 wrapper'),
306
        'instance_settings' => array('wrapper' => '', 'classes' => ''),
307
      ),
308
      'fieldset' => array(
309
        'label' => t('Fieldset'),
310
        'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'),
311
        'format_types' => array('open', 'collapsible', 'collapsed'),
312
        'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1),
313
        'default_formatter' => 'collapsible',
314
      ),
315
      'tabs' => array(
316
        'label' => t('Vertical tabs group'),
317
        'description' => t('This fieldgroup renders child groups in its own vertical tabs wrapper.'),
318
        'instance_settings' => array('classes' => ''),
319
      ),
320
      'tab' => array(
321
        'label' => t('Vertical tab'),
322
        'description' => t('This fieldgroup renders the content in a fieldset, part of vertical tabs group.'),
323
        'format_types' => array('open', 'closed'),
324
        'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1),
325
        'default_formatter' => 'closed',
326
      ),
327
      'htabs' => array(
328
        'label' => t('Horizontal tabs group'),
329
        'description' => t('This fieldgroup renders child groups in its own horizontal tabs wrapper.'),
330
        'instance_settings' => array('classes' => ''),
331
      ),
332
      'htab' => array(
333
        'label' => t('Horizontal tab'),
334
        'format_types' => array('open', 'closed'),
335
        'description' => t('This fieldgroup renders the content in a fieldset, part of horizontal tabs group.'),
336
        'default_formatter' => 'closed',
337
        'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1, 'id' => ''),
338
      ),
339
      'multipage-group' => array(
340
        'label' => t('Multipage group'),
341
        'description' => t('This fieldgroup renders groups on separate pages.'),
342
        'instance_settings' => array('classes' => '', 'page_header' => 3, 'move_additional' => 1, 'page_counter' => 1, 'move_button' => 0),
343
      ),
344
      'multipage' => array(
345
        'label' => t('Multipage'),
346
        'format_types' => array('start', 'no-start'),
347
        'description' => t('This fieldgroup renders the content in a page.'),
348
        'default_formatter' => 'no-start',
349
        'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1),
350
      ),
351
      'accordion' => array(
352
        'label' => t('Accordion group'),
353
        'description' => t('This fieldgroup renders child groups as jQuery accordion.'),
354
        'instance_settings' => array('effect' => 'none', 'classes' => ''),
355
      ),
356
      'accordion-item' => array(
357
        'label' => t('Accordion item'),
358
        'format_types' => array('open', 'closed'),
359
        'description' => t('This fieldgroup renders the content in a div, part of accordion group.'),
360
        'default_formatter' => 'closed',
361
        'instance_settings' => array('description' => '', 'classes' => '', 'required_fields' => 1),
362
      ),
363
    ),
364
    'display' => array(
365
      'html-element' => array(
366
        'label' => t('HTML element'),
367
        'description' => t('This fieldgroup renders the inner content in a HTML element with classes and attributes.'),
368
        'instance_settings' => array('element' => 'div', 'classes' => '', 'attributes' => '', 'required_fields' => 1),
369
      ),
370
      'div' => array(
371
        'label' => t('Div'),
372
        'description' => t('This fieldgroup renders the inner content in a simple div with the title as legend.'),
373
        'format_types' => array('open', 'collapsible', 'collapsed'),
374
        'instance_settings' => array('description' => '', 'show_label' => 1, 'label_element' => 'h3', 'effect' => 'none', 'speed' => 'fast', 'classes' => ''),
375
        'default_formatter' => 'collapsible',
376
      ),
377
      'html5' => array(
378
        'label' => t('HTML5'),
379
        'description' => t('This fieldgroup renders the inner content in a semantic HTML5 wrapper'),
380
        'instance_settings' => array('wrapper' => '', 'classes' => ''),
381
      ),
382
      'fieldset' => array(
383
        'label' => t('Fieldset'),
384
        'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'),
385
        'format_types' => array('open', 'collapsible', 'collapsed'),
386
        'instance_settings' => array('description' => '', 'classes' => ''),
387
        'default_formatter' => 'collapsible',
388
      ),
389
      'tabs' => array(
390
        'label' => t('Vertical tabs group'),
391
        'description' => t('This fieldgroup renders child groups in its own vertical tabs wrapper.'),
392
        'instance_settings' => array('classes' => ''),
393
      ),
394
      'tab' => array(
395
        'label' => t('Vertical tab'),
396
        'description' => t('This fieldgroup renders the content in a fieldset, part of vertical tabs group.'),
397
        'format_types' => array('open', 'closed'),
398
        'instance_settings' => array('description' => '', 'classes' => ''),
399
        'default_formatter' => 'closed',
400
      ),
401
      'htabs' => array(
402
        'label' => t('Horizontal tabs group'),
403
        'description' => t('This fieldgroup renders child groups in its own horizontal tabs wrapper.'),
404
        'instance_settings' => array('classes' => ''),
405
      ),
406
      'htab' => array(
407
        'label' => t('Horizontal tab item'),
408
        'format_types' => array('open', 'closed'),
409
        'description' => t('This fieldgroup renders the content in a fieldset, part of horizontal tabs group.'),
410
        'instance_settings' => array('description' => '', 'classes' => '', 'id' => ''),
411
        'default_formatter' => 'closed',
412
      ),
413
      'accordion' => array(
414
        'label' => t('Accordion group'),
415
        'description' => t('This fieldgroup renders child groups as jQuery accordion.'),
416
        'instance_settings' => array('description' => '', 'classes' => '', 'effect' => 'bounceslide'),
417
      ),
418
      'accordion-item' => array(
419
        'label' => t('Accordion item'),
420
        'format_types' => array('open', 'closed'),
421
        'description' => t('This fieldgroup renders the content in a div, part of accordion group.'),
422
        'instance_settings' => array('classes' => ''),
423
        'default_formatter' => 'closed',
424
      ),
425
    ),
426
  );
427
}
428

    
429
/**
430
 * Implements hook_field_group_format_settings().
431
 * If the group has no format settings, default ones will be added.
432
 * @params Object $group The group object.
433
 * @return Array $form The form element for the format settings.
434
 */
435
function field_group_field_group_format_settings($group) {
436
  // Add a wrapper for extra settings to use by others.
437
  $form = array(
438
    'instance_settings' => array(
439
      '#tree' => TRUE,
440
      '#weight' => 2,
441
    ),
442
  );
443

    
444
  $field_group_types = field_group_formatter_info();
445
  $mode = $group->mode == 'form' ? 'form' : 'display';
446
  $formatter = $field_group_types[$mode][$group->format_type];
447

    
448
  // Add the required formatter type selector.
449
  if (isset($formatter['format_types'])) {
450
    $form['formatter'] = array(
451
      '#title' => t('Fieldgroup settings'),
452
      '#type' => 'select',
453
      '#options' => drupal_map_assoc($formatter['format_types']),
454
      '#default_value' => isset($group->format_settings['formatter']) ? $group->format_settings['formatter'] : $formatter['default_formatter'],
455
      '#weight' => 1,
456
    );
457
  }
458

    
459
  if (isset($formatter['instance_settings']['required_fields']) && $mode == 'form') {
460
    $form['instance_settings']['required_fields'] = array(
461
      '#type' => 'checkbox',
462
      '#title' => t('Mark group as required if it contains required fields.'),
463
      '#default_value' => isset($group->format_settings['instance_settings']['required_fields']) ? $group->format_settings['instance_settings']['required_fields'] : (isset($formatter['instance_settings']['required_fields']) ? $formatter['instance_settings']['required_fields'] : ''),
464
      '#weight' => 2,
465
    );
466
  }
467

    
468
  if (isset($formatter['instance_settings']['id'])) {
469
    $form['instance_settings']['id'] = array(
470
      '#title' => t('ID'),
471
      '#type' => 'textfield',
472
      '#default_value' => isset($group->format_settings['instance_settings']['id']) ? $group->format_settings['instance_settings']['id'] : (isset($formatter['instance_settings']['id']) ? $formatter['instance_settings']['id'] : ''),
473
      '#weight' => 3,
474
      '#element_validate' => array('field_group_validate_id'),
475
    );
476
  }
477
  if (isset($formatter['instance_settings']['classes'])) {
478
    $form['instance_settings']['classes'] = array(
479
      '#title' => t('Extra CSS classes'),
480
      '#type' => 'textfield',
481
      '#default_value' => isset($group->format_settings['instance_settings']['classes']) ? $group->format_settings['instance_settings']['classes'] : (isset($formatter['instance_settings']['classes']) ? $formatter['instance_settings']['classes'] : ''),
482
      '#weight' => 4,
483
      '#element_validate' => array('field_group_validate_css_class'),
484
    );
485
  }
486

    
487
  if (isset($formatter['instance_settings']['description'])) {
488
    $form['instance_settings']['description'] = array(
489
      '#title' => t('Description'),
490
      '#type' => 'textarea',
491
      '#default_value' => isset($group->format_settings['instance_settings']['description']) ? $group->format_settings['instance_settings']['description'] : (isset($formatter['instance_settings']['description']) ? $formatter['instance_settings']['description'] : ''),
492
      '#weight' => 0,
493
    );
494
  }
495

    
496
  // Add optional instance_settings.
497
  switch ($group->format_type) {
498
    case 'html-element':
499
      $form['instance_settings']['element'] = array(
500
        '#title' => t('Element'),
501
        '#type' => 'textfield',
502
        '#default_value' => isset($group->format_settings['instance_settings']['element']) ? $group->format_settings['instance_settings']['element'] : $formatter['instance_settings']['element'],
503
        '#description' => t('E.g. div, section, aside etc.'),
504
        '#weight' => 2,
505
      );
506

    
507
      $form['instance_settings']['attributes'] = array(
508
        '#title' => t('Attributes'),
509
        '#type' => 'textfield',
510
        '#default_value' => isset($group->format_settings['instance_settings']['attributes']) ? $group->format_settings['instance_settings']['attributes'] : $formatter['instance_settings']['attributes'],
511
        '#description' => t('E.g. name="anchor"'),
512
        '#weight' => 4,
513
      );
514
      break;
515
    case 'div':
516
      $form['label']['#description'] = t('Please enter a label for collapsible elements');
517
      $form['instance_settings']['show_label'] = array(
518
        '#title' => t('Show label'),
519
        '#type' => 'select',
520
        '#options' => array(0 => t('No'), 1 => t('Yes')),
521
        '#default_value' => isset($group->format_settings['instance_settings']['show_label']) ? $group->format_settings['instance_settings']['show_label'] : $formatter['instance_settings']['show_label'],
522
        '#weight' => 2,
523
      );
524
      $form['instance_settings']['label_element'] = array(
525
        '#title' => t('Label element'),
526
        '#type' => 'select',
527
        '#options' => array('h2' => t('Header 2'), 'h3' => t('Header 3')),
528
        '#default_value' => isset($group->format_settings['instance_settings']['label_element']) ? $group->format_settings['instance_settings']['label_element'] : $formatter['instance_settings']['label_element'],
529
        '#weight' => 2,
530
      );
531
      $form['instance_settings']['effect'] = array(
532
        '#title' => t('Effect'),
533
        '#type' => 'select',
534
        '#options' => array('none' => t('None'), 'blind' => t('Blind')),
535
        '#default_value' => isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : $formatter['instance_settings']['effect'],
536
        '#weight' => 2,
537
      );
538
      $form['instance_settings']['speed'] = array(
539
        '#title' => t('Speed'),
540
        '#type' => 'select',
541
        '#options' => array('none' => t('None'), 'slow' => t('Slow'), 'fast' => t('Fast')),
542
        '#default_value' => isset($group->format_settings['instance_settings']['speed']) ? $group->format_settings['instance_settings']['speed'] : $formatter['instance_settings']['speed'],
543
        '#weight' => 3,
544
      );
545
      break;
546
    case 'html5':
547
      $form['instance_settings']['wrapper'] = array(
548
        '#title' => t('HTML5 wrapper'),
549
        '#type' => 'select',
550
        '#options' => array('section' => t('Section'), 'article' => t('Article'), 'header' => t('Header'), 'footer' => t('Footer'), 'aside' => t('Aside')),
551
        '#default_value' => isset($group->format_settings['instance_settings']['wrapper']) ? $group->format_settings['instance_settings']['wrapper'] : 'section',
552
      );
553
      break;
554
    case 'fieldset':
555
      $form['label']['#description'] = t('Please enter a label for collapsible elements');
556
      break;
557
    case 'multipage-group':
558
      $form['instance_settings']['page_header'] = array(
559
        '#title' => t('Format page title'),
560
        '#type' => 'select',
561
        '#options' => array(0 => t('None'), 1 => t('Label only'), 2 => t('Step 1 of 10'), 3 => t('Step 1 of 10 [Label]'),),
562
        '#default_value' => isset($group->format_settings['instance_settings']['page_header']) ? $group->format_settings['instance_settings']['page_header'] : $formatter['instance_settings']['page_header'],
563
        '#weight' => 1,
564
      );
565
      $form['instance_settings']['page_counter'] = array(
566
        '#title' => t('Add a page counter at the bottom'),
567
        '#type' => 'select',
568
        '#options' => array(0 => t('No'), 1 => t('Format 1 / 10'), 2 => t('The count number only')),
569
        '#default_value' => isset($group->format_settings['instance_settings']['page_counter']) ? $group->format_settings['instance_settings']['page_counter'] : $formatter['instance_settings']['page_counter'],
570
        '#weight' => 2,
571
      );
572
      $form['instance_settings']['move_button'] = array(
573
        '#title' => t('Move submit button to last multipage'),
574
        '#type' => 'select',
575
        '#options' => array(0 => t('No'), 1 => t('Yes')),
576
        '#default_value' => isset($group->format_settings['instance_settings']['move_button']) ? $group->format_settings['instance_settings']['move_button'] : $formatter['instance_settings']['move_button'],
577
        '#weight' => 3,
578
      );
579
      $form['instance_settings']['move_additional'] = array(
580
        '#title' => t('Move additional settings to last multipage (if available)'),
581
        '#type' => 'select',
582
        '#options' => array(0 => t('No'), 1 => t('Yes')),
583
        '#default_value' => isset($group->format_settings['instance_settings']['move_additional']) ? $group->format_settings['instance_settings']['move_additional'] : $formatter['instance_settings']['move_additional'],
584
        '#weight' => 4,
585
      );
586
    case 'tabs':
587
    case 'htabs':
588
      break;
589
    case 'accordion':
590
      $form['instance_settings']['effect'] = array(
591
        '#title' => t('Effect'),
592
        '#type' => 'select',
593
        '#options' => array('none' => t('None'), 'bounceslide' => t('Bounce slide')),
594
        '#default_value' => isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : $formatter['instance_settings']['effect'],
595
        '#weight' => 2,
596
      );
597
      break;
598
    case 'multipage':
599
      break;
600
    case 'tab':
601
    case 'htab':
602
    case 'accordion-item':
603
    default:
604
  }
605

    
606
  return $form;
607
}
608

    
609
/**
610
 * Helper function to prepare basic variables needed for most formatters.
611
 *
612
 * Called in field_group_field_group_pre_render(), but can also be called in
613
 * other implementations of hook_field_group_pre_render().
614
 */
615
function field_group_pre_render_prepare(&$group) {
616

    
617
  $classes = _field_group_get_html_classes($group);
618

    
619
  $group->classes = implode(' ', $classes->required);
620
  $group->description = isset($group->format_settings['instance_settings']['description']) ? filter_xss_admin(t($group->format_settings['instance_settings']['description'])) : '';
621

    
622
}
623

    
624
/**
625
 * Implements hook_field_group_pre_render().
626
 *
627
 * @param Array $elements by address.
628
 * @param Object $group The Field group info.
629
 */
630
function field_group_field_group_pre_render(& $element, &$group, & $form) {
631

    
632
  field_group_pre_render_prepare($group);
633

    
634
  $view_mode = isset($form['#view_mode']) ? $form['#view_mode'] : 'form';
635

    
636
  // Add all field_group format types to the js settings.
637
  $form['#attached']['js'][] = array(
638
    'data' => array('field_group' => array($group->format_type => $view_mode)),
639
    'type' => 'setting',
640
  );
641

    
642
  if (isset($group->format_settings['instance_settings']['id']) && !empty($group->format_settings['instance_settings']['id'])) {
643
    $element['#id'] = $group->format_settings['instance_settings']['id'];
644
  }
645
  else {
646
    $element['#id'] = $form['#entity_type'] . '_' . $form['#bundle'] . '_' . $view_mode . '_' . $group->group_name;
647
  }
648

    
649
  $element['#weight'] = $group->weight;
650

    
651
  // Call the pre render function for the format type.
652
  $function = "field_group_pre_render_" . str_replace("-", "_", $group->format_type);
653
  if (function_exists($function)) {
654
    $function($element, $group, $form);
655
  }
656

    
657
}
658

    
659
/**
660
 * Implements field_group_pre_render_<format-type>.
661
 * Format type: Fieldset.
662
 *
663
 * @param $element The field group form element.
664
 * @param $group The Field group object prepared for pre_render.
665
 * @param $form The root element or form.
666
 */
667
function field_group_pre_render_fieldset(&$element, $group, &$form) {
668

    
669
  $element += array(
670
    '#type' => 'fieldset',
671
    '#title' => check_plain(t($group->label)),
672
    '#collapsible' => $group->collapsible,
673
    '#collapsed' => $group->collapsed,
674
    '#pre_render' => array(),
675
    '#attributes' => array('class' => explode(' ', $group->classes)),
676
    '#description' => $group->description,
677
  );
678

    
679
  if ($group->collapsible || $group->collapsed) {
680
    $element['#attached']['library'][] = array('system', 'drupal.collapse');
681
  }
682
}
683

    
684
/**
685
 * Implements field_group_pre_render_<format-type>.
686
 * Format type: HTML element.
687
 *
688
 * @param $element The field group form element.
689
 * @param $group The Field group object prepared for pre_render.
690
 * @param $form The root element or form.
691
 */
692
function field_group_pre_render_html_element(&$element, $group, &$form) {
693
  $html_element = isset($group->format_settings['instance_settings']['element']) ? $group->format_settings['instance_settings']['element'] : 'div';
694
  $attributes = isset($group->format_settings['instance_settings']['attributes']) ? ' ' . $group->format_settings['instance_settings']['attributes'] : '';
695
  $group->classes = trim($group->classes);
696

    
697
  // This regex split the attributes string so that we can pass that
698
  // later to drupal_attributes().
699
  preg_match_all('/([^\s=]+)="([^"]+)"/', $attributes, $matches);
700

    
701
  $element_attributes = array();
702
  // Put the attribute and the value together.
703
  foreach ($matches[1] as $key => $attribute) {
704
    $element_attributes[$attribute] = $matches[2][$key];
705
  }
706

    
707
  // Add the classes to the attributes array.
708
  if (!isset($element_attributes['class']) && $group->classes) {
709
    $element_attributes['class'] = $group->classes;
710
  }
711
  elseif (isset($element_attributes['class']) && $group->classes) {
712
    $element_attributes['class'] .= ' ' . $group->classes;
713
  }
714

    
715
  $attributes = drupal_attributes($element_attributes);
716

    
717
  $element['#prefix'] = '<' . $html_element . $attributes . '>';
718
  $element['#suffix'] = '</' . $html_element . '>';
719
}
720

    
721
/**
722
 * Implements field_group_pre_render_<format-type>.
723
 * Format type: Div.
724
 *
725
 * @param $element The field group form element.
726
 * @param $group The Field group object prepared for pre_render.
727
 * @param $form The root element or form.
728
 */
729
function field_group_pre_render_div(&$element, $group, &$form) {
730

    
731
  $show_label = isset($group->format_settings['instance_settings']['show_label']) ? $group->format_settings['instance_settings']['show_label'] : 0;
732
  $label_element = isset($group->format_settings['instance_settings']['label_element']) ? $group->format_settings['instance_settings']['label_element'] : 'h2';
733
  $effect = isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : 'none';
734

    
735
  $element['#type'] = 'markup';
736
  if ($group->format_settings['formatter'] != 'open') {
737
    $element['#prefix'] = '<div id="' . $element['#id'] . '" class="' . $group->classes . '">
738
      <' . $label_element . '><span class="field-group-format-toggler">' . check_plain(t($group->label)) . '</span></' . $label_element . '>
739
      <div class="field-group-format-wrapper" style="display: ' . (!empty($group->collapsed) ? 'none' : 'block') . ';">';
740
    $element['#suffix'] = '</div></div>';
741
  }
742
  else {
743
    $class_attribute = '';
744
    if (!empty($group->classes)) {
745
      $class_attribute = 'class = "' . $group->classes . '"';
746
    }
747
    $element['#prefix'] = '<div id="' . $element['#id'] . '"' . $class_attribute . '>';
748
    if ($show_label) {
749
      $element['#prefix'] .= '<' . $label_element . '><span>' . check_plain(t($group->label)) . '</span></' . $label_element . '>';
750
    }
751
    $element['#suffix'] = '</div>';
752
  }
753
  if (!empty($group->description)) {
754
    $element['#prefix'] .= '<div class="description">' . $group->description . '</div>';
755
  }
756

    
757
  if ($effect == 'blind') {
758
    $element['#attached']['library'][] = array('system', 'effects.blind');
759
  }
760

    
761
}
762

    
763
/**
764
 * Implements field_group_pre_render_<format-type>.
765
 * Format type: HTML5.
766
 *
767
 * @param $element The field group form element.
768
 * @param $group The Field group object prepared for pre_render.
769
 * @param $form The root element or form.
770
 */
771
function field_group_pre_render_html5(&$element, $group, &$form) {
772
  $element += array(
773
    '#type' => 'markup',
774
    '#prefix' => '<' . $group->format_settings['instance_settings']['wrapper'] . ' id="' . $element['#id'] . '" class="' . $group->classes . '">',
775
    '#suffix' => '</' . $group->format_settings['instance_settings']['wrapper'] . '>',
776
  );
777
}
778

    
779
/**
780
 * Implements field_group_pre_render_<format-type>.
781
 * Format type: Accordion.
782
 *
783
 * @param $element The field group form element.
784
 * @param $group The Field group object prepared for pre_render.
785
 * @param $form The root element or form.
786
 */
787
function field_group_pre_render_accordion(&$element, $group, &$form) {
788

    
789
  // Add the jQuery UI accordion.
790
  $element['#attached']['library'][] = array('system', 'ui.accordion');
791

    
792
  $element += array(
793
    '#type' => 'markup',
794
    '#prefix' => '<div class="' . $group->classes . '">',
795
    '#suffix' => '</div>',
796
  );
797
}
798

    
799
/**
800
 * Implements field_group_pre_render_<format-type>.
801
 * Format type: Accordion item.
802
 *
803
 * @param $element The field group form element.
804
 * @param $group The Field group object prepared for pre_render.
805
 * @param $form The root element or form.
806
 */
807
function field_group_pre_render_accordion_item(&$element, $group, &$form) {
808

    
809
  $element += array(
810
    '#type' => 'markup',
811
    '#prefix' => '<h3 class="field-group-format-toggler ' . $group->format_type . ($group->collapsed ? '' : ' field-group-accordion-active') . '"><a href="#">' . check_plain(t($group->label)) . '</a></h3>
812
    <div class="field-group-format-wrapper ' . $group->classes . '">',
813
    '#suffix' => '</div>',
814
    //'#attributes' => array('class' => array($group->format_type)),
815
  );
816
  if (!empty($group->description)) {
817
    $element['#prefix'] .= '<div class="description">' . $group->description . '</div>';
818
  }
819

    
820
}
821

    
822
/**
823
 * Implements field_group_pre_render_<format-type>.
824
 * Format type: Horizontal tabs group.
825
 *
826
 * @param $element The field group form element.
827
 * @param $group The Field group object prepared for pre_render.
828
 * @param $form The root element or form.
829
 */
830
function field_group_pre_render_htabs(&$element, $group, &$form) {
831

    
832
  $element += array(
833
    '#type' => 'horizontal_tabs',
834
    '#title' => check_plain(t($group->label)),
835
    '#theme_wrappers' => array('horizontal_tabs'),
836
    '#prefix' => '<div class="field-group-' . $group->format_type . '-wrapper ' . $group->classes . '">',
837
    '#suffix' => '</div>',
838
  );
839

    
840
  // By default vertical_tabs don't have titles but you can override it in the theme.
841
  if (!empty($group->label)) {
842
    $element['#title'] = check_plain($group->label);
843
  }
844

    
845
  // Only add form.js on forms.
846
  if (!empty($form['#type']) && $form['#type'] == 'form') {
847
    $element['#attached']['js'][] = 'misc/form.js';
848
  }
849

    
850
  $element['#attached']['library'][] = array('field_group', 'horizontal-tabs');
851
}
852

    
853
/**
854
 * Implements field_group_pre_render_<format-type>.
855
 * Format type: Horizontal tab.
856
 *
857
 * @param $element The field group form element.
858
 * @param $group The Field group object prepared for pre_render.
859
 * @param $form The root element or form.
860
 */
861
function field_group_pre_render_htab(&$element, $group, &$form) {
862

    
863
  $element += array(
864
    '#type' => 'fieldset',
865
    '#title' => check_plain(t($group->label)),
866
    '#collapsible' => $group->collapsible,
867
    '#collapsed' => $group->collapsed,
868
    '#attributes' => array('class' => explode(" ", $group->classes)),
869
    '#group' => $group->parent_name,
870
    // very important. Cannot be added on the form!
871
    '#parents' => array($group->parent_name),
872
    '#description' => $group->description,
873
  );
874

    
875
}
876

    
877
/**
878
 * Implements field_group_pre_render_<format-type>.
879
 * Format type: Multipage group.
880
 *
881
 * @param $element The field group form element.
882
 * @param $group The Field group object prepared for pre_render.
883
 * @param $form The root element or form.
884
 */
885
function field_group_pre_render_multipage_group(&$element, &$group, &$form) {
886

    
887
  $multipage_element = array(
888
    '#type' => 'multipage',
889
    '#theme_wrappers' => array('multipage'),
890
    '#prefix' => '<div class="field-group-' . $group->format_type . '-wrapper ' . $group->classes . '">',
891
    '#suffix' => '</div>',
892
  );
893

    
894
  $element += $multipage_element;
895

    
896
  $move_additional = isset($group->format_settings['instance_settings']['move_additional']) ? ($group->format_settings['instance_settings']['move_additional'] && isset($form['additional_settings'])) : isset($form['additional_settings']);
897
  $move_button = isset($group->format_settings['instance_settings']['move_button']) ? $group->format_settings['instance_settings']['move_button'] : 0;
898

    
899
  drupal_add_js(array(
900
    'field_group' => array(
901
      'multipage_move_submit' => (bool) $move_button,
902
      'multipage_move_additional' => (bool) $move_additional
903
    )
904
  ), 'setting');
905

    
906
}
907

    
908
/**
909
 * Implements field_group_pre_render_<format-type>.
910
 * Format type: Multipage.
911
 *
912
 * @param $element The field group form element.
913
 * @param $group The Field group object prepared for pre_render.
914
 * @param $form The root element or form.
915
 */
916
function field_group_pre_render_multipage(&$element, $group, &$form) {
917

    
918
  $group->classes .= $group->format_settings['formatter'] == 'start' ? ' multipage-open' : ' multipage-closed';
919
  $element += array(
920
    '#type' => 'multipage_pane',
921
    '#title' => check_plain(t($group->label)),
922
    '#collapsible' => $group->collapsible,
923
    '#collapsed' => $group->collapsed,
924
    '#attributes' => array('class' => explode(" ", $group->classes)),
925
    '#group' => $group->parent_name,
926
    '#group_object' => $group,
927
    '#parent_group_object' => $form['#groups'][$group->parent_name],
928
    // very important. Cannot be added on the form!
929
    '#parents' => array($group->parent_name),
930
    '#description' => $group->description,
931
  );
932

    
933
  $element['#attached']['library'][] = array('field_group', 'multipage');
934
}
935

    
936
/**
937
 * Implements field_group_pre_render_<format-type>.
938
 * Format type: Vertical tabs wrapper.
939
 *
940
 * @param $element The field group form element.
941
 * @param $group The Field group object prepared for pre_render.
942
 * @param $form The root element or form.
943
 */
944
function field_group_pre_render_tabs(&$element, $group, &$form) {
945

    
946
  $element += array(
947
    '#type' => 'vertical_tabs',
948
    '#theme_wrappers' => array('vertical_tabs'),
949
    '#prefix' => '<div class="field-group-' . $group->format_type . '-wrapper ' . $group->classes . '">',
950
    '#suffix' => '</div>',
951
  );
952

    
953
  // By default vertical_tabs don't have titles but you can override it in the theme.
954
  if (!empty($group->label)) {
955
    $element['#title'] = check_plain($group->label);
956
  }
957

    
958
  $element[$group->group_name . '__active_tab'] = array(
959
    '#type' => 'hidden',
960
    '#default_value' => '',
961
    '#attributes' => array('class' => array('vertical-tabs-active-tab')),
962
  );
963

    
964
  $element['#attached']['library'][] = array('system', 'drupal.collapse');
965
}
966

    
967
/**
968
 * Implements field_group_pre_render_<format-type>.
969
 * Format type: Vertical tab.
970
 *
971
 * @param $element The field group form element.
972
 * @param $group The Field group object prepared for pre_render.
973
 * @param $form The root element or form.
974
 */
975
function field_group_pre_render_tab(&$element, $group, &$form) {
976

    
977
  $view_mode = isset($form['#view_mode']) ? $form['#view_mode'] : 'form';
978

    
979
  // Could be it never runs through htab.
980
  $form['#attached']['js'][] = array(
981
    'data' => array('field_group' => array('tabs' => $view_mode)),
982
    'type' => 'setting',
983
  );
984

    
985
  $add = array(
986
    '#type' => 'fieldset',
987
    '#id' => 'edit-' . $group->group_name,
988
    '#title' => check_plain(t($group->label)),
989
    '#collapsible' => $group->collapsible,
990
    '#collapsed' => $group->collapsed,
991
    '#attributes' => array('class' => explode(" ", $group->classes)),
992
    '#description' => $group->description,
993
  );
994

    
995
  // Front-end and back-end on configuration will lead
996
  // to vertical tabs nested in a separate vertical group.
997
  if ($view_mode != 'form') {
998
    $add['#group'] = empty($group->parent_name) ? 'additional_settings' : $group->parent_name;
999
    $add['#parents'] = array($add['#group']);
1000
    $element += $add;
1001
  }
1002
  // Form fieldgroups which are nested into a vertical tab group
1003
  // are handled a little different.
1004
  elseif (!empty($group->parent_name)) {
1005
    $add['#group'] = $group->parent_name;
1006
    $element += $add;
1007
  }
1008
  // Forms "can" have additional settins. We'll try to locate it first, if not
1009
  // exists, field_group will create a parallel additional settings entry.
1010
  else {
1011
    // Create the fieldgroup element.
1012
    $add['#parents'] = array('additional_settings');
1013
    $add['#group'] = 'additional_settings';
1014
    $add['#weight'] = -30 + $group->weight; // hardcoded to bring our extra additional vtabs on top.
1015

    
1016
    // Check if the additional_settings exist for this type of form.
1017
    if (isset($form['additional_settings']['group']['#groups']['additional_settings'])) {
1018

    
1019
      // Merge fieldgroups with the core additional settings.
1020
      $form['additional_settings']['group']['#groups']['additional_settings'][$group->group_name] = $add;
1021
      $form['additional_settings']['group']['#groups'][$group->group_name] = array('#group_exists' => TRUE);
1022
      // Nest the fields inside the appropriate structure.
1023
      foreach (element_children($element) as $fieldname) {
1024
        $form['additional_settings']['group']['#groups']['additional_settings'][$group->group_name][$fieldname] = &$element[$fieldname];
1025
        unset($element[$fieldname]);
1026
      }
1027
    }
1028
    // Assumption the wrapper is in the root. This could be done by field_group itself
1029
    // in previous loop of tabs in same wrapper or even some other contrib / custom module.
1030
    else {
1031
      if (!isset($form['additional_settings']['#type'])) {
1032
        $form['additional_settings'] = array(
1033
          '#type' => 'vertical_tabs',
1034
          '#weight' => $group->weight,
1035
          '#theme_wrappers' => array('vertical_tabs'),
1036
          '#prefix' => '<div class="field-group-' . $group->format_type . '-wrapper">',
1037
          '#suffix' => '</div>',
1038
        );
1039
        $form['#attached']['library'][] = array('system', 'drupal.collapse');
1040
      }
1041
      $form['additional_settings'][$group->group_name] = $add;
1042
      // Nest the fields inside the appropriate structure.
1043
      foreach (element_children($element) as $fieldname) {
1044
        $form['additional_settings'][$group->group_name][$fieldname] = &$element[$fieldname];
1045
        unset($element[$fieldname]);
1046
      }
1047
    }
1048
  }
1049

    
1050
}
1051

    
1052
/**
1053
 * Implements hook_field_group_build_pre_render_alter().
1054
 * @param Array $elements by address.
1055
 */
1056
function field_group_field_group_build_pre_render_alter(& $element) {
1057

    
1058
  // Someone is doing a node view, in a node view. Reset content.
1059
  // TODO Check if this breaks something else.
1060
  if (isset($element['#node']->content) && count($element['#node']->content) > 0) {
1061
    $element['#node']->content = array();
1062
  }
1063

    
1064
  $display = isset($element['#view_mode']);
1065
  $groups = array_keys($element['#groups']);
1066

    
1067
  // Dish the fieldgroups with no fields for non-forms.
1068
  if ($display) {
1069
    field_group_remove_empty_display_groups($element, $groups);
1070
  }
1071
  else {
1072
    // Fix the problem on forms with additional settings.
1073
    field_group_remove_empty_form_groups('form', $element, $groups, $element['#groups'], $element['#entity_type']);
1074
  }
1075

    
1076
  // Add the default field_group javascript and stylesheet.
1077
  $element['#attached']['js'][] = drupal_get_path('module', 'field_group') . '/field_group.js';
1078
  $element['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.css';
1079

    
1080
  // Move additional settings to the last multipage pane if configured that way.
1081
  // Note that multipages MUST be in the root of the form.
1082
  foreach (element_children($element) as $name) {
1083
    if (isset($element[$name]['#type']) && $element[$name]['#type'] == 'multipage' && isset($element['additional_settings'])) {
1084
      $parent_group = $element['#groups'][$name];
1085
      $move_additional = isset($parent_group->format_settings['instance_settings']['move_additional']) ? $parent_group->format_settings['instance_settings']['move_additional'] : 1;
1086
      $last_pane = NULL;
1087
      foreach (element_children($element[$name], TRUE) as $pane) {
1088
        $last_pane = $pane;
1089
      }
1090
      $element[$name][$last_pane]['additional_settings'] = $element['additional_settings'];
1091
      unset($element['additional_settings']);
1092
    }
1093
  }
1094

    
1095
}
1096

    
1097
/**
1098
 * Remove empty groups on forms.
1099
 *
1100
 * @param String $parent_name
1101
 *   The name of the element.
1102
 * @param array $element
1103
 *   The element to check the empty state.
1104
 * @param array $groups
1105
 *   Array of group objects.
1106
 */
1107
function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) {
1108

    
1109
  $exceptions = array('user__account', 'comment__author');
1110

    
1111
  $children = element_children($element);
1112

    
1113
  $hasChildren = FALSE;
1114
  if (count($children)) {
1115
    foreach ($children as $childname) {
1116
      if (in_array($childname, $groups)) {
1117
        field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity);
1118
      }
1119
      $exception = $entity . '__' . $childname;
1120
      $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || in_array($exception, $exceptions));
1121
    }
1122
  }
1123

    
1124
  if (!$hasChildren) {
1125

    
1126
    // Remove empty elements from the #groups.
1127
    if (empty($element) && isset($form_groups[$name]) && !is_array($form_groups[$name])) {
1128
      foreach ($form_groups as $group_name => $group) {
1129
        if (isset($group->children)) {
1130
          $group_children = array_flip($group->children);
1131
          if (isset($group_children[$name])) {
1132
            unset($form_groups[$group_name]->children[$group_children[$name]]);
1133
          }
1134
        }
1135
      }
1136
    }
1137

    
1138
    $element['#access'] = FALSE;
1139

    
1140
  }
1141

    
1142
}
1143

    
1144
/**
1145
 * Remove empty groups on entity display.
1146
 * @param array $element
1147
 *   The element to check the empty state.
1148
 * @param array $groups
1149
 *   Array of group objects.
1150
 */
1151
function field_group_remove_empty_display_groups(& $element, $groups) {
1152

    
1153
  $empty_child = TRUE;
1154
  $empty_group = TRUE;
1155

    
1156
  // Loop through the children for current element.
1157
  foreach (element_children($element) as $name) {
1158

    
1159
    // Descend if the child is a group.
1160
    if (in_array($name, $groups)) {
1161
      $empty_child = field_group_remove_empty_display_groups($element[$name], $groups);
1162
      if (!$empty_child) {
1163
        $empty_group = FALSE;
1164
      }
1165
    }
1166
    // Child is a field, the element is not empty and access is set to true (or empty).
1167
    elseif (!empty($element[$name]) && (!isset($element[$name]['#access']) || $element[$name]['#access'])) {
1168
      $empty_group = FALSE;
1169
    }
1170

    
1171
  }
1172

    
1173
  // Reset an empty group.
1174
  if ($empty_group) {
1175
    $element = NULL;
1176
  }
1177

    
1178
  return $empty_group;
1179

    
1180
}
1181

    
1182
/**
1183
 * Implements hook_field_group_format_summary().
1184
 */
1185
function field_group_field_group_format_summary($group) {
1186

    
1187
  $group_form = module_invoke_all('field_group_format_settings', $group);
1188

    
1189
  $output = '';
1190
  if (isset($group->format_settings['formatter'])) {
1191
    $output .= '<strong>' . $group->format_type . '</strong> ' . $group->format_settings['formatter'] . '';
1192
  }
1193
  if (isset($group->format_settings['instance_settings'])) {
1194
    $last = end($group->format_settings['instance_settings']);
1195
    $output .= '<br />';
1196
    foreach ($group->format_settings['instance_settings'] as $key => $value) {
1197
      if (empty($value)) {
1198
        continue;
1199
      }
1200

    
1201
      $output .= '<strong>' . $key . '</strong> ';
1202

    
1203
      if (isset($group_form['instance_settings'], $group_form['instance_settings'][$key]['#options'])) {
1204
        if (is_array($value)) {
1205
          $value = implode(array_filter($value), ', ');
1206
        }
1207
        else {
1208
          $value = $group_form['instance_settings'][$key]['#options'][$value];
1209
        }
1210
      }
1211

    
1212
      // Shorten the string.
1213
      if (drupal_strlen($value) > 38) {
1214
        $value = truncate_utf8($value, 50, TRUE, TRUE);
1215
      }
1216
      // If still numeric, handle it as yes or no.
1217
      elseif (is_numeric($value)) {
1218
        $value = $value == '1' ? t('yes') : t('no');
1219
      }
1220
      $output .= check_plain($value);
1221
      $output .= $last == $value ? ' ' : '<br />';
1222
    }
1223
  }
1224
  return $output;
1225
}
1226

    
1227
/**
1228
 * Implements hook_element_info().
1229
 */
1230
function field_group_element_info() {
1231
  $types['horizontal_tabs'] = array(
1232
    '#theme_wrappers' => array('horizontal_tabs'),
1233
    '#default_tab' => '',
1234
    '#process' => array('form_process_horizontal_tabs'),
1235
  );
1236
  $types['multipage'] = array(
1237
    '#theme_wrappers' => array('multipage'),
1238
    '#default_tab' => '',
1239
    '#process' => array('form_process_multipage'),
1240
  );
1241
  $types['multipage_pane'] = array(
1242
    '#value' => NULL,
1243
    '#process' => array('form_process_fieldset', 'ajax_process_form'),
1244
    '#pre_render' => array('form_pre_render_fieldset'),
1245
    '#theme_wrappers' => array('multipage_pane'),
1246
  );
1247
  return $types;
1248
}
1249

    
1250
/**
1251
 * Implements hook_library().
1252
 */
1253
function field_group_library() {
1254

    
1255
  $path = drupal_get_path('module', 'field_group');
1256
  // Horizontal Tabs.
1257
  $libraries['horizontal-tabs'] = array(
1258
    'title' => 'Horizontal Tabs',
1259
    'website' => 'http://drupal.org/node/323112',
1260
    'version' => '1.0',
1261
    'js' => array(
1262
      $path . '/horizontal-tabs/horizontal-tabs.js' => array(),
1263
    ),
1264
    'css' => array(
1265
      $path . '/horizontal-tabs/horizontal-tabs.css' => array(),
1266
    ),
1267
  );
1268
  // Multipage Tabs.
1269
  $libraries['multipage'] = array(
1270
    'title' => 'Multipage',
1271
    'website' => 'http://drupal.org/node/323112',
1272
    'version' => '1.0',
1273
    'js' => array(
1274
      $path . '/multipage/multipage.js' => array(),
1275
    ),
1276
    'css' => array(
1277
      $path . '/multipage/multipage.css' => array(),
1278
    ),
1279
  );
1280

    
1281
  return $libraries;
1282
}
1283

    
1284
/**
1285
 * Implements hook_field_extra_fields().
1286
 */
1287
function field_group_field_extra_fields() {
1288
  $extra = array();
1289

    
1290
  $extra['user']['user'] = array('form' => array());
1291

    
1292
  // User picture field to integrate with user module.
1293
  if (variable_get('user_pictures', 0)) {
1294
    $extra['user']['user']['form']['picture'] = array(
1295
      'label' => t('Picture'),
1296
      'description' => t('User picture'),
1297
      'weight' => 5,
1298
    );
1299
  }
1300

    
1301
  // Field to itegrate with overlay module.
1302
  if (module_exists('overlay')) {
1303
    $extra['user']['user']['form']['overlay_control'] = array(
1304
      'label' => t('Administrative overlay'),
1305
      'description' => t('Administrative overlay'),
1306
      'weight' => 5,
1307
    );
1308
  }
1309

    
1310
  // Field to itegrate with contact module.
1311
  if (module_exists('contact')) {
1312
    $extra['user']['user']['form']['contact'] = array(
1313
      'label' => t('Contact'),
1314
      'description' => t('Contact user element'),
1315
     'weight' => 5,
1316
    );
1317
  }
1318

    
1319
  // Field to integrate with the locale module.
1320
  if (module_exists('locale')) {
1321
    $extra['user']['user']['form']['locale'] = array(
1322
      'label' => t('Language settings'),
1323
      'description' => t('Language settings for the user account.'),
1324
      'weight' => 5,
1325
    );
1326
  }
1327

    
1328
  // Field to integrate with the wysiwyg module on user settings.
1329
  if (module_exists('wysiwyg')) {
1330
    $extra['user']['user']['form']['wysiwyg'] = array(
1331
      'label' => t('Wysiwyg status'),
1332
      'description' => t('Text formats enabled for rich-text editing'),
1333
      'weight' => 5,
1334
    );
1335
  }
1336

    
1337
  return $extra;
1338
}
1339

    
1340
/**
1341
 * Implements hook_field_attach_rename_bundle().
1342
 */
1343
function field_group_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
1344
  db_query('UPDATE {field_group} SET bundle = :bundle WHERE bundle = :old_bundle AND entity_type = :entity_type', array(
1345
    ':bundle' => $bundle_new,
1346
    ':old_bundle' => $bundle_old,
1347
    ':entity_type' => $entity_type,
1348
  ));
1349
}
1350

    
1351
/**
1352
 * Creates a group formatted as horizontal tabs.
1353
 * This function will never be callable from within field_group rendering. Other
1354
 * modules using #type horizontal_tabs will have the benefit of this processor.
1355
 *
1356
 * @param $element
1357
 *   An associative array containing the properties and children of the
1358
 *   fieldset.
1359
 * @param $form_state
1360
 *   The $form_state array for the form this horizontal tab widget belongs to.
1361
 * @return
1362
 *   The processed element.
1363
 */
1364
function form_process_horizontal_tabs($element, &$form_state) {
1365
  // Inject a new fieldset as child, so that form_process_fieldset() processes
1366
  // this fieldset like any other fieldset.
1367
  $element['group'] = array(
1368
    '#type' => 'fieldset',
1369
    '#theme_wrappers' => array(),
1370
    '#parents' => $element['#parents'],
1371
  );
1372

    
1373
  // The JavaScript stores the currently selected tab in this hidden
1374
  // field so that the active tab can be restored the next time the
1375
  // form is rendered, e.g. on preview pages or when form validation
1376
  // fails.
1377
  $name = implode('__', $element['#parents']);
1378
  if (isset($form_state['values'][$name . '__active_tab'])) {
1379
    $element['#default_tab'] = $form_state['values'][$name . '__active_tab'];
1380
  }
1381
  $element[$name . '__active_tab'] = array(
1382
    '#type' => 'hidden',
1383
    '#default_value' => $element['#default_tab'],
1384
    '#attributes' => array('class' => array('horizontal-tabs-active-tab')),
1385
  );
1386

    
1387
  return $element;
1388
}
1389

    
1390
/**
1391
 * Returns HTML for an element's children fieldsets as horizontal tabs.
1392
 *
1393
 * @param $variables
1394
 *   An associative array containing:
1395
 *   - element: An associative array containing the properties and children of the
1396
 *     fieldset. Properties used: #children.
1397
 *
1398
 * @ingroup themeable
1399
 */
1400
function theme_horizontal_tabs($variables) {
1401
  $element = $variables['element'];
1402
  // Add required JavaScript and Stylesheet.
1403
  $element['#attached']['library'][] = array('field_group', 'horizontal-tabs');
1404

    
1405
  $output = '<h2 class="element-invisible">' . (!empty($element['#title']) ? $element['#title'] : t('Horizontal Tabs')) . '</h2>';
1406
  $output .= '<div class="horizontal-tabs-panes">' . $element['#children'] . '</div>';
1407

    
1408
  return $output;
1409
}
1410

    
1411
/**
1412
 * Creates a group formatted as multipage.
1413
 * This function will never be callable from within field_group rendering. Other
1414
 * modules using #type multipage will have the benefit of this processor.
1415
 *
1416
 * @param $element
1417
 *   An associative array containing the properties and children of the
1418
 *   fieldset.
1419
 * @param $form_state
1420
 *   The $form_state array for the form this multipage tab widget belongs to.
1421
 * @return
1422
 *   The processed element.
1423
 */
1424
function form_process_multipage($element, &$form_state) {
1425
  // Inject a new fieldset as child, so that form_process_fieldset() processes
1426
  // this fieldset like any other fieldset.
1427
  $element['group'] = array(
1428
    '#type' => 'fieldset',
1429
    '#theme_wrappers' => array(),
1430
    '#parents' => $element['#parents'],
1431
  );
1432

    
1433
  // The JavaScript stores the currently selected tab in this hidden
1434
  // field so that the active control can be restored the next time the
1435
  // form is rendered, e.g. on preview pages or when form validation
1436
  // fails.
1437
  $name = implode('__', $element['#parents']);
1438
  if (isset($form_state['values'][$name . '__active_control'])) {
1439
    $element['#default_tab'] = $form_state['values'][$name . '__active_control'];
1440
  }
1441
  $element[$name . '__active_control'] = array(
1442
    '#type' => 'hidden',
1443
    '#default_value' => $element['#default_control'],
1444
    '#attributes' => array('class' => array('multipage-active-control')),
1445
  );
1446
  
1447
  return $element;
1448
}
1449

    
1450
/**
1451
 * Returns HTML for an element's children fieldsets as multipage.
1452
 *
1453
 * @param $variables
1454
 *   An associative array containing:
1455
 *   - element: An associative array containing the properties and children of the
1456
 *     fieldset. Properties used: #children.
1457
 *
1458
 * @ingroup themeable
1459
 */
1460
function theme_multipage($variables) {
1461
  $element = $variables['element'];
1462
  // Add required JavaScript and Stylesheet.
1463
  $element['#attached']['library'][] = array('field_group', 'multipage');
1464

    
1465
  $output = '<h2 class="element-invisible">' . (!empty($element['#title']) ? $element['#title'] : t('Multipage')) . '</h2>';
1466

    
1467
  $output .= '<div class="multipage-panes">';
1468
  $output .= $element['#children'];
1469
  $output .= '</div>';
1470

    
1471
  return $output;
1472
}
1473

    
1474
/**
1475
 * Returns HTML for multipage pane.
1476
 *
1477
 * @param $variables
1478
 *   An associative array containing:
1479
 *   - element: An associative array containing the properties and children of the
1480
 *     fieldset. Properties used: #children.
1481
 *
1482
 * @ingroup themeable
1483
 */
1484
function theme_multipage_pane($variables) {
1485

    
1486
  $element = $variables['element'];
1487
  $group = $variables['element']['#group_object'];
1488
  $parent_group = $variables['element']['#parent_group_object'];
1489

    
1490
  static $multipages;
1491
  if (!isset($multipages[$group->parent_name])) {
1492
    $multipages = array($group->parent_name => 0);
1493
  }
1494
  $multipages[$parent_group->group_name]++;
1495

    
1496
  // Create a page title from the label.
1497
  $page_header = isset($parent_group->format_settings['instance_settings']['page_header']) ? $parent_group->format_settings['instance_settings']['page_header'] : 3;
1498
  switch ($page_header) {
1499
    case 1:
1500
      $title = $element['#title'];
1501
      break;
1502
    case 2:
1503
      $title = t('Step %count of %total', array('%count' => $multipages[$parent_group->group_name], '%total' => count($parent_group->children)));
1504
      break;
1505
    case 3:
1506
      $title = t('Step %count of %total !label', array('%count' => $multipages[$parent_group->group_name], '%total' => count($parent_group->children), '!label' => $element['#title']));
1507
      break;
1508
    case 0:
1509
    default:
1510
      $title = '';
1511
      break;
1512
  }
1513

    
1514
  element_set_attributes($element, array('id'));
1515
  _form_set_class($element, array('form-wrapper'));
1516

    
1517
  $output = '<div' . drupal_attributes($element['#attributes']) . '>';
1518
  if (!empty($element['#title'])) {
1519
    // Always wrap fieldset legends in a SPAN for CSS positioning.
1520
    $output .= '<h2 class="multipage-pane-title"><span>' . $title . '</span></h2>';
1521
  }
1522
  $output .= '<div class="fieldset-wrapper multipage-pane-wrapper">';
1523
  if (!empty($element['#description'])) {
1524
    $output .= '<div class="fieldset-description">' . $element['#description'] . '</div>';
1525
  }
1526
  $output .= $element['#children'];
1527
  if (isset($element['#value'])) {
1528
    $output .= $element['#value'];
1529
  }
1530

    
1531
  // Add a page counter if needed.
1532
  // counter array(0 => t('No'), 1 => t('Format 1 / 10'), 2 => t('The count number only'));
1533
  $page_counter_format = isset($parent_group->format_settings['instance_settings']['page_counter']) ? $parent_group->format_settings['instance_settings']['page_counter'] : 1;
1534
  $multipage_element['#page_counter_rendered'] = '';
1535
  if ($page_counter_format == 1) {
1536
    $output .= t('<span class="multipage-counter">%count / %total</span>', array('%count' => $multipages[$parent_group->group_name], '%total' => count($parent_group->children)));
1537
  }
1538
  elseif ($page_counter_format == 2) {
1539
    $output .=  t('<span class="multipage-counter">%count</span>', array('%count' => $multipages[$parent_group->group_name]));
1540
  }
1541

    
1542
  $output .= '</div>';
1543
  $output .= "</div>\n";
1544

    
1545
  return $output;
1546

    
1547
}
1548

    
1549
/**
1550
 * Get all groups.
1551
 *
1552
 * @param $entity_type
1553
 *   The name of the entity.
1554
 * @param $bundle
1555
 *   The name of the bundle.
1556
 * @param $view_mode
1557
 *   The view mode.
1558
 * @param $reset.
1559
 *   Whether to reset the cache or not.
1560
 */
1561
function field_group_info_groups($entity_type = NULL, $bundle = NULL, $view_mode = NULL, $reset = FALSE) {
1562
  static $groups = FALSE;
1563

    
1564
  if (!$groups || $reset) {
1565
    if (!$reset && $cached = cache_get('field_groups', 'cache_field')) {
1566
      $groups = $cached->data;
1567
    }
1568
    else {
1569
      drupal_static_reset('ctools_export_load_object');
1570
      drupal_static_reset('ctools_export_load_object_all');
1571
      $groups = field_group_read_groups();
1572
      cache_set('field_groups', $groups, 'cache_field');
1573
    }
1574
  }
1575

    
1576
  if (!isset($entity_type)) {
1577
    return $groups;
1578
  }
1579
  elseif (!isset($bundle) && isset($groups[$entity_type])) {
1580
    return $groups[$entity_type];
1581
  }
1582
  elseif (!isset($view_mode) && isset($groups[$entity_type][$bundle])) {
1583
    return $groups[$entity_type][$bundle];
1584
  }
1585
  elseif (isset($groups[$entity_type][$bundle][$view_mode])) {
1586
    return $groups[$entity_type][$bundle][$view_mode];
1587
  }
1588
  return array();
1589
}
1590

    
1591
/**
1592
 * Read all groups.
1593
 *
1594
 * @param array $conditions
1595
 *   Parameters for the query, as elements of the $conditions array.
1596
 *   'entity_type' The name of the entity type.
1597
 *   'bundle' The name of the bundle.
1598
 *   'mode' The view mode.
1599
 *
1600
 * @param boolean $enabled
1601
 *   Return enabled or disabled groups.
1602
 *
1603
 * @return array
1604
 *   Array of groups.
1605
 */
1606
function field_group_read_groups($conditions = array(), $enabled = TRUE) {
1607

    
1608
  $groups = array();
1609
  ctools_include('export');
1610

    
1611
  if (empty($conditions)) {
1612
    $records = ctools_export_load_object('field_group');
1613
  }
1614
  else {
1615
    $records = ctools_export_load_object('field_group', 'conditions', $conditions);
1616
  }
1617

    
1618
  foreach ($records as $group) {
1619

    
1620
    // Return only enabled groups.
1621
    if ($enabled && isset($group->disabled) && $group->disabled) {
1622
      continue;
1623
    }
1624
    // Return only disabled groups.
1625
    elseif (!$enabled && (!isset($group->disabled) || !$group->disabled)) {
1626
      continue;
1627
    }
1628

    
1629
    $groups[$group->entity_type][$group->bundle][$group->mode][$group->group_name] = field_group_unpack($group);
1630

    
1631
  }
1632
  drupal_alter('field_group_info', $groups);
1633
  return $groups;
1634

    
1635
}
1636

    
1637
/**
1638
 * Utility function to recreate identifiers.
1639
 */
1640
function _field_group_recreate_identifiers() {
1641

    
1642
  // Migrate the field groups so they have a unique identifier.
1643
  $result = db_select('field_group', 'fg')
1644
    ->fields('fg')
1645
    ->execute();
1646
  $rows = array();
1647
  foreach($result as $row) {
1648
    $row->identifier = $row->group_name . '|' . $row->entity_type . '|' . $row->bundle . '|' . $row->mode;
1649
    $row->data = unserialize($row->data);
1650
    $rows[] = $row;
1651
  }
1652
  foreach ($rows as $row) {
1653
    drupal_write_record('field_group', $row, array('id'));
1654
  }
1655

    
1656
}
1657

    
1658
/**
1659
 * Checks if a field_group exists in required context.
1660
 *
1661
 * @param String $group_name
1662
 *   The name of the group.
1663
 * @param String $entity_type
1664
 *   The name of the entity.
1665
 * @param String $bundle
1666
 *   The bundle for the entity.
1667
 * @param String $mode
1668
 *   The view mode context the group will be rendered.
1669
 */
1670
function field_group_exists($group_name, $entity_type, $bundle, $mode) {
1671
  $groups = field_group_read_groups();
1672
  return !empty($groups[$entity_type][$bundle][$mode][$group_name]);
1673
}
1674

    
1675
/**
1676
 * Unpacks a database row in a FieldGroup object.
1677
 * @param $packed_group
1678
 *   Database result object with stored group data.
1679
 * @return $group
1680
 *   Field group object.
1681
 */
1682
function field_group_unpack($packed_group) {
1683
  if (!isset($packed_group->data)) {
1684
    return $packed_group;
1685
  }
1686

    
1687
  // Extract unserialized data.
1688
  $group = clone $packed_group;
1689
  $data = $group->data;
1690
  unset($group->data);
1691
  $group->label = isset($data['label']) ? $data['label'] : '';
1692
  $group->weight = isset($data['weight']) ? $data['weight'] : '';
1693
  $group->children = isset($data['children']) ? $data['children'] : '';
1694
  $group->format_type = !empty($data['format_type']) ? $data['format_type'] : 'fieldset';
1695
  if (isset($data['format_settings'])) {
1696
    $group->format_settings = $data['format_settings'];
1697
  }
1698

    
1699
  return $group;
1700
}
1701

    
1702
/**
1703
 * Packs a FieldGroup object into a database row.
1704
 * @param $group
1705
 *   FieldGroup object.
1706
 * @return $record
1707
 *   Database row object, ready to be inserted/update
1708
 */
1709
function field_group_pack($group) {
1710

    
1711
  $record = clone $group;
1712
  $record->data = array(
1713
    'label' => $record->label,
1714
    'weight' => $record->weight,
1715
    'children' => $record->children,
1716
    'format_type' => !empty($record->format_type) ? $record->format_type : 'fieldset',
1717
  );
1718
  if (isset($record->format_settings)) {
1719
    $record->data['format_settings'] = $record->format_settings;
1720
  }
1721
  return $record;
1722
}
1723

    
1724
/**
1725
 * Delete a field group.
1726
 * This function is also called by ctools export when calls are
1727
 * made through ctools_export_crud_delete().
1728
 *
1729
 * @param $group
1730
 *   A group definition.
1731
 * @param $ctools_crud
1732
 *  Is this function called by the ctools crud delete.
1733
 */
1734
function field_group_group_export_delete($group, $ctools_crud = TRUE) {
1735

    
1736
  $query = db_delete('field_group');
1737

    
1738
  if (isset($group->identifier)) {
1739
    $query->condition('identifier', $group->identifier);
1740
    if (!$ctools_crud) {
1741
      ctools_export_crud_disable('field_group', $group->identifier);
1742
    }
1743
  }
1744
  elseif (isset($group->id)) {
1745
    $query->condition('id', $group->id);
1746
  }
1747

    
1748
  if (!empty($group->mode)) {
1749
    $query->condition('mode', $group->mode);
1750
  }
1751

    
1752
  $query->execute();
1753

    
1754
  cache_clear_all('field_groups', 'cache_field');
1755
  module_invoke_all('field_group_delete_field_group', $group);
1756

    
1757
}
1758

    
1759
/**
1760
 * field_group_group_save().
1761
 *
1762
 * Saves a group definition.
1763
 * This function is called by ctools export when calls are made
1764
 * through ctools_export_crud_save().
1765
 *
1766
 * @param $group
1767
 *   A group definition.
1768
 */
1769
function field_group_group_save(& $group) {
1770

    
1771
  // Prepare the record.
1772
  $object = field_group_pack($group);
1773

    
1774
  if (isset($object->export_type) && $object->export_type & EXPORT_IN_DATABASE) {
1775
    // Existing record.
1776
    $update = array('id');
1777
    module_invoke_all('field_group_update_field_group', $object);
1778
  }
1779
  else {
1780
    // New record.
1781
    $update = array();
1782
    $object->export_type = EXPORT_IN_DATABASE;
1783
    module_invoke_all('field_group_create_field_group', $object);
1784
  }
1785

    
1786
  return drupal_write_record('field_group', $object, $update);
1787

    
1788
}
1789

    
1790
/**
1791
 * Function to retrieve all format possibilities for the fieldgroups.
1792
 */
1793
function field_group_formatter_info($display_overview = FALSE) {
1794
  $cache = &drupal_static(__FUNCTION__, array());
1795
  if (empty($cache)) {
1796
    if ($cached = cache_get('field_group_formatter_info', 'cache_field')) {
1797
      $formatters = $cached->data;
1798
    }
1799
    else {
1800
      $formatters = array();
1801
      $formatters += module_invoke_all('field_group_formatter_info');
1802
      $hidden_region = array(
1803
        'label' => '<' . t('Hidden') . '>',
1804
        'description' => '',
1805
        'format_types' => array(),
1806
        'instance_settings' => array(),
1807
        'default_formatter' => '',
1808
      );
1809
      //$formatters['form']['hidden'] = $hidden_region;
1810
      $formatters['display']['hidden'] = $hidden_region;
1811
      cache_set('field_group_formatter_info', $formatters, 'cache_field');
1812
    }
1813
    $cache = $formatters;
1814
  }
1815
  return $cache;
1816
}
1817

    
1818
/**
1819
 * Attach groups to the (form) build.
1820
 *
1821
 * @param Array $element
1822
 *   The part of the form.
1823
 * @param String $view_mode
1824
 *   The mode for the build.
1825
 * @param Array $form_state
1826
 *   The optional form state when in view_mode = form context.
1827
 */
1828
function field_group_attach_groups(&$element, $view_mode, $form_state = array()) {
1829

    
1830
  $entity_type = $element['#entity_type'];
1831
  $bundle = $element['#bundle'];
1832

    
1833
  $element['#groups'] = field_group_info_groups($entity_type, $bundle, $view_mode);
1834
  $element['#fieldgroups'] = $element['#groups'];
1835

    
1836
  // Create a lookup array.
1837
  $group_children = array();
1838
  foreach ($element['#groups'] as $group_name => $group) {
1839
    foreach ($group->children as $child) {
1840
      $group_children[$child] = $group_name;
1841
    }
1842
  }
1843
  $element['#group_children'] = $group_children;
1844

    
1845
}
1846

    
1847
/**
1848
 * Pre render callback for rendering groups.
1849
 * @see field_group_field_attach_form
1850
 * @param $element Form that is beïng rendered.
1851
 */
1852
function field_group_form_pre_render(&$element) {
1853
  return field_group_build_entity_groups($element, 'form');
1854
}
1855

    
1856
/**
1857
 * Preprocess/ Pre-render callback.
1858
 *
1859
 * @see field_group_form_pre_render()
1860
 * @see field_group_theme_registry_alter
1861
 * @see field_group_fields_nest()
1862
 * @param $vars preprocess vars or form element
1863
 * @param $type The type of object beïng rendered
1864
 * @return $element Array with re-arranged fields in forms.
1865
 */
1866
function field_group_build_entity_groups(&$vars, $type) {
1867

    
1868
  if ($type == 'form') {
1869
    $element = &$vars;
1870
    $nest_vars = NULL;
1871
  }
1872
  else {
1873
    $element = &$vars['elements'];
1874
    $nest_vars = &$vars;
1875
  }
1876

    
1877
  // No groups on the entity.
1878
  if (empty($element['#fieldgroups'])) {
1879
    return $element;
1880
  }
1881

    
1882
  // Nest the fields in the corresponding field groups.
1883
  field_group_fields_nest($element, $nest_vars);
1884

    
1885
  // Allow others to alter the pre_rendered build.
1886
  drupal_alter('field_group_build_pre_render', $element);
1887

    
1888
  // Return the element on forms.
1889
  if ($type == 'form') {
1890
    return $element;
1891
  }
1892

    
1893
  // Put groups inside content if we are rendering an entity_view.
1894
  foreach ($element['#groups'] as $group) {
1895
    if (!empty($element[$group->group_name]) && $type != 'user_profile') {
1896
      $vars['content'][$group->group_name] = $element[$group->group_name];
1897
    }
1898
    elseif (!empty($element[$group->group_name])) {
1899
      $vars['user_profile'][$group->group_name] = $element[$group->group_name];
1900
    }
1901
  }
1902

    
1903
  // New css / js can be attached.
1904
  drupal_process_attached($element);
1905
}
1906

    
1907
/**
1908
 * Recursive function to nest fields in the field groups.
1909
 *
1910
 * This function will take out all the elements in the form and
1911
 * place them in the correct container element, a fieldgroup.
1912
 * The current group element in the loop is passed recursively so we can
1913
 * stash fields and groups in it while we go deeper in the array.
1914
 * @param Array $element
1915
 *   The current element to analyse for grouping.
1916
 * @param Array $vars
1917
 *   Rendering vars from the entity beïng viewed.
1918
 */
1919
function field_group_fields_nest(&$element, &$vars = NULL) {
1920

    
1921
  // Create all groups and keep a flat list of references to these groups.
1922
  $group_references = array();
1923
  foreach ($element['#fieldgroups'] as $group_name => $group) {
1924
    // Construct own weight, as some fields (for example preprocess fields) don't have weight set.
1925
    $element[$group_name] = array();
1926
    $group_references[$group_name] = &$element[$group_name];
1927
  }
1928

    
1929
  // Loop through all form children looking for those that are supposed to be
1930
  // in groups, and insert placeholder element for the new group field in the
1931
  // correct location within the form structure.
1932
  $element_clone = array();
1933
  foreach (element_children($element) as $child_name) {
1934
    $element_clone[$child_name] = $element[$child_name];
1935
    // If this element is in a group, create the placeholder element.
1936
    if (isset($element['#group_children'][$child_name])) {
1937
      $element_clone[$element['#group_children'][$child_name]] = array();
1938
    }
1939
  }
1940
  $element = array_merge($element_clone, $element);
1941

    
1942
  // Move all children to their parents. Use the flat list of references for
1943
  // direct access as we don't know where in the root_element hierarchy the
1944
  // parent currently is situated.
1945
  foreach ($element['#group_children'] as $child_name => $parent_name) {
1946

    
1947
    // Entity being viewed
1948
    if ($vars) {
1949
      // If not a group, check vars['content'] for empty field.
1950
      if (!isset($element['#groups'][$child_name]) && isset($vars['content'][$child_name])) {
1951
        $group_references[$parent_name][$child_name] = $vars['content'][$child_name];
1952
        unset($vars['content'][$child_name]);
1953
      }
1954
      elseif (!isset($element['#groups'][$child_name]) && isset($vars['user_profile'][$child_name])) {
1955
        $group_references[$parent_name][$child_name] = $vars['user_profile'][$child_name];
1956
        unset($vars['user_profile'][$child_name]);
1957
      }
1958
      // If this is a group, we have to use a reference to keep the reference
1959
      // list intact (but if it is a field we don't mind).
1960
      else {
1961
        $group_references[$parent_name][$child_name] = &$element[$child_name];
1962
        unset($element[$child_name]);
1963
      }
1964
    }
1965
    // Form being viewed
1966
    else {
1967

    
1968
      // Block denied fields (#access) before they are put in groups.
1969
      // Fields (not groups) that don't have children (like field_permissions) are removed
1970
      // in field_group_field_group_build_pre_render_alter.
1971
      if (isset($element[$child_name]) && (!isset($element[$child_name]['#access']) || $element[$child_name]['#access'])) {
1972
        // If this is a group, we have to use a reference to keep the reference
1973
        // list intact (but if it is a field we don't mind).
1974
        $group_references[$parent_name][$child_name] = &$element[$child_name];
1975
        $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight;
1976
      }
1977

    
1978
      // The child has been copied to its parent: remove it from the root element.
1979
      unset($element[$child_name]);
1980
    }
1981

    
1982
  }
1983

    
1984
  // Bring extra element wrappers to achieve a grouping of fields.
1985
  // This will mainly be prefix and suffix altering.
1986
  foreach ($element['#fieldgroups'] as $group_name => $group) {
1987
    field_group_pre_render($group_references[$group_name], $group, $element);
1988
  }
1989

    
1990
}
1991

    
1992
/**
1993
 * Function to pre render the field group element.
1994
 *
1995
 * @see field_group_fields_nest()
1996
 *
1997
 * @param $element Array of group element that needs to be created!
1998
 * @param $group Object with the group information.
1999
 * @param $form The form object itself.
2000
 */
2001
function field_group_pre_render(& $element, $group, & $form) {
2002

    
2003
  // Only run the pre_render function if the group has elements.
2004
  // $group->group_name
2005
  if ($element == array()) {
2006
    return;
2007
  }
2008

    
2009
  // Let modules define their wrapping element.
2010
  // Note that the group element has no properties, only elements.
2011
  foreach (module_implements('field_group_pre_render') as $module) {
2012
    $function = $module . '_field_group_pre_render';
2013
    if (function_exists($function)) {
2014
      // The intention here is to have the opportunity to alter the
2015
      // elements, as defined in hook_field_group_formatter_info.
2016
      // Note, implement $element by reference!
2017
      $function($element, $group, $form);
2018
    }
2019
  }
2020

    
2021
  // Allow others to alter the pre_render.
2022
  drupal_alter('field_group_pre_render', $element, $group, $form);
2023

    
2024
}
2025

    
2026
/**
2027
 * Hides field groups including children in a render array.
2028
 *
2029
 * @param array $element
2030
 *   A render array. Can be a form, node, user, ...
2031
 * @param array $group_names
2032
 *   An array of field group names that should be hidden.
2033
 */
2034
function field_group_hide_field_groups(&$element, $group_names) {
2035
  foreach ($group_names as $group_name) {
2036
    if (isset($element['#fieldgroups'][$group_name]) && isset($element['#group_children'])) {
2037
      // Hide the field group.
2038
      $element['#fieldgroups'][$group_name]->format_type = 'hidden';
2039
      // Hide the elements inside the field group.
2040
      $sub_groups = array();
2041
      foreach (array_keys($element['#group_children'], $group_name) as $field_name) {
2042
        if (isset($element['#fieldgroups'][$field_name])) {
2043
          $sub_groups[] = $field_name;
2044
        } else {
2045
          $element[$field_name]['#access'] = FALSE;
2046
        }
2047
      }
2048
      field_group_hide_field_groups($element, $sub_groups);
2049
    }
2050
  }
2051
}
2052

    
2053
/**
2054
 * Calculates html classes for a group.
2055
 */
2056
function _field_group_get_html_classes(&$group) {
2057

    
2058
  if (isset($group->format_settings['formatter'])) {
2059
    $group->collapsible = in_array($group->format_settings['formatter'], array('collapsible', 'collapsed'));
2060
    // Open or closed horizontal or vertical tabs will be collapsible by default.
2061
    if ($group->format_type == 'tab' || $group->format_type == 'htab') {
2062
      $group->collapsible = TRUE;
2063
    }
2064
    $group->collapsed = in_array($group->format_settings['formatter'], array('collapsed', 'closed'));
2065
  }
2066

    
2067
  $classes = new stdClass();
2068

    
2069
  // Prepare extra classes, required and optional ones.
2070
  $optional = array(str_replace('_', '-', $group->group_name));
2071
  $required = array();
2072
  if ($group->format_type == 'multipage') {
2073
    $required[] = 'field-group-' . $group->format_type;
2074
  }
2075
  else {
2076
    $optional[] = 'field-group-' . $group->format_type;
2077
  }
2078

    
2079
  if (isset($group->format_settings['formatter']) && $group->collapsible) {
2080
    $required[] = 'collapsible';
2081
    if ($group->collapsed) {
2082
      $required[] = 'collapsed';
2083
    }
2084
  }
2085

    
2086
  if (isset($group->format_settings['instance_settings'])) {
2087

    
2088
    // Add a required-fields class to trigger the js.
2089
    if (!empty($group->format_settings['instance_settings']['required_fields'])) {
2090
      $required[] = 'required-fields';
2091
    }
2092

    
2093
    // Add user selected classes.
2094
    if (!empty($group->format_settings['instance_settings']['classes'])) {
2095
      $required[] = check_plain($group->format_settings['instance_settings']['classes']);
2096
    }
2097

    
2098
    // Extra required classes for div.
2099
    if ($group->format_type == 'div') {
2100
      if ($group->format_settings['formatter'] != 'open') {
2101

    
2102
        $speed = isset($group->format_settings['instance_settings']['speed']) ? $group->format_settings['instance_settings']['speed'] : 'none';
2103
        $required[] = 'speed-' . $speed;
2104

    
2105
        $effect = isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : 'none';
2106
        $required[] = 'effect-' . $effect;
2107
      }
2108
    }
2109

    
2110
    // Extra required classes for accordions.
2111
    elseif ($group->format_type == 'accordion') {
2112
      $required[] = 'field-group-' . $group->format_type . '-wrapper';
2113
      $effect = isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : 'none';
2114
      $required[] = 'effect-' . $effect;
2115
    }
2116

    
2117
  }
2118

    
2119
  $classes->required = $required;
2120
  $classes->optional = $optional;
2121

    
2122
  return $classes;
2123
}
2124

    
2125
/**
2126
 * Get the default formatter settings for a given formatter and a mode.
2127
 */
2128
function _field_group_get_default_formatter_settings($format_type, $mode) {
2129

    
2130
  $field_group_types = field_group_formatter_info();
2131
  $display_mode = $mode == 'form' ? 'form' : 'display';
2132
  $formatter = $field_group_types[$display_mode][$format_type];
2133

    
2134
  return array(
2135
    'formatter' => isset($formatter['default_formatter']) ? $formatter['default_formatter'] : '',
2136
    'instance_settings' => $formatter['instance_settings']
2137
  );
2138
}
2139