Project

General

Profile

Paste
Download (24.6 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / views_bulk_operations / actions / modify.action.inc @ 9a28ac3f

1
<?php
2

    
3
/**
4
 * @file
5
 * VBO action to modify entity values (properties and fields).
6
 */
7

    
8
// Specifies that all available values should be shown to the user for editing.
9
define('VBO_MODIFY_ACTION_ALL', '_all_');
10

    
11
/**
12
 * Implements hook_action_info().
13
 */
14
function views_bulk_operations_modify_action_info() {
15
  return array(
16
    'views_bulk_operations_modify_action' => array(
17
      'type' => 'entity',
18
      'label' => t('Modify entity values'),
19
      'behavior' => array('changes_property'),
20
      // This action only works when invoked through VBO. That's why it's
21
      // declared as non-configurable to prevent it from being shown in the
22
      // "Create an advanced action" dropdown on admin/config/system/actions.
23
      'configurable' => FALSE,
24
      'vbo_configurable' => TRUE,
25
      'triggers' => array('any'),
26
    ),
27
  );
28
}
29

    
30
/**
31
 * Action function.
32
 *
33
 * Goes through new values and uses them to modify the passed entity by either
34
 * replacing the existing values, or appending to them (based on user input).
35
 */
36
function views_bulk_operations_modify_action($entity, $context) {
37
  list(,, $bundle_name) = entity_extract_ids($context['entity_type'], $entity);
38
  // Handle Field API fields.
39
  if (!empty($context['selected']['bundle_' . $bundle_name])) {
40
    // The pseudo entity is cloned so that changes to it don't get carried
41
    // over to the next execution.
42
    $pseudo_entity = clone $context['entities'][$bundle_name];
43
    foreach ($context['selected']['bundle_' . $bundle_name] as $key) {
44
      // Get this field's language. We can just pull it from the pseudo entity
45
      // as it was created using field_attach_form and entity_language so it's
46
      // already been figured out if this field is translatable or not and
47
      // applied the appropriate language code to the field.
48
      $language = key($pseudo_entity->{$key});
49
      // Replace any tokens that might exist in the field columns.
50
      foreach ($pseudo_entity->{$key}[$language] as $delta => &$item) {
51
        foreach ($item as $column => $value) {
52
          if (is_string($value)) {
53
            $item[$column] = token_replace($value, array($context['entity_type'] => $entity), array('sanitize' => FALSE));
54
          }
55
        }
56
      }
57

    
58
      if (in_array($key, $context['append']['bundle_' . $bundle_name]) && !empty($entity->{$key})) {
59
        $entity->{$key}[$language] = array_merge($entity->{$key}[$language], $pseudo_entity->{$key}[$language]);
60

    
61
        // Check if we breached cardinality, and notify the user.
62
        $field_info = field_info_field($key);
63
        $field_count = count($entity->{$key}[$language]);
64
        if ($field_info['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $field_count > $field_info['cardinality']) {
65
          $entity_label = entity_label($context['entity_type'], $entity);
66
          $warning = t('Tried to set !field_count values for field !field_name that supports a maximum of !cardinality.',
67
            array(
68
              '!field_count' => $field_count,
69
              '!field_name' => $field_info['field_name'],
70
              '!cardinality' => $field_info['cardinality'],
71
            ));
72
          drupal_set_message($warning, 'warning', FALSE);
73
        }
74

    
75
        // Prevent storing duplicate references.
76
        if (strpos($field_info['type'], 'reference') !== FALSE) {
77
          $entity->{$key}[$language] = array_unique($entity->{$key}[LANGUAGE_NONE], SORT_REGULAR);
78
        }
79
      }
80
      else {
81
        $entity->{$key}[$language] = $pseudo_entity->{$key}[$language];
82
      }
83
    }
84
  }
85

    
86
  // Handle properties.
87
  // Use the wrapper to set property values, since some properties need
88
  // additional massaging by their setter callbacks.
89
  // The wrapper will automatically modify $entity itself.
90
  $wrapper = entity_metadata_wrapper($context['entity_type'], $entity);
91
  // The default setting for 'revision' property (create new revisions) should
92
  // be respected for nodes. This requires some special treatment.
93
  if ($context['entity_type'] == 'node' && in_array('revision', variable_get('node_options_' . $bundle_name)) && !in_array('revision', $context['selected']['properties'])) {
94
    $wrapper->revision->set(1);
95
  }
96

    
97
  if (!empty($context['selected']['properties'])) {
98
    foreach ($context['selected']['properties'] as $key) {
99
      if (!$wrapper->{$key}->access('update')) {
100
        // No access.
101
        continue;
102
      }
103

    
104
      if (in_array($key, $context['append']['properties'])) {
105
        $old_values = $wrapper->{$key}->value();
106
        $wrapper->{$key}->set($context['properties'][$key]);
107
        $new_values = $wrapper->{$key}->value();
108
        $all_values = array_merge($old_values, $new_values);
109
        $wrapper->{$key}->set($all_values);
110
      }
111
      else {
112
        $value = $context['properties'][$key];
113
        if (is_string($value)) {
114
          $value = token_replace($value, array($context['entity_type'] => $entity), array('sanitize' => FALSE));
115
        }
116
        $wrapper->{$key}->set($value);
117
      }
118
    }
119
  }
120
}
121

    
122
/**
123
 * Action form function.
124
 *
125
 * Displays form elements for properties acquired through Entity Metadata
126
 * (hook_entity_property_info()), as well as field widgets for each
127
 * entity bundle, as provided by field_attach_form().
128
 */
129
function views_bulk_operations_modify_action_form($context, &$form_state) {
130
  // This action form uses admin-provided settings. If they were not set, pull
131
  // the defaults now.
132
  if (!isset($context['settings'])) {
133
    $context['settings'] = views_bulk_operations_modify_action_views_bulk_operations_form_options();
134
  }
135

    
136
  $form_state['entity_type'] = $entity_type = $context['entity_type'];
137
  // For Field API integration to work, a pseudo-entity is constructed for each
138
  // bundle that has fields available for editing.
139
  // The entities then get passed to Field API functions
140
  // (field_attach_form(), field_attach_form_validate(), field_attach_submit()),
141
  // and filled with form data.
142
  // After submit, the pseudo-entities get passed to the actual action
143
  // (views_bulk_operations_modify_action()) which copies the data from the
144
  // relevant pseudo-entity constructed here to the actual entity being
145
  // modified.
146
  $form_state['entities'] = array();
147

    
148
  $info = entity_get_info($entity_type);
149
  $properties = _views_bulk_operations_modify_action_get_properties($entity_type, $context['settings']['display_values']);
150
  $bundles = _views_bulk_operations_modify_action_get_bundles($entity_type, $context);
151

    
152
  $form['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/modify.action.css';
153
  $form['#tree'] = TRUE;
154

    
155
  if (!empty($properties)) {
156
    $form['properties'] = array(
157
      '#type' => 'fieldset',
158
      '#title' => t('Properties'),
159
    );
160
    $form['properties']['show_value'] = array(
161
      '#suffix' => '<div class="clearfix"></div>',
162
    );
163

    
164
    foreach ($properties as $key => $property) {
165
      $form['properties']['show_value'][$key] = array(
166
        '#type' => 'checkbox',
167
        '#title' => $property['label'],
168
      );
169

    
170
      // According to _views_bulk_operations_modify_action_get_properties
171
      // we have fixed list of supported types. Most of these types are string
172
      // and only some of them has options list.
173
      if (isset($property['options list'])) {
174
        $determined_type = ($property['type'] == 'list') ? 'checkboxes' : 'select';
175
      }
176
      else {
177
        $determined_type = ($property['type'] == 'boolean') ? 'checkbox' : 'textfield';
178
      }
179

    
180
      $form['properties'][$key] = array(
181
        '#type' => $determined_type,
182
        '#title' => $property['label'],
183
        '#description' => $property['description'],
184
        '#states' => array(
185
          'visible' => array(
186
            '#edit-properties-show-value-' . str_replace('_', '-', $key) => array('checked' => TRUE),
187
          ),
188
        ),
189
      );
190
      // The default #maxlength for textfields is 128, while most varchar
191
      // columns hold 255 characters, which makes it a saner default here.
192
      if ($determined_type == 'textfield') {
193
        $form['properties'][$key]['#maxlength'] = 255;
194
      }
195

    
196
      if (!empty($property['options list']) && is_callable($property['options list'])) {
197
        $form['properties'][$key]['#type'] = 'select';
198
        $form['properties'][$key]['#options'] = call_user_func_array($property['options list'], array($key, array()));
199

    
200
        if ($property['type'] == 'list') {
201
          $form['properties'][$key]['#type'] = 'checkboxes';
202

    
203
          $form['properties']['_append::' . $key] = array(
204
            '#type' => 'checkbox',
205
            '#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $property['label'])),
206
            '#states' => array(
207
              'visible' => array(
208
                '#edit-properties-show-value-' . $key => array('checked' => TRUE),
209
              ),
210
            ),
211
          );
212
        }
213
      }
214
    }
215
  }
216

    
217
  // Going to need this for multilingual nodes.
218
  global $language;
219
  foreach ($bundles as $bundle_name => $bundle) {
220
    $bundle_key = $info['entity keys']['bundle'];
221
    $default_values = array();
222
    // If the bundle key exists, it must always be set on an entity.
223
    if (!empty($bundle_key)) {
224
      $default_values[$bundle_key] = $bundle_name;
225
    }
226
    $default_values['language'] = $language->language;
227
    $entity = entity_create($context['entity_type'], $default_values);
228
    $form_state['entities'][$bundle_name] = $entity;
229

    
230
    // Show the more detailed label only if the entity type has multiple
231
    // bundles. Otherwise, it would just be confusing.
232
    if (count($info['bundles']) > 1) {
233
      $label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
234
    }
235
    else {
236
      $label = t('Fields');
237
    }
238

    
239
    $form_key = 'bundle_' . $bundle_name;
240
    $form[$form_key] = array(
241
      '#type' => 'fieldset',
242
      '#title' => $label,
243
      '#parents' => array($form_key),
244
    );
245
    field_attach_form($context['entity_type'], $entity, $form[$form_key], $form_state, entity_language($context['entity_type'], $entity));
246
    // Now that all the widgets have been added, sort them by #weight.
247
    // This ensures that they will stay in the correct order when they get
248
    // assigned new weights.
249
    uasort($form[$form_key], 'element_sort');
250

    
251
    $display_values = $context['settings']['display_values'];
252
    $instances = field_info_instances($entity_type, $bundle_name);
253
    $weight = 0;
254
    foreach (element_get_visible_children($form[$form_key]) as $field_name) {
255
      // For our use case it makes no sense for any field widget to be required.
256
      if (isset($form[$form_key][$field_name]['#language'])) {
257
        $field_language = $form[$form_key][$field_name]['#language'];
258
        _views_bulk_operations_modify_action_unset_required($form[$form_key][$field_name][$field_language]);
259
      }
260

    
261
      // The admin has specified which fields to display, but this field didn't
262
      // make the cut. Hide it with #access => FALSE and move on.
263
      if (empty($display_values[VBO_MODIFY_ACTION_ALL]) && empty($display_values[$bundle_name . '::' . $field_name])) {
264
        $form[$form_key][$field_name]['#access'] = FALSE;
265
        continue;
266
      }
267

    
268
      if (isset($instances[$field_name])) {
269
        $field = $instances[$field_name];
270
        $form[$form_key]['show_value'][$field_name] = array(
271
          '#type' => 'checkbox',
272
          '#title' => $field['label'],
273
        );
274
        $form[$form_key][$field_name]['#states'] = array(
275
          'visible' => array(
276
            '#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
277
          ),
278
        );
279
        // All field widgets get reassigned weights so that additional elements
280
        // added between them (such as "_append") can be properly ordered.
281
        $form[$form_key][$field_name]['#weight'] = $weight++;
282

    
283
        $field_info = field_info_field($field_name);
284
        if ($field_info['cardinality'] != 1) {
285
          $form[$form_key]['_append::' . $field_name] = array(
286
            '#type' => 'checkbox',
287
            '#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $field['label'])),
288
            '#states' => array(
289
              'visible' => array(
290
                '#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
291
              ),
292
            ),
293
            '#weight' => $weight++,
294
          );
295
        }
296
      }
297
    }
298

    
299
    // Add a clearfix below the checkboxes so that the widgets are not floated.
300
    $form[$form_key]['show_value']['#suffix'] = '<div class="clearfix"></div>';
301
    $form[$form_key]['show_value']['#weight'] = -1;
302
  }
303

    
304
  // If the form has only one group (for example, "Properties"), remove the
305
  // title and the fieldset, since there's no need to visually group values.
306
  $form_elements = element_get_visible_children($form);
307
  if (count($form_elements) == 1) {
308
    $element_key = reset($form_elements);
309
    unset($form[$element_key]['#type']);
310
    unset($form[$element_key]['#title']);
311

    
312
    // Get a list of all elements in the group, and filter out the non-values.
313
    $values = element_get_visible_children($form[$element_key]);
314
    foreach ($values as $index => $key) {
315
      if ($key == 'show_value' || substr($key, 0, 1) == '_') {
316
        unset($values[$index]);
317
      }
318
    }
319
    // If the group has only one value, no need to hide it through #states.
320
    if (count($values) == 1) {
321
      $value_key = reset($values);
322
      $form[$element_key]['show_value'][$value_key]['#type'] = 'value';
323
      $form[$element_key]['show_value'][$value_key]['#value'] = TRUE;
324
    }
325
  }
326

    
327
  if (module_exists('token') && $context['settings']['show_all_tokens']) {
328
    $token_type = str_replace('_', '-', $entity_type);
329
    $form['tokens'] = array(
330
      '#type' => 'fieldset',
331
      '#title' => t('Available tokens'),
332
      '#collapsible' => TRUE,
333
      '#collapsed' => TRUE,
334
      '#weight' => 998,
335
    );
336
    $form['tokens']['tree'] = array(
337
      '#theme' => 'token_tree',
338
      '#token_types' => array($token_type, 'site'),
339
      '#global_types' => array(),
340
      '#dialog' => TRUE,
341
    );
342
  }
343

    
344
  return $form;
345
}
346

    
347
/**
348
 * Action form validate function.
349
 *
350
 * Checks that the user selected at least one value to modify, validates
351
 * properties and calls Field API to validate fields for each bundle.
352
 */
353
function views_bulk_operations_modify_action_validate($form, &$form_state) {
354
  // The form structure for "Show" checkboxes is a bit bumpy.
355
  $search = array('properties');
356
  foreach ($form_state['entities'] as $bundle => $entity) {
357
    $search[] = 'bundle_' . $bundle;
358
  }
359

    
360
  $has_selected = FALSE;
361
  foreach ($search as $group) {
362
    // Store names of selected and appended entity values in a nicer format.
363
    $form_state['selected'][$group] = array();
364
    $form_state['append'][$group] = array();
365

    
366
    // This group has no values, move on.
367
    if (!isset($form_state['values'][$group])) {
368
      continue;
369
    }
370

    
371
    foreach ($form_state['values'][$group]['show_value'] as $key => $value) {
372
      if ($value) {
373
        $has_selected = TRUE;
374
        $form_state['selected'][$group][] = $key;
375
      }
376
      if (!empty($form_state['values'][$group]['_append::' . $key])) {
377
        $form_state['append'][$group][] = $key;
378
        unset($form_state['values'][$group]['_append::' . $key]);
379
      }
380
    }
381
    unset($form_state['values'][$group]['show_value']);
382
  }
383

    
384
  if (!$has_selected) {
385
    form_set_error('', t('You must select at least one value to modify.'));
386
    return;
387
  }
388

    
389
  // Use the wrapper to validate property values.
390
  if (!empty($form_state['selected']['properties'])) {
391
    // The entity used is irrelevant, and we can't rely on
392
    // $form_state['entities'] being non-empty, so a new one is created.
393
    $info = entity_get_info($form_state['entity_type']);
394
    $bundle_key = $info['entity keys']['bundle'];
395
    $default_values = array();
396
    // If the bundle key exists, it must always be set on an entity.
397
    if (!empty($bundle_key)) {
398
      $bundle_names = array_keys($info['bundles']);
399
      $bundle_name = reset($bundle_names);
400
      $default_values[$bundle_key] = $bundle_name;
401
    }
402
    $entity = entity_create($form_state['entity_type'], $default_values);
403
    $wrapper = entity_metadata_wrapper($form_state['entity_type'], $entity);
404

    
405
    $properties = _views_bulk_operations_modify_action_get_properties($form_state['entity_type']);
406
    foreach ($form_state['selected']['properties'] as $key) {
407
      $value = $form_state['values']['properties'][$key];
408
      if (!$wrapper->{$key}->validate($value)) {
409
        $label = $properties[$key]['label'];
410
        form_set_error('properties][' . $key, t('%label contains an invalid value.', array('%label' => $label)));
411
      }
412
    }
413
  }
414

    
415
  foreach ($form_state['entities'] as $bundle_name => $entity) {
416
    field_attach_form_validate($form_state['entity_type'], $entity, $form['bundle_' . $bundle_name], $form_state);
417
  }
418
}
419

    
420
/**
421
 * Action form submit function.
422
 *
423
 * Fills each constructed entity with property and field values, then
424
 * passes them to views_bulk_operations_modify_action().
425
 */
426
function views_bulk_operations_modify_action_submit($form, $form_state) {
427
  foreach ($form_state['entities'] as $bundle_name => $entity) {
428
    field_attach_submit($form_state['entity_type'], $entity, $form['bundle_' . $bundle_name], $form_state);
429
  }
430

    
431
  return array(
432
    'append' => $form_state['append'],
433
    'selected' => $form_state['selected'],
434
    'entities' => $form_state['entities'],
435
    'properties' => isset($form_state['values']['properties']) ? $form_state['values']['properties'] : array(),
436
  );
437
}
438

    
439
/**
440
 * Returns all properties that can be modified.
441
 *
442
 * Properties that can't be changed are entity keys, timestamps, and the ones
443
 * without a setter callback.
444
 *
445
 * @param string $entity_type
446
 *   The entity type whose properties will be fetched.
447
 * @param array $display_values
448
 *   An optional, admin-provided list of properties and fields that should be
449
 *   displayed for editing, used to filter the returned list of properties.
450
 */
451
function _views_bulk_operations_modify_action_get_properties($entity_type, $display_values = NULL) {
452
  $properties = array();
453
  $info = entity_get_info($entity_type);
454

    
455
  // List of properties that can't be modified.
456
  $disabled_properties = array('created', 'changed');
457
  foreach (array('id', 'bundle', 'revision') as $key) {
458
    if (!empty($info['entity keys'][$key])) {
459
      $disabled_properties[] = $info['entity keys'][$key];
460
    }
461
  }
462
  // List of supported types.
463
  $supported_types = array(
464
    'text',
465
    'token',
466
    'integer',
467
    'decimal',
468
    'date',
469
    'duration',
470
    'boolean',
471
    'uri',
472
    'list',
473
  );
474
  $property_info = entity_get_property_info($entity_type);
475
  if (empty($property_info['properties'])) {
476
    // Stop here if no properties were found.
477
    return array();
478
  }
479

    
480
  foreach ($property_info['properties'] as $key => $property) {
481
    if (in_array($key, $disabled_properties)) {
482
      continue;
483
    }
484
    // Filter out properties that can't be set (they are usually generated by a
485
    // getter callback based on other properties, and not stored in the DB).
486
    if (empty($property['setter callback'])) {
487
      continue;
488
    }
489
    // Determine the property type. If it's empty (permitted), default to text.
490
    // If it's a list type such as list<boolean>, extract the "boolean" part.
491
    $property['type'] = empty($property['type']) ? 'text' : $property['type'];
492
    $type = $property['type'];
493
    if ($list_type = entity_property_list_extract_type($type)) {
494
      $type = $list_type;
495
      $property['type'] = 'list';
496
    }
497
    // Filter out non-supported types (such as the Field API fields that
498
    // Commerce adds to its entities so that they show up in tokens).
499
    if (!in_array($type, $supported_types)) {
500
      continue;
501
    }
502

    
503
    $properties[$key] = $property;
504
  }
505

    
506
  if (isset($display_values) && empty($display_values[VBO_MODIFY_ACTION_ALL])) {
507
    // Return only the properties that the admin specified.
508
    return array_intersect_key($properties, $display_values);
509
  }
510

    
511
  return $properties;
512
}
513

    
514
/**
515
 * Returns all bundles for which field widgets should be displayed.
516
 *
517
 * If the admin decided to limit the modify form to certain properties / fields
518
 * (through the action settings) then only bundles that have at least one field
519
 * selected are returned.
520
 *
521
 * @param string $entity_type
522
 *   The entity type whose bundles will be fetched.
523
 * @param array $context
524
 *   The VBO context variable.
525
 */
526
function _views_bulk_operations_modify_action_get_bundles($entity_type, $context) {
527
  $bundles = array();
528

    
529
  $view = $context['view'];
530
  $vbo = _views_bulk_operations_get_field($view);
531
  $display_values = $context['settings']['display_values'];
532
  $info = entity_get_info($entity_type);
533
  $bundle_key = $info['entity keys']['bundle'];
534

    
535
  // Check if this View has a filter on the bundle key and assemble a list
536
  // of allowed bundles according to the filter.
537
  $filtered_bundles = array_keys($info['bundles']);
538

    
539
  // Go over all the filters and find any relevant ones.
540
  foreach ($view->filter as $key => $filter) {
541
    // Check it's the right field on the right table.
542
    if ($filter->table == $vbo->table && $filter->field == $bundle_key) {
543
      // Exposed filters may have no bundles, so check that there is a value.
544
      if (empty($filter->value)) {
545
        continue;
546
      }
547

    
548
      $operator = $filter->operator;
549
      if ($operator == 'in') {
550
        $filtered_bundles = array_intersect($filtered_bundles, $filter->value);
551
      }
552
      elseif ($operator == 'not in') {
553
        $filtered_bundles = array_diff($filtered_bundles, $filter->value);
554
      }
555
    }
556
  }
557

    
558
  foreach ($info['bundles'] as $bundle_name => $bundle) {
559
    // The view is limited to specific bundles, but this bundle isn't one of
560
    // them. Ignore it.
561
    if (!in_array($bundle_name, $filtered_bundles)) {
562
      continue;
563
    }
564

    
565
    $instances = field_info_instances($entity_type, $bundle_name);
566
    // Ignore bundles that don't have any field instances attached.
567
    if (empty($instances)) {
568
      continue;
569
    }
570

    
571
    $has_enabled_fields = FALSE;
572
    foreach ($display_values as $key) {
573
      if (strpos($key, $bundle_name . '::') === 0) {
574
        $has_enabled_fields = TRUE;
575
      }
576
    }
577
    // The admin has either specified that all values should be modifiable, or
578
    // selected at least one field belonging to this bundle.
579
    if (!empty($display_values[VBO_MODIFY_ACTION_ALL]) || $has_enabled_fields) {
580
      $bundles[$bundle_name] = $bundle;
581
    }
582
  }
583

    
584
  return $bundles;
585
}
586

    
587
/**
588
 * Helper function that recursively strips #required from field widgets.
589
 */
590
function _views_bulk_operations_modify_action_unset_required(&$element) {
591
  $element['#required'] = FALSE;
592
  foreach (element_children($element) as $key) {
593
    _views_bulk_operations_modify_action_unset_required($element[$key]);
594
  }
595
}
596

    
597
/**
598
 * VBO settings form function.
599
 */
600
function views_bulk_operations_modify_action_views_bulk_operations_form_options() {
601
  $options['show_all_tokens'] = TRUE;
602
  $options['display_values'] = array(VBO_MODIFY_ACTION_ALL);
603
  return $options;
604
}
605

    
606
/**
607
 * The settings form for this action.
608
 */
609
function views_bulk_operations_modify_action_views_bulk_operations_form($options, $entity_type, $dom_id) {
610
  // Initialize default values.
611
  if (empty($options)) {
612
    $options = views_bulk_operations_modify_action_views_bulk_operations_form_options();
613
  }
614

    
615
  $form['show_all_tokens'] = array(
616
    '#type' => 'checkbox',
617
    '#title' => t('Show available tokens'),
618
    '#description' => t('Check this to show a list of all available tokens in the bottom of the form. Requires the token module.'),
619
    '#default_value' => $options['show_all_tokens'],
620
  );
621

    
622
  $info = entity_get_info($entity_type);
623
  $properties = _views_bulk_operations_modify_action_get_properties($entity_type);
624
  $values = array(VBO_MODIFY_ACTION_ALL => t('- All -'));
625
  foreach ($properties as $key => $property) {
626
    $label = t('Properties');
627
    $values[$label][$key] = $property['label'];
628
  }
629
  foreach ($info['bundles'] as $bundle_name => $bundle) {
630
    $bundle_key = $info['entity keys']['bundle'];
631
    // Show the more detailed label only if the entity type has multiple
632
    // bundles. Otherwise, it would just be confusing.
633
    if (count($info['bundles']) > 1) {
634
      $label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
635
    }
636
    else {
637
      $label = t('Fields');
638
    }
639

    
640
    $instances = field_info_instances($entity_type, $bundle_name);
641
    foreach ($instances as $field_name => $field) {
642
      $values[$label][$bundle_name . '::' . $field_name] = $field['label'];
643
    }
644
  }
645

    
646
  $form['display_values'] = array(
647
    '#type' => 'select',
648
    '#title' => t('Display values'),
649
    '#options' => $values,
650
    '#multiple' => TRUE,
651
    '#description' => t('Select which values the action form should present to the user.'),
652
    '#default_value' => $options['display_values'],
653
    '#size' => 10,
654
  );
655
  return $form;
656
}