# This patch file was generated by NetBeans IDE # It uses platform neutral UTF-8 encoding and \n newlines. --- a/conditional_fields.module +++ b/conditional_fields.module @@ -211,6 +211,64 @@ } /** + * Implements hook_field_group_build_pre_render_alter() + * Have to wait for the group elements to be created - unfortunately this is after after_build + */ +function conditional_fields_field_group_build_pre_render_alter(&$element) { + // Have to re-process conditional fields over again for newly created + // fieldgroup elements (and existing elements moved to fieldgroup children) + + // If this isn't for a form (i.e., being called because the entity is being viewed), just return. + if ( !isset($element['#form_state']) || empty($element['#form_state']) ) { + return; + } + // $form_state is added to $form['#form_state'] via conditional_fields_form_after_build() + $form_state = $element['#form_state']; + $form_state['complete form'] = $element; + $form = &$form_state['complete form']; + // Loop through fieldgroups and fieldgroup children + $new_dependents = array(); + foreach ($form['#fieldgroups'] as $group_name => $group) { + // Call element_after_build for fieldgroup children + foreach ($group->children as $field_name) { + $parents = array_merge($group->array_parents, array($field_name)); + $field_element = &drupal_array_get_nested_value($form, $parents); + conditional_fields_element_after_build($field_element, $form_state, TRUE); + } + // Call element_after_build for fieldgroup element + $group_element = &drupal_array_get_nested_value($form, $group->array_parents); + $group_element['#field_name'] = $group_name; + // Make sure group element has an ID to select in drupal_process_states + $group_element['#id'] = isset($group_element['#id']) ? $group_element['#id'] : $group_name; + // Need to wrap markup type with a div with the ID, unless ID already present in prefix or markup + if (isset($group_element['#type']) && $group_element['#type'] == 'markup') { + if ((!isset($group_element['#prefix']) || !strstr($group_element['#prefix'], '#' . $group_element['#id'])) + && (!isset($group_element['#markup']) || !strstr($group_element['#markup'], '#' . $group_element['#id'])) + ) { + $group_element['#prefix'] = '
' . (isset($group_element['#prefix']) ? $group_element['#prefix'] : ''); + $group_element['#suffix'] = (isset($group_element['#suffix']) ? $group_element['#suffix'] : '') . '
'; + } + } + conditional_fields_element_after_build($group_element, $form_state, TRUE); + if (isset($form['#conditional_fields'][$group_name], $form['#conditional_fields'][$group_name]['dependees'])) { + $new_dependents[$group_name] = &$group_element; + } + } + + // Call form_after_build + $form = conditional_fields_form_after_build($form, $form_state, TRUE); + + // Call validation for group dependents + foreach ($new_dependents as $dependent_name => &$dependent) { + conditional_fields_dependent_validate($dependent, $form_state, $form); + } + // Call form validation + conditional_fields_form_validate($form, $form_state); + + $element = $form; +} + +/** * Processes form elements with dependencies. * * Just adds a #conditional_fields property to the form with the needed @@ -218,7 +276,7 @@ * - The fields #parents property. * - Field dependencies data. */ -function conditional_fields_element_after_build($element, &$form_state) { +function conditional_fields_element_after_build($element, &$form_state, $reprocess = FALSE) { // Some fields are wrapped in containers before processing. Wrapped data must // take precedence over the container, because Entity Translation and // possibly other modules add #field_name to the container as well. @@ -239,6 +297,10 @@ return $element; } + // Get fieldgroup name if field belongs to one + $group_name = isset($form['#group_children']) && isset($form['#group_children'][$field['#field_name']]) ? + $form['#group_children'][$field['#field_name']] : NULL; + // Some fields do not have entity type and bundle properties. In this case we // try to use the properties from the form. This is not an optimal solution, // since in case of fields in entities within entities they might not correspond, @@ -264,19 +326,32 @@ // 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])) { + if ($reprocess || !isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) { conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id); } } } + // Also attach dependent if element is a (first) child in a dependent fieldgroup + // and the dependency state isn't 'visible' or 'hidden' + // (this is done on the fieldgroup element itself) + if ($group_name && isset($dependencies['dependents'][$group_name]) ) { + $group_dependent = $dependencies['dependents'][$group_name]; + if (!in_array($group_dependent[key($group_dependent)]['options']['state'], array('visible', 'hidden'))) { + foreach ($group_dependent as $id => $dependency) { + if ($reprocess || !isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) { + conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id); + } + } + } + } // Attach dependee. // TODO: collect information about every element of the dependee widget, not // just the first encountered. This bottom-up approach would allow us to // 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])) { + if ($reprocess || !isset($form['#conditional_fields'][$field['#field_name']]['dependents'][$id])) { conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id); } } @@ -402,6 +477,7 @@ } if (isset($dependent_parents)) { $form['#conditional_fields'][$dependent['#field_name']]['field_parents'] = $dependent_parents; + $form['#conditional_fields'][$dependent['#field_name']]['array_parents'] = $dependent['#array_parents']; $form['#conditional_fields'][$dependent['#field_name']]['dependees'][$id] = array( 'dependee' => $dependee['#field_name'], 'options' => $options, @@ -426,6 +502,10 @@ return $form; } + // Set $form_state in $form so hook_field_group_build_pre_render_alter has access to it + // when it is invoked after after_build (when fieldgroup elements are attached to form) + $form['#form_state'] =& $form_state; + $effects = array(); $state_handlers = conditional_fields_states_handlers(); @@ -436,6 +516,12 @@ if (empty($dependent_info['dependees'])) { continue; } + if ( isset($dependent_info['options']['group']) && $dependent_info['options']['group'] ) { + $is_group = TRUE; + } + else { + $is_group = FALSE; + } $dependent_location = array_merge($dependent_info['field_parents'], array($dependent)); $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location); @@ -448,8 +534,14 @@ continue; } + // Skip this dependency if it's a group element and state is not visible/hidden + // (dependencies for other states are attached to fieldgroup children elements) + if (in_array($dependent, array_keys($form['#fieldgroups'])) && !in_array($dependency['options']['state'], array('visible', 'hidden'))) { + continue; + } $dependee_info = $form['#conditional_fields'][$dependee]; - $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']); + $dependee_location = $dependee_info['parents']; + $dependee_form_field = drupal_array_get_nested_value($form, $dependee_location); $options = $dependency['options']; // Load field edit behaviors. @@ -672,7 +764,12 @@ * @see conditional_fields_form_validate() */ function conditional_fields_dependent_validate($element, &$form_state, $form) { - $dependent = $element[$element['#language']]; + if (isset($element['#language'], $element[$element['#language']])) { + $dependent = $element[$element['#language']]; + } + else { + $dependent = $element; + } // Check if this field's dependencies were triggered. if (conditional_fields_evaluate_dependencies($dependent, $form, $form_state)) { @@ -693,7 +790,7 @@ // conditional_fields_form_validate(). $errors = form_get_errors(); - if ($errors) { + if ($errors && isset($dependent['#parents'])) { // group elements have no #parents key $error_key = implode('][', $dependent['#parents']); foreach ($errors as $name => $error) { // An error triggered by this field might have been set on a descendant @@ -722,7 +819,9 @@ */ function conditional_fields_field_get_values($element, $form_state) { // Fall back to #parents to support custom dependencies. - $parents = !empty($element['#field_parents']) ? $element['#field_parents'] : $element['#parents']; + // $parents = !empty($element['#field_parents']) ? $element['#field_parents'] : $element['#parents']; + // Should this be the other way around? Isn't #parents the parent keys in $form_state['values']? + $parents = isset($element['#parents']) ? $element['#parents'] : $element['#field_parents']; return drupal_array_get_nested_value($form_state['values'], $parents); } @@ -846,11 +945,10 @@ $evaluated_dependents = array(); foreach ($dependencies['dependents'] as $dependent => $dependency) { - if (empty($build[$dependent]['#access'])) { - continue; - } - foreach ($dependency as $dependency_options) { + if (empty($build[$dependent]['#access']) && $dependency_options['type'] != 'group') { + continue 2; + } $dependee = $dependency_options['dependee']; $options = $dependency_options['options']; @@ -923,7 +1021,12 @@ case CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE: // Hide the dependent if it is not triggered. if (!$is_triggered) { - $build[$dependent]['#access'] = FALSE; + if ( $dependency_options['type'] == 'group' ) { + conditional_fields_hide_group($build, $dependent); + } + else { + $build[$dependent]['#access'] = FALSE; + } } break; @@ -934,7 +1037,12 @@ case CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN: // Hide the dependent if the dependee is not viewable and the dependency is not triggered. if ($is_orphan && !$is_triggered) { - $build[$dependent]['#access'] = FALSE; + if ( $dependency_options['type'] == 'group' ) { + conditional_fields_hide_group($build, $dependent); + } + else { + $build[$dependent]['#access'] = FALSE; + } } break; @@ -976,6 +1084,49 @@ } /** + * Helper function to hide a group. + * @param array $build The build array. + * @param string $dependent The group name to be hidden; + */ +function conditional_fields_hide_group(&$build, $dependent) { + $all_children = _conditional_fields_get_all_progeny($build['#group_children'], $build['#groups'], $dependent); + foreach ( $all_children as $child => $parent ) { + if ( in_array($child, $build['#groups']) ) { + unset($build['#groups'][$child]); + } + else { + $build[$child]['#access'] = FALSE; + } + unset($build['#group_children'][$child]); + if ( isset($build['#groups'][$parent]) ) { + unset($build['#groups'][$parent]); + } + } + unset($build['#groups'][$dependent]); + unset($build['#fieldgroups'][$dependent]); +} + +/** + * Helper function to recursively find all progeny of a group to deal with nested groups. + * @param array $elements Array of all fields and groups that are in a group. + * @param array $groups Array of all groups. + * @param string $parent The parent group for which its children are being sought. + * @return array Children as keys, and the parent group as the values. + */ +function _conditional_fields_get_all_progeny($elements, $groups, $parent) { + $children = array(); + foreach ( $elements as $child => $group ) { + if ( in_array($child, $groups) ) { + $children += _conditional_fields_get_all_progeny($elements, $groups, $child); + } + if ( $group == $parent ) { + $children[$child] = $group; + } + } + return $children; +} + +/** * Evaluates an array with 'AND', 'OR' and 'XOR' groupings, * each containing a list of boolean values. */ @@ -1274,7 +1425,7 @@ * * The result can be filtered by providing an entity type and a bundle name. */ -function conditional_fields_load_dependencies($entity_type = NULL, $bundle = NULL) { +function conditional_fields_load_dependencies($entity_type = NULL, $bundle = NULL, $group_view_mode = 'form') { // Use the advanced drupal_static() pattern. static $dependencies; if (!isset($dependencies)) { @@ -1291,49 +1442,62 @@ } $default_options = conditional_fields_dependency_default_options(); + // Get dependent fields and groupd separately, as which will be 'joined' depends + // on the options['group'] value. + $dependent_fields_select = db_select('field_config_instance', 'fci') + ->fields('fci', array('id', 'field_name')); + $dependent_groups_select = db_select('field_group', 'fg') + ->fields('fg', array('id', 'group_name')) + ->condition('mode', $group_view_mode); + $select = db_select('conditional_fields', 'cf') - ->fields('cf', array('id', 'options')) - ->orderBy('cf.dependent'); + ->fields('cf', array('id', 'dependent', 'options')); $fci_depende = $select->join('field_config_instance', 'fci_dependee', 'cf.dependee = fci_dependee.id'); - $fci_dependent = $select->join('field_config_instance', 'fci_dependent', 'cf.dependent = fci_dependent.id'); $select->addField($fci_depende, 'field_name', 'dependee'); - $select->addField($fci_dependent, 'field_name', 'dependent'); $select->addField($fci_depende, 'entity_type'); $select->addField($fci_depende, 'bundle'); if ($entity_type) { - $select->condition( - db_and() - ->condition('fci_dependee.entity_type', $entity_type) - ->condition('fci_dependent.entity_type', $entity_type) - ); + $dependent_fields_select->condition('fci.entity_type', $entity_type); + $dependent_groups_select->condition('fg.entity_type', $entity_type); + $select->condition('fci_dependee.entity_type', $entity_type); } if ($bundle) { - $select->condition( - db_and() - ->condition('fci_dependee.bundle', $bundle) - ->condition('fci_dependent.bundle', $bundle) - ); + $dependent_fields_select->condition('fci.bundle', $bundle); + $dependent_groups_select->condition('fg.bundle', $bundle); + $select->condition('fci_dependee.bundle', $bundle); } $result = $select->execute(); + $dependent_fields = $dependent_fields_select->execute()->fetchAllAssoc('id'); + $dependent_groups = $dependent_groups_select->execute()->fetchAllAssoc('id'); foreach ($result as $dependency) { $result_entity_type = $entity_type ? $entity_type : $dependency->entity_type; $result_bundle = $bundle ? $bundle : $dependency->bundle; $options = unserialize($dependency->options); + if ( isset($options['group']) && $options['group'] ) { + $dependency->dependent = $dependent_groups[$dependency->dependent]->group_name; + $dependency->type = 'group'; + } + else { + $dependency->dependent =$dependent_fields[$dependency->dependent]->field_name; + $dependency->type = 'field'; + } $options += $default_options; $dependencies[$result_entity_type][$result_bundle]['dependents'][$dependency->dependent][$dependency->id] = array( 'dependee' => $dependency->dependee, 'options' => $options, + 'type' => $dependency->type, ); $dependencies[$result_entity_type][$result_bundle]['dependees'][$dependency->dependee][$dependency->id] = array( 'dependent' => $dependency->dependent, 'options' => $options, + 'type' => $dependency->type, ); } } --- a/includes/conditional_fields.admin.inc +++ b/includes/conditional_fields.admin.inc @@ -106,6 +106,15 @@ function conditional_fields_dependency_add_form($form, &$form_state, $entity_type, $bundle_name) { $form = array(); $instances = field_info_instances($entity_type, $bundle_name); + // Get field groups on entity + $groups = field_group_info_groups($entity_type, $bundle_name); + $groups = isset($groups['form']) ? $groups['form'] : array(); + foreach ($groups as $group_name => $group) { + $groups[$group_name] = (array)$group; + } + // New merged array of fields & groups for dependents + // Dependees should only be fields ($instances) + $instances_groups = $instances + $groups; if (count($instances) < 2) { $form['no_fields'] = array( @@ -163,7 +172,7 @@ // first row they will appear grouped. if ($first_row == TRUE) { $form['table']['dependencies'][$id]['dependent'] = array( - '#markup' => check_plain($instances[$dependent]['label']) . ' (' . $dependent . ')', + '#markup' => check_plain($instances_groups[$dependent]['label']) . ' (' . $dependent . ')', '#rowspan' => $dependee_count, ); @@ -191,7 +200,7 @@ $row['description']['#colspan'] = 2; } - $row['description']['#markup'] = conditional_fields_dependency_description($instances[$dependency['dependee']]['label'], $instances[$dependent]['label'], $dependency['options']); + $row['description']['#markup'] = conditional_fields_dependency_description($instances[$dependency['dependee']]['label'], $instances_groups[$dependent]['label'], $dependency['options']); $row['edit'] = array( '#type' => 'link', @@ -221,6 +230,15 @@ asort($fields); + // Build list of available groups. Preface each id with a 'g' to avoid collission + // with field ids, and to identify it as a group. + $fieldgroups = array(); + foreach ($groups as $group_name => $group) { + $fieldgroups['g' . $group['id']] = check_plain($group['label'] . ' (' . $group['group_name'] . ')'); + } + + asort($fieldgroups); + // Build list of states. $states = array_map('drupal_strtolower', conditional_fields_states()); @@ -236,7 +254,7 @@ '#title' => t('Dependent'), '#title_display' => 'invisible', '#description' => t('Dependent'), - '#options' => $fields, + '#options' => $fields + $fieldgroups, '#prefix' => '
' . t('Add new dependency') . '
', ), 'dependee' => array( @@ -302,6 +320,12 @@ 'state' => $form_state['values']['state'], 'condition' => $form_state['values']['condition'] ); + // Determine if the dependent is a group, and adjust the ID and save the group + // indicator in options. + if ( substr($form_state['values']['dependent'], 0, 1) == 'g' ) { + $options['group'] = TRUE; + $form_state['values']['dependent'] = substr($form_state['values']['dependent'], 1); + } if (!$id = conditional_fields_dependency_insert($form_state['values']['dependee'], $form_state['values']['dependent'], $options)) { drupal_set_message(t('There was an error while trying to create the dependency.'), 'error'); @@ -800,6 +824,11 @@ } } + // Save the group indicator in options. + if ( isset($form['#dependency']['options']['group']) && $form['#dependency']['options']['group'] ) { + $dependency['options']['group'] = $form['#dependency']['options']['group']; + } + conditional_fields_dependency_update($dependency); drupal_set_message(t('Saved dependency configuration.')); } --- a/includes/conditional_fields.features.inc +++ b/includes/conditional_fields.features.inc @@ -111,7 +111,21 @@ } $entity_instances = $instances[$field['entity']][$field['bundle']]; $dependee_id = $entity_instances[$field['dependee']]['id']; + $dependent_id = 0; + if (array_key_exists($field['dependent'], $entity_instances)) { $dependent_id = $entity_instances[$field['dependent']]['id']; + } + elseif (module_exists('field_group')) { + $entity_groups = field_group_info_groups($field['entity'], $field['bundle'], 'form'); + if (array_key_exists($field['dependent'], $entity_groups)) { + if (!$entity_groups[$field['dependent']]->id) { + field_group_group_save($entity_groups[$field['dependent']]); + $identifier = $entity_groups[$field['dependent']]->identifier; + $entity_groups[$field['dependent']] = field_group_load_field_group_by_identifier($identifier); + } + $dependent_id = $entity_groups[$field['dependent']]->id; + } + } conditional_fields_dependency_insert($dependee_id, $dependent_id, $field['options']); } }