diff --git a/conditional_fields.module b/conditional_fields.module index 5b50edd..7ead811 100644 --- a/conditional_fields.module +++ b/conditional_fields.module @@ -247,6 +247,9 @@ function conditional_fields_element_after_build($element, &$form_state) { $entity_type = $field['#entity_type']; $bundle = $field['#bundle']; } + elseif (isset($field[0]['#entity_type']) && $bundle = _conditional_fields_field_parent_bundle($field)) { + $entity_type = $field[0]['#entity_type']; + } elseif (isset($form['#entity_type'], $form['#bundle'])) { $entity_type = $form['#entity_type']; $bundle = $form['#bundle']; @@ -261,11 +264,13 @@ function conditional_fields_element_after_build($element, &$form_state) { return $element; } - // Attach dependent. + $field_parents_key = implode(':', $field['#field_parents']); + if (isset($dependencies['dependents'][$field['#field_name']])) { + // Attach dependent. 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); + if (!isset($form['#conditional_fields'][$field_parents_key][$field['#field_name']]['dependees'][$id])) { + conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id, $field_parents_key); } } } @@ -276,8 +281,8 @@ 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); + if (!isset($form['#conditional_fields'][$field_parents_key][$field['#field_name']]['dependents'][$id])) { + conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id, $field_parents_key); } } } @@ -303,6 +308,8 @@ function conditional_fields_element_after_build($element, &$form_state) { * the array are #field_name and #field_parents. * @param $options * An array of dependency options with the following key/value pairs: + * (You don't need to manually set all these options, because default + * settings are always provided.) * - state: The state applied to the dependent when the dependency is * triggered. See conditional_fields_states() for available states. * - condition: The condition for the dependency to be triggered. See @@ -348,14 +355,16 @@ function conditional_fields_element_after_build($element, &$form_state) { * where the keys are role ids and the values are arrays with the same * structure of 'element_edit'. * - selector: (optional) Custom jQuery selector for the dependee. + * Note that you don't need to manually set all these options, since default + * settings are always provided. * @param $id * (internal use) The identifier for the dependency. Omit this parameter when * attaching a custom dependency. - * - * Note that you don't need to manually set all these options, since default - * settings are always provided. + * @param $field_parents_key + * (internal use) The field parents structure of the dependent field. Omit + * this parameter when attaching a custom dependency. */ -function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $options, $id = 0) { +function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $options, $id = 0, $field_parents_key = '') { $options += conditional_fields_dependency_default_options(); // The absence of the $id parameter identifies a custom dependency. @@ -380,14 +389,18 @@ function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $op $id = $current_id; $current_id++; } + + if (!$field_parents_key) { + $field_parents_key = implode(':', $dependent['#field_parents']); + } } // Attach dependee. // Use the #array_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['#array_parents']; - $form['#conditional_fields'][$dependee['#field_name']]['dependents'][$id] = array( + $form['#conditional_fields'][$field_parents_key][$dependee['#field_name']]['parents'] = $dependee['#array_parents']; + $form['#conditional_fields'][$field_parents_key][$dependee['#field_name']]['dependents'][$id] = array( 'dependent' => $dependent['#field_name'], 'options' => $options, ); @@ -401,8 +414,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'][$field_parents_key][$dependent['#field_name']]['field_parents'] = $dependent_parents; + $form['#conditional_fields'][$field_parents_key][$dependent['#field_name']]['dependees'][$id] = array( 'dependee' => $dependee['#field_name'], 'options' => $options, ); @@ -430,112 +443,114 @@ function conditional_fields_form_after_build($form, &$form_state) { $state_handlers = conditional_fields_states_handlers(); // Cycle all dependents. - foreach ($form['#conditional_fields'] as $dependent => $dependent_info) { - $states = array(); + foreach ($form['#conditional_fields'] as $parent_dependent_key => $parent_dependent_info) { + foreach ($parent_dependent_info as $dependent => $dependent_info) { + $states = array(); - if (empty($dependent_info['dependees'])) { - continue; - } + 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); + $dependent_location = array_merge($dependent_info['field_parents'], array($dependent)); + $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location); - // Cycle the dependant's dependees. - foreach ($dependent_info['dependees'] as $dependency) { - $dependee = $dependency['dependee']; + // Cycle the dependant's dependees. + foreach ($dependent_info['dependees'] as $dependency) { + $dependee = $dependency['dependee']; - if (empty($form['#conditional_fields'][$dependee])) { - continue; - } + if (empty($form['#conditional_fields'][$parent_dependent_key][$dependee])) { + continue; + } - $dependee_info = $form['#conditional_fields'][$dependee]; - $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']); - $options = $dependency['options']; + $dependee_info = $form['#conditional_fields'][$parent_dependent_key][$dependee]; + $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']); + $options = $dependency['options']; - // 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); - } + // 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); + } - // 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; + // 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 ($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]); + unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]); + 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]); - unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]); - 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_NOT) { - // NOT behaves like OR with switched state. - $options['state'] = strpos($options['state'], '!') === 0 ? drupal_substr($options['state'], 1) : '!' . $options['state']; + elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND) { + $values[$options['condition']] = count($options['values']) == 1 ? $options['values'][0] : $options['values']; } + 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; + $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); + // 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); + } } } } @@ -552,83 +567,83 @@ function conditional_fields_form_after_build($form, &$form_state) { // dependencies' states with the same selector. $states[$key][$options['grouping']][$selector][] = $constraint; } - } - // 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; + // 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'], + ); } - $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]); + // 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]); + } } - } - // Execute custom behaviors callbacks. - if (!empty($behaviors)) { - foreach ($behaviors as $behavior) { - $behavior($form, $form_state, $dependent, $dependent_info); + // Execute custom behaviors callbacks. + if (!empty($behaviors)) { + foreach ($behaviors as $behavior) { + $behavior($form, $form_state, $dependent, $dependent_info); + } } - } - unset($behaviors); + unset($behaviors); - if (empty($states)) { - continue; - } + if (empty($states)) { + continue; + } - // Save the modified field back into the form. - drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field); + // Save the modified field back into the form. + drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field); - // 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); + // 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']; } - // '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); + // 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; } - // '2' as a string so that we get an object. - $states_new[$state_key]['2'] = $xor; } - } - $states = $states_new; + $states = $states_new; - // Add the #states property to the dependent field. - drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states); + // Add the #states property to the dependent field. + drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states); - $has_states = TRUE; + $has_states = TRUE; + } } if (empty($has_states)) { @@ -1000,7 +1015,8 @@ 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']; + $field_parents_key = implode(':', $dependent['#field_parents']); + $dependencies = $form['#conditional_fields'][$field_parents_key][$dependent['#field_name']]['dependees']; $evaluated_dependees = array(); foreach ($dependencies as $dependency_id => $dependency) { @@ -1021,6 +1037,10 @@ function conditional_fields_evaluate_dependencies($dependent, $form, $form_state * Extracts field values from a submitted form. */ function conditional_fields_field_form_get_values($field_name, $form, $form_state) { + if (empty($form['#conditional_fields'][$field_name])) { + return array(); + } + $field_parents = $form['#conditional_fields'][$field_name]['parents']; // We have the parents of the field, but depending on the entity type and @@ -2027,3 +2047,19 @@ function conditional_fields_features_api() { ), ); } + +/** + * Returns a field's immediate parent bundle. + * + * @param array $field A field structure extracted from a form array + * @return string|bool The parent's bundle or FALSE if not found + */ +function _conditional_fields_field_parent_bundle($field) { + $languages = array_keys(language_list()); + foreach (array_reverse($field[0]['#field_parents']) as $parent) { + if (!is_numeric($parent) && $parent != LANGUAGE_NONE && !in_array($parent, $languages)) { + return $parent; + } + } + return FALSE; +}