modify.action.inc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <?php
  2. /**
  3. * @file
  4. * VBO action to modify entity values (properties and fields).
  5. */
  6. // Specifies that all available values should be shown to the user for editing.
  7. define('VBO_MODIFY_ACTION_ALL', '_all_');
  8. /**
  9. * Implements hook_action_info().
  10. */
  11. function views_bulk_operations_modify_action_info() {
  12. return array(
  13. 'views_bulk_operations_modify_action' => array(
  14. 'type' => 'entity',
  15. 'label' => t('Modify entity values'),
  16. 'behavior' => array('changes_property'),
  17. // This action only works when invoked through VBO. That's why it's
  18. // declared as non-configurable to prevent it from being shown in the
  19. // "Create an advanced action" dropdown on admin/config/system/actions.
  20. 'configurable' => FALSE,
  21. 'vbo_configurable' => TRUE,
  22. 'triggers' => array('any'),
  23. ),
  24. );
  25. }
  26. /**
  27. * Action function.
  28. *
  29. * Goes through new values and uses them to modify the passed entity by either
  30. * replacing the existing values, or appending to them (based on user input).
  31. */
  32. function views_bulk_operations_modify_action($entity, $context) {
  33. list(,, $bundle_name) = entity_extract_ids($context['entity_type'], $entity);
  34. // Handle Field API fields.
  35. if (!empty($context['selected']['bundle_' . $bundle_name])) {
  36. // The pseudo entity is cloned so that changes to it don't get carried
  37. // over to the next execution.
  38. $pseudo_entity = clone $context['entities'][$bundle_name];
  39. foreach ($context['selected']['bundle_' . $bundle_name] as $key) {
  40. // Get this field's language. We can just pull it from the pseudo entity
  41. // as it was created using field_attach_form and entity_language so it's
  42. // already been figured out if this field is translatable or not and
  43. // applied the appropriate language code to the field.
  44. $language = key($pseudo_entity->{$key});
  45. // Replace any tokens that might exist in the field columns.
  46. foreach ($pseudo_entity->{$key}[$language] as $delta => &$item) {
  47. foreach ($item as $column => $value) {
  48. if (is_string($value)) {
  49. $item[$column] = token_replace($value, array($context['entity_type'] => $entity), array('sanitize' => FALSE));
  50. }
  51. }
  52. }
  53. if (in_array($key, $context['append']['bundle_' . $bundle_name]) && !empty($entity->$key)) {
  54. $entity->{$key}[$language] = array_merge($entity->{$key}[$language], $pseudo_entity->{$key}[$language]);
  55. // Check if we breached cardinality, and notify the user.
  56. $field_info = field_info_field($key);
  57. $field_count = count($entity->{$key}[$language]);
  58. if ($field_info['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $field_count > $field_info['cardinality']) {
  59. $entity_label = entity_label($context['entity_type'], $entity);
  60. $warning = t('Tried to set !field_count values for field !field_name that supports a maximum of !cardinality.',
  61. array(
  62. '!field_count' => $field_count,
  63. '!field_name' => $field_info['field_name'],
  64. '!cardinality' => $field_info['cardinality'],
  65. ));
  66. drupal_set_message($warning, 'warning', FALSE);
  67. }
  68. // Prevent storing duplicate references.
  69. if (strpos($field_info['type'], 'reference') !== FALSE) {
  70. $entity->{$key}[$language] = array_unique($entity->{$key}[LANGUAGE_NONE], SORT_REGULAR);
  71. }
  72. }
  73. else {
  74. $entity->{$key}[$language] = $pseudo_entity->{$key}[$language];
  75. }
  76. }
  77. }
  78. // Handle properties.
  79. // Use the wrapper to set property values, since some properties need
  80. // additional massaging by their setter callbacks.
  81. // The wrapper will automatically modify $entity itself.
  82. $wrapper = entity_metadata_wrapper($context['entity_type'], $entity);
  83. // The default setting for 'revision' property (create new revisions) should
  84. // be respected for nodes. This requires some special treatment.
  85. if ($context['entity_type'] == 'node' && in_array('revision', variable_get('node_options_' . $bundle_name)) && !in_array('revision', $context['selected']['properties'])) {
  86. $wrapper->revision->set(1);
  87. }
  88. if (!empty($context['selected']['properties'])) {
  89. foreach ($context['selected']['properties'] as $key) {
  90. if (!$wrapper->$key->access('update')) {
  91. // No access.
  92. continue;
  93. }
  94. if (in_array($key, $context['append']['properties'])) {
  95. $old_values = $wrapper->$key->value();
  96. $wrapper->$key->set($context['properties'][$key]);
  97. $new_values = $wrapper->{$key}->value();
  98. $all_values = array_merge($old_values, $new_values);
  99. $wrapper->$key->set($all_values);
  100. }
  101. else {
  102. $value = $context['properties'][$key];
  103. if (is_string($value)) {
  104. $value = token_replace($value, array($context['entity_type'] => $entity), array('sanitize' => FALSE));
  105. }
  106. $wrapper->$key->set($value);
  107. }
  108. }
  109. }
  110. }
  111. /**
  112. * Action form function.
  113. *
  114. * Displays form elements for properties acquired through Entity Metadata
  115. * (hook_entity_property_info()), as well as field widgets for each
  116. * entity bundle, as provided by field_attach_form().
  117. */
  118. function views_bulk_operations_modify_action_form($context, &$form_state) {
  119. // This action form uses admin-provided settings. If they were not set, pull
  120. // the defaults now.
  121. if (!isset($context['settings'])) {
  122. $context['settings'] = views_bulk_operations_modify_action_views_bulk_operations_form_options();
  123. }
  124. $form_state['entity_type'] = $entity_type = $context['entity_type'];
  125. // For Field API integration to work, a pseudo-entity is constructed for each
  126. // bundle that has fields available for editing.
  127. // The entities then get passed to Field API functions
  128. // (field_attach_form(), field_attach_form_validate(), field_attach_submit()),
  129. // and filled with form data.
  130. // After submit, the pseudo-entities get passed to the actual action
  131. // (views_bulk_operations_modify_action()) which copies the data from the
  132. // relevant pseudo-entity constructed here to the actual entity being
  133. // modified.
  134. $form_state['entities'] = array();
  135. $info = entity_get_info($entity_type);
  136. $properties = _views_bulk_operations_modify_action_get_properties($entity_type, $context['settings']['display_values']);
  137. $bundles = _views_bulk_operations_modify_action_get_bundles($entity_type, $context);
  138. $form['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/modify.action.css';
  139. $form['#tree'] = TRUE;
  140. if (!empty($properties)) {
  141. $form['properties'] = array(
  142. '#type' => 'fieldset',
  143. '#title' => t('Properties'),
  144. );
  145. $form['properties']['show_value'] = array(
  146. '#suffix' => '<div class="clearfix"></div>',
  147. );
  148. foreach ($properties as $key => $property) {
  149. $form['properties']['show_value'][$key] = array(
  150. '#type' => 'checkbox',
  151. '#title' => $property['label'],
  152. );
  153. // According to _views_bulk_operations_modify_action_get_properties
  154. // we have fixed list of supported types. Most of these types are string
  155. // and only some of them has options list.
  156. if (isset($property['options list'])) {
  157. $determined_type = ($property['type'] == 'list') ? 'checkboxes' : 'select';
  158. }
  159. else {
  160. $determined_type = ($property['type'] == 'boolean') ? 'checkbox' : 'textfield';
  161. }
  162. $form['properties'][$key] = array(
  163. '#type' => $determined_type,
  164. '#title' => $property['label'],
  165. '#description' => $property['description'],
  166. '#states' => array(
  167. 'visible' => array(
  168. '#edit-properties-show-value-' . str_replace('_', '-', $key) => array('checked' => TRUE),
  169. ),
  170. ),
  171. );
  172. // The default #maxlength for textfields is 128, while most varchar
  173. // columns hold 255 characters, which makes it a saner default here.
  174. if ($determined_type == 'textfield') {
  175. $form['properties'][$key]['#maxlength'] = 255;
  176. }
  177. if (!empty($property['options list'])) {
  178. $form['properties'][$key]['#type'] = 'select';
  179. $form['properties'][$key]['#options'] = $property['options list']($key, array());
  180. if ($property['type'] == 'list') {
  181. $form['properties'][$key]['#type'] = 'checkboxes';
  182. $form['properties']['_append::' . $key] = array(
  183. '#type' => 'checkbox',
  184. '#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $property['label'])),
  185. '#states' => array(
  186. 'visible' => array(
  187. '#edit-properties-show-value-' . $key => array('checked' => TRUE),
  188. ),
  189. ),
  190. );
  191. }
  192. }
  193. }
  194. }
  195. // Going to need this for multilingual nodes.
  196. global $language;
  197. foreach ($bundles as $bundle_name => $bundle) {
  198. $bundle_key = $info['entity keys']['bundle'];
  199. $default_values = array();
  200. // If the bundle key exists, it must always be set on an entity.
  201. if (!empty($bundle_key)) {
  202. $default_values[$bundle_key] = $bundle_name;
  203. }
  204. $default_values['language'] = $language->language;
  205. $entity = entity_create($context['entity_type'], $default_values);
  206. $form_state['entities'][$bundle_name] = $entity;
  207. // Show the more detailed label only if the entity type has multiple
  208. // bundles. Otherwise, it would just be confusing.
  209. if (count($info['bundles']) > 1) {
  210. $label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
  211. }
  212. else {
  213. $label = t('Fields');
  214. }
  215. $form_key = 'bundle_' . $bundle_name;
  216. $form[$form_key] = array(
  217. '#type' => 'fieldset',
  218. '#title' => $label,
  219. '#parents' => array($form_key),
  220. );
  221. field_attach_form($context['entity_type'], $entity, $form[$form_key], $form_state, entity_language($context['entity_type'], $entity));
  222. // Now that all the widgets have been added, sort them by #weight.
  223. // This ensures that they will stay in the correct order when they get
  224. // assigned new weights.
  225. uasort($form[$form_key], 'element_sort');
  226. $display_values = $context['settings']['display_values'];
  227. $instances = field_info_instances($entity_type, $bundle_name);
  228. $weight = 0;
  229. foreach (element_get_visible_children($form[$form_key]) as $field_name) {
  230. // For our use case it makes no sense for any field widget to be required.
  231. if (isset($form[$form_key][$field_name]['#language'])) {
  232. $field_language = $form[$form_key][$field_name]['#language'];
  233. _views_bulk_operations_modify_action_unset_required($form[$form_key][$field_name][$field_language]);
  234. }
  235. // The admin has specified which fields to display, but this field didn't
  236. // make the cut. Hide it with #access => FALSE and move on.
  237. if (empty($display_values[VBO_MODIFY_ACTION_ALL]) && empty($display_values[$bundle_name . '::' . $field_name])) {
  238. $form[$form_key][$field_name]['#access'] = FALSE;
  239. continue;
  240. }
  241. if (isset($instances[$field_name])) {
  242. $field = $instances[$field_name];
  243. $form[$form_key]['show_value'][$field_name] = array(
  244. '#type' => 'checkbox',
  245. '#title' => $field['label'],
  246. );
  247. $form[$form_key][$field_name]['#states'] = array(
  248. 'visible' => array(
  249. '#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
  250. ),
  251. );
  252. // All field widgets get reassigned weights so that additional elements
  253. // added between them (such as "_append") can be properly ordered.
  254. $form[$form_key][$field_name]['#weight'] = $weight++;
  255. $field_info = field_info_field($field_name);
  256. if ($field_info['cardinality'] != 1) {
  257. $form[$form_key]['_append::' . $field_name] = array(
  258. '#type' => 'checkbox',
  259. '#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $field['label'])),
  260. '#states' => array(
  261. 'visible' => array(
  262. '#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
  263. ),
  264. ),
  265. '#weight' => $weight++,
  266. );
  267. }
  268. }
  269. }
  270. // Add a clearfix below the checkboxes so that the widgets are not floated.
  271. $form[$form_key]['show_value']['#suffix'] = '<div class="clearfix"></div>';
  272. $form[$form_key]['show_value']['#weight'] = -1;
  273. }
  274. // If the form has only one group (for example, "Properties"), remove the
  275. // title and the fieldset, since there's no need to visually group values.
  276. $form_elements = element_get_visible_children($form);
  277. if (count($form_elements) == 1) {
  278. $element_key = reset($form_elements);
  279. unset($form[$element_key]['#type']);
  280. unset($form[$element_key]['#title']);
  281. // Get a list of all elements in the group, and filter out the non-values.
  282. $values = element_get_visible_children($form[$element_key]);
  283. foreach ($values as $index => $key) {
  284. if ($key == 'show_value' || substr($key, 0, 1) == '_') {
  285. unset($values[$index]);
  286. }
  287. }
  288. // If the group has only one value, no need to hide it through #states.
  289. if (count($values) == 1) {
  290. $value_key = reset($values);
  291. $form[$element_key]['show_value'][$value_key]['#type'] = 'value';
  292. $form[$element_key]['show_value'][$value_key]['#value'] = TRUE;
  293. }
  294. }
  295. if (module_exists('token') && $context['settings']['show_all_tokens']) {
  296. $token_type = str_replace('_', '-', $entity_type);
  297. $form['tokens'] = array(
  298. '#type' => 'fieldset',
  299. '#title' => t('Available tokens'),
  300. '#collapsible' => TRUE,
  301. '#collapsed' => TRUE,
  302. '#weight' => 998,
  303. );
  304. $form['tokens']['tree'] = array(
  305. '#theme' => 'token_tree',
  306. '#token_types' => array($token_type, 'site'),
  307. '#global_types' => array(),
  308. '#dialog' => TRUE,
  309. );
  310. }
  311. return $form;
  312. }
  313. /**
  314. * Action form validate function.
  315. *
  316. * Checks that the user selected at least one value to modify, validates
  317. * properties and calls Field API to validate fields for each bundle.
  318. */
  319. function views_bulk_operations_modify_action_validate($form, &$form_state) {
  320. // The form structure for "Show" checkboxes is a bit bumpy.
  321. $search = array('properties');
  322. foreach ($form_state['entities'] as $bundle => $entity) {
  323. $search[] = 'bundle_' . $bundle;
  324. }
  325. $has_selected = FALSE;
  326. foreach ($search as $group) {
  327. // Store names of selected and appended entity values in a nicer format.
  328. $form_state['selected'][$group] = array();
  329. $form_state['append'][$group] = array();
  330. // This group has no values, move on.
  331. if (!isset($form_state['values'][$group])) {
  332. continue;
  333. }
  334. foreach ($form_state['values'][$group]['show_value'] as $key => $value) {
  335. if ($value) {
  336. $has_selected = TRUE;
  337. $form_state['selected'][$group][] = $key;
  338. }
  339. if (!empty($form_state['values'][$group]['_append::' . $key])) {
  340. $form_state['append'][$group][] = $key;
  341. unset($form_state['values'][$group]['_append::' . $key]);
  342. }
  343. }
  344. unset($form_state['values'][$group]['show_value']);
  345. }
  346. if (!$has_selected) {
  347. form_set_error('', t('You must select at least one value to modify.'));
  348. return;
  349. }
  350. // Use the wrapper to validate property values.
  351. if (!empty($form_state['selected']['properties'])) {
  352. // The entity used is irrelevant, and we can't rely on
  353. // $form_state['entities'] being non-empty, so a new one is created.
  354. $info = entity_get_info($form_state['entity_type']);
  355. $bundle_key = $info['entity keys']['bundle'];
  356. $default_values = array();
  357. // If the bundle key exists, it must always be set on an entity.
  358. if (!empty($bundle_key)) {
  359. $bundle_names = array_keys($info['bundles']);
  360. $bundle_name = reset($bundle_names);
  361. $default_values[$bundle_key] = $bundle_name;
  362. }
  363. $entity = entity_create($form_state['entity_type'], $default_values);
  364. $wrapper = entity_metadata_wrapper($form_state['entity_type'], $entity);
  365. $properties = _views_bulk_operations_modify_action_get_properties($form_state['entity_type']);
  366. foreach ($form_state['selected']['properties'] as $key) {
  367. $value = $form_state['values']['properties'][$key];
  368. if (!$wrapper->$key->validate($value)) {
  369. $label = $properties[$key]['label'];
  370. form_set_error('properties][' . $key, t('%label contains an invalid value.', array('%label' => $label)));
  371. }
  372. }
  373. }
  374. foreach ($form_state['entities'] as $bundle_name => $entity) {
  375. field_attach_form_validate($form_state['entity_type'], $entity, $form['bundle_' . $bundle_name], $form_state);
  376. }
  377. }
  378. /**
  379. * Action form submit function.
  380. *
  381. * Fills each constructed entity with property and field values, then
  382. * passes them to views_bulk_operations_modify_action().
  383. */
  384. function views_bulk_operations_modify_action_submit($form, $form_state) {
  385. foreach ($form_state['entities'] as $bundle_name => $entity) {
  386. field_attach_submit($form_state['entity_type'], $entity, $form['bundle_' . $bundle_name], $form_state);
  387. }
  388. return array(
  389. 'append' => $form_state['append'],
  390. 'selected' => $form_state['selected'],
  391. 'entities' => $form_state['entities'],
  392. 'properties' => isset($form_state['values']['properties']) ? $form_state['values']['properties'] : array(),
  393. );
  394. }
  395. /**
  396. * Returns all properties that can be modified.
  397. *
  398. * Properties that can't be changed are entity keys, timestamps, and the ones
  399. * without a setter callback.
  400. *
  401. * @param string $entity_type
  402. * The entity type whose properties will be fetched.
  403. * @param array $display_values
  404. * An optional, admin-provided list of properties and fields that should be
  405. * displayed for editing, used to filter the returned list of properties.
  406. */
  407. function _views_bulk_operations_modify_action_get_properties($entity_type, $display_values = NULL) {
  408. $properties = array();
  409. $info = entity_get_info($entity_type);
  410. // List of properties that can't be modified.
  411. $disabled_properties = array('created', 'changed');
  412. foreach (array('id', 'bundle', 'revision') as $key) {
  413. if (!empty($info['entity keys'][$key])) {
  414. $disabled_properties[] = $info['entity keys'][$key];
  415. }
  416. }
  417. // List of supported types.
  418. $supported_types = array(
  419. 'text',
  420. 'token',
  421. 'integer',
  422. 'decimal',
  423. 'date',
  424. 'duration',
  425. 'boolean',
  426. 'uri',
  427. 'list',
  428. );
  429. $property_info = entity_get_property_info($entity_type);
  430. if (empty($property_info['properties'])) {
  431. // Stop here if no properties were found.
  432. return array();
  433. }
  434. foreach ($property_info['properties'] as $key => $property) {
  435. if (in_array($key, $disabled_properties)) {
  436. continue;
  437. }
  438. // Filter out properties that can't be set (they are usually generated by a
  439. // getter callback based on other properties, and not stored in the DB).
  440. if (empty($property['setter callback'])) {
  441. continue;
  442. }
  443. // Determine the property type. If it's empty (permitted), default to text.
  444. // If it's a list type such as list<boolean>, extract the "boolean" part.
  445. $property['type'] = empty($property['type']) ? 'text' : $property['type'];
  446. $type = $property['type'];
  447. if ($list_type = entity_property_list_extract_type($type)) {
  448. $type = $list_type;
  449. $property['type'] = 'list';
  450. }
  451. // Filter out non-supported types (such as the Field API fields that
  452. // Commerce adds to its entities so that they show up in tokens).
  453. if (!in_array($type, $supported_types)) {
  454. continue;
  455. }
  456. $properties[$key] = $property;
  457. }
  458. if (isset($display_values) && empty($display_values[VBO_MODIFY_ACTION_ALL])) {
  459. // Return only the properties that the admin specified.
  460. return array_intersect_key($properties, $display_values);
  461. }
  462. return $properties;
  463. }
  464. /**
  465. * Returns all bundles for which field widgets should be displayed.
  466. *
  467. * If the admin decided to limit the modify form to certain properties / fields
  468. * (through the action settings) then only bundles that have at least one field
  469. * selected are returned.
  470. *
  471. * @param string $entity_type
  472. * The entity type whose bundles will be fetched.
  473. * @param array $context
  474. * The VBO context variable.
  475. */
  476. function _views_bulk_operations_modify_action_get_bundles($entity_type, $context) {
  477. $bundles = array();
  478. $view = $context['view'];
  479. $vbo = _views_bulk_operations_get_field($view);
  480. $display_values = $context['settings']['display_values'];
  481. $info = entity_get_info($entity_type);
  482. $bundle_key = $info['entity keys']['bundle'];
  483. // Check if this View has a filter on the bundle key and assemble a list
  484. // of allowed bundles according to the filter.
  485. $filtered_bundles = array_keys($info['bundles']);
  486. // Go over all the filters and find any relevant ones.
  487. foreach ($view->filter as $key => $filter) {
  488. // Check it's the right field on the right table.
  489. if ($filter->table == $vbo->table && $filter->field == $bundle_key) {
  490. // Exposed filters may have no bundles, so check that there is a value.
  491. if (empty($filter->value)) {
  492. continue;
  493. }
  494. $operator = $filter->operator;
  495. if ($operator == 'in') {
  496. $filtered_bundles = array_intersect($filtered_bundles, $filter->value);
  497. }
  498. elseif ($operator == 'not in') {
  499. $filtered_bundles = array_diff($filtered_bundles, $filter->value);
  500. }
  501. }
  502. }
  503. foreach ($info['bundles'] as $bundle_name => $bundle) {
  504. // The view is limited to specific bundles, but this bundle isn't one of
  505. // them. Ignore it.
  506. if (!in_array($bundle_name, $filtered_bundles)) {
  507. continue;
  508. }
  509. $instances = field_info_instances($entity_type, $bundle_name);
  510. // Ignore bundles that don't have any field instances attached.
  511. if (empty($instances)) {
  512. continue;
  513. }
  514. $has_enabled_fields = FALSE;
  515. foreach ($display_values as $key) {
  516. if (strpos($key, $bundle_name . '::') !== FALSE) {
  517. $has_enabled_fields = TRUE;
  518. }
  519. }
  520. // The admin has either specified that all values should be modifiable, or
  521. // selected at least one field belonging to this bundle.
  522. if (!empty($display_values[VBO_MODIFY_ACTION_ALL]) || $has_enabled_fields) {
  523. $bundles[$bundle_name] = $bundle;
  524. }
  525. }
  526. return $bundles;
  527. }
  528. /**
  529. * Helper function that recursively strips #required from field widgets.
  530. */
  531. function _views_bulk_operations_modify_action_unset_required(&$element) {
  532. unset($element['#required']);
  533. foreach (element_children($element) as $key) {
  534. _views_bulk_operations_modify_action_unset_required($element[$key]);
  535. }
  536. }
  537. /**
  538. * VBO settings form function.
  539. */
  540. function views_bulk_operations_modify_action_views_bulk_operations_form_options() {
  541. $options['show_all_tokens'] = TRUE;
  542. $options['display_values'] = array(VBO_MODIFY_ACTION_ALL);
  543. return $options;
  544. }
  545. /**
  546. * The settings form for this action.
  547. */
  548. function views_bulk_operations_modify_action_views_bulk_operations_form($options, $entity_type, $dom_id) {
  549. // Initialize default values.
  550. if (empty($options)) {
  551. $options = views_bulk_operations_modify_action_views_bulk_operations_form_options();
  552. }
  553. $form['show_all_tokens'] = array(
  554. '#type' => 'checkbox',
  555. '#title' => t('Show available tokens'),
  556. '#description' => t('Check this to show a list of all available tokens in the bottom of the form. Requires the token module.'),
  557. '#default_value' => $options['show_all_tokens'],
  558. );
  559. $info = entity_get_info($entity_type);
  560. $properties = _views_bulk_operations_modify_action_get_properties($entity_type);
  561. $values = array(VBO_MODIFY_ACTION_ALL => t('- All -'));
  562. foreach ($properties as $key => $property) {
  563. $label = t('Properties');
  564. $values[$label][$key] = $property['label'];
  565. }
  566. foreach ($info['bundles'] as $bundle_name => $bundle) {
  567. $bundle_key = $info['entity keys']['bundle'];
  568. // Show the more detailed label only if the entity type has multiple
  569. // bundles. Otherwise, it would just be confusing.
  570. if (count($info['bundles']) > 1) {
  571. $label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
  572. }
  573. else {
  574. $label = t('Fields');
  575. }
  576. $instances = field_info_instances($entity_type, $bundle_name);
  577. foreach ($instances as $field_name => $field) {
  578. $values[$label][$bundle_name . '::' . $field_name] = $field['label'];
  579. }
  580. }
  581. $form['display_values'] = array(
  582. '#type' => 'select',
  583. '#title' => t('Display values'),
  584. '#options' => $values,
  585. '#multiple' => TRUE,
  586. '#description' => t('Select which values the action form should present to the user.'),
  587. '#default_value' => $options['display_values'],
  588. '#size' => 10,
  589. );
  590. return $form;
  591. }