diff --git a/conditional_fields.module b/conditional_fields.module index 31c76ec..76817aa 100755 --- a/conditional_fields.module +++ b/conditional_fields.module @@ -275,9 +275,7 @@ function conditional_fields_element_after_build($element, &$form_state) { // Attach dependent. if (isset($dependencies['dependents'][$field['#field_name']])) { foreach ($dependencies['dependents'][$field['#field_name']] as $id => $dependency) { - if (!isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) { - conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id); - } + conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id); } } @@ -287,9 +285,7 @@ function conditional_fields_element_after_build($element, &$form_state) { // define per-element sets of dependency values. if (isset($dependencies['dependees'][$field['#field_name']])) { foreach ($dependencies['dependees'][$field['#field_name']] as $id => $dependency) { - if (!isset($form['#conditional_fields'][$field['#field_name']]['dependents'][$id])) { - conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id); - } + conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id); } } @@ -397,8 +393,8 @@ function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $op // Use the #parents property of the dependee instead of #field_parents since // we will need access to the full structure of the widget. if (isset($dependee['#parents'])) { - $form['#conditional_fields'][$dependee['#field_name']]['parents'] = $dependee['#parents']; - $form['#conditional_fields'][$dependee['#field_name']]['dependents'][$id] = array( + $form['#conditional_fields'][$dependee['#field_name']]['parents'][$id][] = $dependee['#parents']; + $form['#conditional_fields'][$dependee['#field_name']]['dependents'][$id][] = array( 'dependent' => $dependent['#field_name'], 'options' => $options, ); @@ -412,8 +408,8 @@ function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $op $dependent_parents = $dependent['#parents']; } if (isset($dependent_parents)) { - $form['#conditional_fields'][$dependent['#field_name']]['field_parents'] = $dependent_parents; - $form['#conditional_fields'][$dependent['#field_name']]['dependees'][$id] = array( + $form['#conditional_fields'][$dependent['#field_name']]['field_parents'][$id][] = $dependent_parents; + $form['#conditional_fields'][$dependent['#field_name']]['dependees'][$id][] = array( 'dependee' => $dependee['#field_name'], 'options' => $options, ); @@ -442,204 +438,202 @@ function conditional_fields_form_after_build($form, &$form_state) { // Cycle all dependents. foreach ($form['#conditional_fields'] as $dependent => $dependent_info) { - $states = array(); - if (empty($dependent_info['dependees'])) { continue; } - $dependent_location = array_merge($dependent_info['field_parents'], array($dependent)); - $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location); + foreach ($dependent_info['field_parents'] as $id => $field_parents_list) { + foreach ($field_parents_list as $key => $field_parents) { + $dependent_location = array_merge($field_parents, array($dependent)); - // Cycle the dependant's dependees. - foreach ($dependent_info['dependees'] as $dependency) { - $dependee = $dependency['dependee']; + $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location); - if (empty($form['#conditional_fields'][$dependee])) { - continue; - } + // Cycle the dependant's dependees. + $dependency = $dependent_info['dependees'][$id][$key]; + $states = array(); + $dependee = $dependency['dependee']; - $dependee_info = $form['#conditional_fields'][$dependee]; - $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']); - $options = $dependency['options']; + if (empty($form['#conditional_fields'][$dependee])) { + continue; + } - // Load field edit behaviors. - // If this dependent has multiple dependees, only the logic of the first - // dependency will be taken into account. - if (!isset($behaviors)) { - $behaviors = conditional_fields_field_behaviors('edit', $options); - } + $dependee_info = $form['#conditional_fields'][$dependee]; + $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents'][$id][$key]); + $options = $dependency['options']; - // Determine if the dependee is in the form. - if (empty($dependee_form_field) || (isset($dependee_form_field['#access']) && $dependee_form_field['#access'] == FALSE)) { - // Apply orphan dependent behaviors. - /* - if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN, $behaviors)) { - // TODO - $is_triggered = TRUE; + // Load field edit behaviors. If this dependent has multiple dependees, + // only the logic of the first dependency will be taken into account. + if (!isset($behaviors)) { + $behaviors = conditional_fields_field_behaviors('edit', $options); + } - if ($is_orphan && !$is_triggered) { - $form[$dependent]['#access'] = FALSE; + // Determine if the dependee is in the form. + if (empty($dependee_form_field) || (isset($dependee_form_field['#access']) && $dependee_form_field['#access'] == FALSE)) { + // Apply orphan dependent behaviors. + /* + if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN, $behaviors)) { + // TODO + $is_triggered = TRUE; + if ($is_orphan && !$is_triggered) { + $form[$dependent]['#access'] = FALSE; + } } + */ + if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN, $behaviors)) { + $dependent_form_field['#access'] = FALSE; + } + unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]); + unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]); + continue; } - */ - if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN, $behaviors)) { - $dependent_form_field['#access'] = FALSE; - } + unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]); unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]); - continue; - } - - unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]); - unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]); - // Build a jQuery selector if it was not overridden by a custom value. - // Note that this may be overridden later by a state handler. - if (!$options['selector']) { - $options['selector'] = conditional_fields_field_selector($dependee_form_field); - } - else { - // Replace the language placeholder in the selector with current language. - $options['selector'] = str_replace('%lang', $dependee_form_field['#language'], $options['selector']); - } - - if ($options['condition'] != 'value') { - // Conditions different than "value" are always evaluated against TRUE. - $state = array($options['state'] => array($options['selector'] => array($options['condition'] => TRUE))); - } - else { - // Build the values that trigger the dependency. - $values = array(); - - if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) { - $values[$options['condition']] = $options['value_form']; + // Build a jQuery selector if it was not overridden by a custom value. + // Note that this may be overridden later by a state handler. + if (!$options['selector']) { + $options['selector'] = conditional_fields_field_selector($dependee_form_field); } - elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) { - $values[$options['condition']] = $options['value']; + else { + // Replace the language placeholder in the selector with current language. + $options['selector'] = str_replace('%lang', $dependee_form_field['#language'], $options['selector']); } - elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND) { - $values[$options['condition']] = count($options['values']) == 1 ? $options['values'][0] : $options['values']; + + if ($options['condition'] != 'value') { + // Conditions different than "value" are always evaluated against TRUE. + $state = array($options['state'] => array($options['selector'] => array($options['condition'] => TRUE))); } else { - if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR) { - // XOR behaves like OR with added 'xor' element. - $values[] = 'xor'; + // Build the values that trigger the dependency. + $values = array(); + + if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) { + $values[$options['condition']] = $options['value_form']; + } + elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) { + $values[$options['condition']] = $options['value']; + } + elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND) { + $values[$options['condition']] = count($options['values']) == 1 ? $options['values'][0] : $options['values']; } - elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT) { - // NOT behaves like OR with switched state. - $options['state'] = strpos($options['state'], '!') === 0 ? drupal_substr($options['state'], 1) : '!' . $options['state']; + else { + if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR) { + // XOR behaves like OR with added 'xor' element. + $values[] = 'xor'; + } + elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT) { + // NOT behaves like OR with switched state. + $options['state'] = strpos($options['state'], '!') === 0 ? drupal_substr($options['state'], 1) : '!' . $options['state']; + } + + // OR, NOT and XOR conditions are obtained with a nested array. + foreach ($options['values'] as $value) { + $values[] = array($options['condition'] => $value); + } } - // OR, NOT and XOR conditions are obtained with a nested array. - foreach ($options['values'] as $value) { - $values[] = array($options['condition'] => $value); + $state = array($options['state'] => array($options['selector'] => $values)); + $dependee_form_state = isset($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language']) ? field_form_get_state($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language'], $form_state) : NULL; + // Execute special handler for fields that need further processing. + // The handler has no return value. Modify the $state parameter by + // reference if needed. + foreach ($state_handlers as $handler => $handler_conditions) { + if (array_intersect_assoc($handler_conditions, $dependee_form_field) == $handler_conditions) { + $handler($dependee_form_field, $dependee_form_state, $options, $state); + } } - } - $state = array($options['state'] => array($options['selector'] => $values)); - $dependee_form_state = isset($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language']) ? field_form_get_state($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language'], $form_state) : NULL; + // Add validation callback to element. + _conditional_fields_element_add_property($dependent_form_field, '#element_validate', 'conditional_fields_dependent_validate', 'append'); + } - // Execute special handler for fields that need further processing. - // The handler has no return value. Modify the $state parameter by - // reference if needed. - foreach ($state_handlers as $handler => $handler_conditions) { - if (array_intersect_assoc($handler_conditions, $dependee_form_field) == $handler_conditions) { - $handler($dependee_form_field, $dependee_form_state, $options, $state); + // Add the $state into the correct logic group in $states. + foreach ($state as $key => $constraints) { + if (empty($states[$key][$options['grouping']])) { + $states[$key][$options['grouping']] = $constraints; + } + else { + $states[$key][$options['grouping']] = array_merge($states[$key][$options['grouping']], $constraints); } } - // Add validation callback to element. - _conditional_fields_element_add_property($dependent_form_field, '#element_validate', 'conditional_fields_dependent_validate', 'append'); - } + // Build effect settings for effects with options. + // TODO: add dependee key to allow different effects on the same selector. + if ($options['effect'] && $options['effect'] != 'show') { + $selector = conditional_fields_field_selector(drupal_array_get_nested_value($form, array($dependent_location[0]))); + // Convert numeric strings to numbers. + foreach ($options['effect_options'] as &$effect_option) { + if (is_numeric($effect_option)) { + $effect_option += 0; + } + } + $effects[$selector] = array( + 'effect' => $options['effect'], + 'options' => $options['effect_options'], + ); + } - // Add the $state into the correct logic group in $states. - foreach ($state as $key => $constraints) { - if (empty($states[$key][$options['grouping']])) { - $states[$key][$options['grouping']] = $constraints; + // Apply reset dependent to default if untriggered behavior. + if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED, $behaviors)) { + // Add property to element so conditional_fields_dependent_validate() can + // pick it up. + $dependent_form_field['#conditional_fields_reset_if_untriggered'] = TRUE; + unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]); } - else { - $states[$key][$options['grouping']] = array_merge($states[$key][$options['grouping']], $constraints); + // Execute custom behaviors. + if (!empty($behaviors)) { + foreach ($behaviors as $behavior) { + // Custom behaviors are callbacks. + if (function_exists($$behavior)) { + $$behavior('edit', $form, $form_state, $dependent, $dependencies); + } + } } - } - // Build effect settings for effects with options. - // TODO: add dependee key to allow different effects on the same selector. - if ($options['effect'] && $options['effect'] != 'show') { - $selector = conditional_fields_field_selector(drupal_array_get_nested_value($form, array($dependent_location[0]))); - // Convert numeric strings to numbers. - foreach ($options['effect_options'] as &$effect_option) { - if (is_numeric($effect_option)) { - $effect_option += 0; - } + if (empty($states)) { + continue; } - $effects[$selector] = array( - 'effect' => $options['effect'], - 'options' => $options['effect_options'], - ); - } - // Apply reset dependent to default if untriggered behavior. - if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED, $behaviors)) { - // Add property to element so conditional_fields_dependent_validate() can - // pick it up. - $dependent_form_field['#conditional_fields_reset_if_untriggered'] = TRUE; - unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]); - } - } + // Save the modified field back into the form. + drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field); - // Execute custom behaviors. - if (!empty($behaviors)) { - foreach ($behaviors as $behavior) { - // Custom behaviors are callbacks. - if (function_exists($$behavior)) { - $$behavior('edit', $form, $form_state, $dependent, $dependencies); + // Map the states based on the conjunctions. + $states_new = array(); + foreach ($states as $state_key => $value) { + // As the main object is ANDed together we can add the AND items directly. + if (!empty($states[$state_key]['AND'])) { + $states_new[$state_key] = $states[$state_key]['AND']; + } + // The OR and XOR groups are moved into a sub-array that has numeric keys + // so that we get a JSON array and not an object, as required by the States + // API for OR and XOR groupings. + if (!empty($states[$state_key]['OR'])) { + $or = array(); + foreach ($states[$state_key]['OR'] as $constraint_key => $constraint_value) { + $or[] = array($constraint_key => $constraint_value); + } + // '1' as a string so that we get an object (which means logic groups + // are ANDed together). + $states_new[$state_key]['1'] = $or; + } + if (!empty($states[$state_key]['XOR'])) { + $xor = array('xor'); + foreach ($states[$state_key]['XOR'] as $constraint_key => $constraint_value) { + $xor[] = array($constraint_key => $constraint_value); + } + // '2' as a string so that we get an object. + $states_new[$state_key]['2'] = $xor; + } } - } - } - unset($behaviors); - - if (empty($states)) { - continue; - } - // Save the modified field back into the form. - drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field); + $states = $states_new; - // Map the states based on the conjunctions. - $states_new = array(); - foreach ($states as $state_key => $value) { - // As the main object is ANDed together we can add the AND items directly. - if (!empty($states[$state_key]['AND'])) { - $states_new[$state_key] = $states[$state_key]['AND']; - } - // The OR and XOR groups are moved into a sub-array that has numeric keys - // so that we get a JSON array and not an object, as required by the States - // API for OR and XOR groupings. - if (!empty($states[$state_key]['OR'])) { - $or = array(); - foreach ($states[$state_key]['OR'] as $constraint_key => $constraint_value) { - $or[] = array($constraint_key => $constraint_value); - } - // '1' as a string so that we get an object (which means logic groups - // are ANDed together). - $states_new[$state_key]['1'] = $or; - } - if (!empty($states[$state_key]['XOR'])) { - $xor = array('xor'); - foreach ($states[$state_key]['XOR'] as $constraint_key => $constraint_value) { - $xor[] = array($constraint_key => $constraint_value); - } - // '2' as a string so that we get an object. - $states_new[$state_key]['2'] = $xor; + // Add the #states property to the dependent field. + drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states); + $has_states = TRUE; } } - $states = $states_new; - - // Add the #states property to the dependent field. - drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states); - - $has_states = TRUE; } if (empty($has_states)) { @@ -1009,28 +1003,29 @@ function conditional_fields_evaluate_grouping($groups) { * The field form element in the current language. */ function conditional_fields_evaluate_dependencies($dependent, $form, $form_state) { - $dependencies = $form['#conditional_fields'][$dependent['#field_name']]['dependees']; + $entitydependencies = $form['#conditional_fields'][$dependent['#field_name']]['dependees']; $evaluated_dependees = array(); + foreach ($entitydependencies as $entity_id => $dependencies) { + foreach ($dependencies as $dependency_id => $dependency) { + // Extract field values from submitted values. + $dependee = $dependency['dependee']; + $dependee_parents = $form['#conditional_fields'][$dependee]['parents'][$entity_id][$dependency_id]; + + // We have the parents of the field, but depending on the entity type and + // the widget type, they may include additional elements that are actually + // part of the value. So we find the depth of the field inside the form + // structure and use the parents only up to that depth. + $dependee_parents_keys = array_flip($dependee_parents); + $dependee_parent = drupal_array_get_nested_value($form, array_slice($dependee_parents, 0, $dependee_parents_keys[$dependee])); + $values = conditional_fields_form_field_get_values($dependee_parent[$dependee], $form_state); + + // Remove the language key. + if (isset($dependee_parent[$dependee]['#language'], $values[$dependee_parent[$dependee]['#language']])) { + $values = $values[$dependee_parent[$dependee]['#language']]; + } - foreach ($dependencies as $dependency_id => $dependency) { - // Extract field values from submitted values. - $dependee = $dependency['dependee']; - $dependee_parents = $form['#conditional_fields'][$dependee]['parents']; - - // We have the parents of the field, but depending on the entity type and - // the widget type, they may include additional elements that are actually - // part of the value. So we find the depth of the field inside the form - // structure and use the parents only up to that depth. - $dependee_parents_keys = array_flip($dependee_parents); - $dependee_parent = drupal_array_get_nested_value($form, array_slice($dependee_parents, 0, $dependee_parents_keys[$dependee])); - $values = conditional_fields_form_field_get_values($dependee_parent[$dependee], $form_state); - - // Remove the language key. - if (isset($dependee_parent[$dependee]['#language'], $values[$dependee_parent[$dependee]['#language']])) { - $values = $values[$dependee_parent[$dependee]['#language']]; + $evaluated_dependees[$dependent['#field_name']][$dependency['options']['grouping']][] = conditional_fields_evaluate_dependency('edit', $values, $dependency['options']); } - - $evaluated_dependees[$dependent['#field_name']][$dependency['options']['grouping']][] = conditional_fields_evaluate_dependency('edit', $values, $dependency['options']); } return conditional_fields_evaluate_grouping($evaluated_dependees[$dependent['#field_name']]);