multiple_fields_remove_button.module in Multiple Fields Remove Button 7
Same filename and directory in other branches
In this file we use widget hooks to extend their functionality.
We use drupal hooks and add remove button for fields remove.
File
multiple_fields_remove_button.moduleView source
<?php
/**
* @file
* In this file we use widget hooks to extend their functionality.
*
* We use drupal hooks and add remove button for fields remove.
*/
/**
* Implements hook_menu().
*/
function multiple_fields_remove_button_menu() {
$items = array();
$items['multiple_fields_remove_button/ajax'] = array(
'title' => 'Remove item callback',
'page callback' => 'multiple_fields_remove_button_js',
'delivery callback' => 'ajax_deliver',
'access callback' => TRUE,
'theme callback' => 'ajax_base_page_theme',
'type' => MENU_CALLBACK,
'file path' => 'includes',
'file' => 'form.inc',
);
return $items;
}
/**
* Ajax callback remove field when remove click is trigger.
*
* In this callback we will replace field items. Main job
* to delete field item we will done into submit handler.
*
* @see multiple_fields_remove_button_field_widget_form_alter()
*/
function multiple_fields_remove_button_js() {
if (isset($_POST['ajax_html_ids'])) {
unset($_POST['ajax_html_ids']);
}
list($form, $form_state) = ajax_get_form();
drupal_process_form($form['#form_id'], $form, $form_state);
// Get the information on what we're removing.
$button = $form_state['triggering_element'];
// Go two levels up in the form, to the whole widget.
$element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -3));
// Now send back the proper AJAX command to replace it.
$return = array(
'#type' => 'ajax',
'#commands' => array(
ajax_command_replace('#' . $element['#id'], drupal_render($element)),
),
);
// Because we're doing this ourselves, messages aren't automatic. We have
// to add them.
$messages = theme('status_messages');
if ($messages) {
$return['#commands'][] = ajax_command_prepend('#' . $element['#id'], $messages);
}
return $return;
}
/**
* Implements hook_form_alter().
*/
function multiple_fields_remove_button_form_alter(&$form, &$form_state) {
if (isset($_POST['ajax_html_ids'])) {
$name = '';
if (isset($form_state['clicked_button']['#parents'][0]) && !empty($form_state['clicked_button']['#parents'][0])) {
$name = $form_state['clicked_button']['#parents'][0];
}
$language = !empty($form['language']['#value']) ? $form['language']['#value'] : LANGUAGE_NONE;
if (!empty($name) && isset($form_state['input'][$name][$language]) && !empty($form_state['input'][$name][$language])) {
$count = count($form_state['input'][$name][$language]) - 1;
if (isset($form[$name][$language]) && !empty($form[$name][$language])) {
foreach ($form[$name][$language] as $key => &$value) {
if (isset($key) && is_numeric($key) && !empty($value['target_id'])) {
if ($key > $count) {
$value['target_id']['#default_value'] = '';
}
}
}
}
}
}
}
/**
* Implements hook_field_widget_form_alter().
*/
function multiple_fields_remove_button_field_widget_form_alter(&$element, &$form_state, $context) {
// Remove button for the following field type widgets.
$fieldwidgets = array(
'multiple_selects',
'text_textfield',
'date_popup',
'date_text',
'link_field',
'entityreference_autocomplete',
'number',
'text_textarea',
'text_textarea_with_summary',
'addressfield_standard',
'location',
);
// Add extra widgets with the help of others modules.
drupal_alter('multiple_field_remove_button_field_widgets', $fieldwidgets);
if (isset($element['#entity']) && $element['#entity'] && $context['field']['cardinality'] == FIELD_CARDINALITY_UNLIMITED && in_array($context['instance']['widget']['type'], $fieldwidgets)) {
$original_element = $element;
if ($context['field']['type'] == 'entityreference') {
$original_element = $element['target_id'];
}
$field_parents = isset($original_element['#field_parents']) ? $original_element['#field_parents'] : array();
$field_name = isset($original_element['#field_name']) ? $original_element['#field_name'] : NULL;
$language = isset($original_element['#language']) ? $original_element['#language'] : LANGUAGE_NONE;
$delta = isset($original_element['#delta']) ? $original_element['#delta'] : 0;
// Get parent which will we use into Remove Button Element.
$parents = array_merge($field_parents, array(
$field_name,
$language,
$delta,
));
$element['remove_button'] = array(
'#delta' => $delta,
'#name' => implode('_', $parents) . '_remove_button',
'#type' => 'submit',
'#value' => t('Remove'),
'#validate' => array(),
'#attributes' => array(
'class' => array(
'multiple-fields-remove-button',
),
),
'#submit' => array(
'multiple_fields_remove_button_submit_handler',
),
'#limit_validation_errors' => array(),
'#ajax' => array(
'path' => 'multiple_fields_remove_button/ajax',
'effect' => 'fade',
),
'#weight' => 1000,
);
}
}
/**
* Submit callback to remove an item from the field UI multiple wrapper.
*
* When a remove button is submitted, we need to find the item that it
* referenced and delete it. Since field UI has the deltas as a straight
* unbroken array key, we have to renumber everything down. Since we do this
* we *also* need to move all the deltas around in the $form_state['values']
* and $form_state['input'] so that user changed values follow. This is a bit
* of a complicated process.
*/
function multiple_fields_remove_button_submit_handler($form, &$form_state) {
$button = $form_state['triggering_element'];
$delta = $button['#delta'];
$inpt = 'input';
// Where in the form we'll find the parent element.
$address = array_slice($button['#array_parents'], 0, -2);
// Go one level up in the form, to the widgets container.
$parent_element = drupal_array_get_nested_value($form, $address);
$field_name = $parent_element['#field_name'];
$langcode = $parent_element['#language'];
$parents = $parent_element['#field_parents'];
// We should update also entity (if set) in form state
// to correctly add new items.
if (isset($form['#entity_type']) && isset($form_state[$form['#entity_type']]->{$field_name})) {
$entity_field =& $form_state[$form['#entity_type']]->{$field_name};
}
else {
$entity_field = array();
}
$field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
// Go ahead and renumber everything from our delta to the last
// item down one. This will overwrite the item being removed.
for ($i = $delta; $i <= $field_state['items_count']; $i++) {
$old_element_address = array_merge($address, array(
$i + 1,
));
$new_element_address = array_merge($address, array(
$i,
));
// Address of field elements oin the entity field.
$old_entity_element_address = $old_element_address;
array_shift($old_entity_element_address);
$new_entity_element_address = $new_element_address;
array_shift($new_entity_element_address);
$moving_element = drupal_array_get_nested_value($form, $old_element_address);
$moving_element_value = drupal_array_get_nested_value($form_state['values'], $old_element_address);
$moving_element_input = drupal_array_get_nested_value($form_state[$inpt], $old_element_address);
$moving_element_item = drupal_array_get_nested_value($entity_field, $old_entity_element_address);
// Tell the element where it's being moved to.
$moving_element['#parents'] = $new_element_address;
// Move the element around.
form_set_value($moving_element, $moving_element_value, $form_state);
drupal_array_set_nested_value($form_state[$inpt], $moving_element['#parents'], $moving_element_input);
drupal_array_set_nested_value($entity_field, $new_entity_element_address, $moving_element_item);
// Move the entity in our saved state.
if (isset($field_state['entity'][$i + 1])) {
$field_state['entity'][$i] = $field_state['entity'][$i + 1];
}
else {
unset($field_state['entity'][$i]);
}
}
// Replace the deleted entity with an empty one. This helps to ensure that
// trying to add a new entity won't resurrect a deleted entity
// from thev trash bin.
// $count = count($field_state['entity']);
// Then remove the last item. But we must not go negative.
if ($field_state['items_count'] > 0) {
$field_state['items_count']--;
}
// Fix the weights. Field UI lets the weights be in a range of
// (-1 * item_count) to (item_count). This means that when we remove one,
// the range shrinks; weights outside of that range then get set to
// the first item in the select by the browser, floating them to the top.
// We use a brute force method because we lost weights on both ends
// and if the user has moved things around, we have to cascade because
// if I have items weight weights 3 and 4, and I change 4 to 3 but leave
// the 3, the order of the two 3s now is undefined and may not match what
// the user had selected.
$input = drupal_array_get_nested_value($form_state[$inpt], $address);
// Sort by weight.
uasort($input, '_field_sort_items_helper');
// Reweight everything in the correct order.
$weight = -1 * $field_state['items_count'];
foreach ($input as $key => $item) {
if ($item) {
$input[$key]['_weight'] = $weight++;
}
}
drupal_array_set_nested_value($form_state[$inpt], $address, $input);
field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
$form_state['rebuild'] = TRUE;
}
Functions
Name![]() |
Description |
---|---|
multiple_fields_remove_button_field_widget_form_alter | Implements hook_field_widget_form_alter(). |
multiple_fields_remove_button_form_alter | Implements hook_form_alter(). |
multiple_fields_remove_button_js | Ajax callback remove field when remove click is trigger. |
multiple_fields_remove_button_menu | Implements hook_menu(). |
multiple_fields_remove_button_submit_handler | Submit callback to remove an item from the field UI multiple wrapper. |