PageRenderTime 43ms CodeModel.GetById 3ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/sites/all/modules/contrib/conditional_fields/conditional_fields.module

https://bitbucket.org/antisocnet/drupal
Unknown | 1993 lines | 1760 code | 233 blank | 0 comment | 0 complexity | aa76a8d242b83c6a6dc0deb1b9ed14e4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 * @file
   4 * Define dependencies between fields based on their states and values.
   5 *
   6 * Conditional Fields for Drupal 7 is basically an user interface for the States
   7 * API, plus the ability to hide fields on certain conditions when viewing
   8 * content.
   9 */
  10
  11/**
  12 * Dependency is triggered if the dependee has a certain value.
  13 */
  14define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET', 1);
  15
  16/**
  17 * Dependency is triggered if the dependee has all values.
  18 */
  19define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND', 2);
  20
  21/**
  22 * Dependency is triggered if the dependee has any of the values.
  23 */
  24define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR', 3);
  25
  26/**
  27 * Dependency is triggered if the dependee has only one of the values.
  28 */
  29define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR', 4);
  30
  31/**
  32 * Dependency is triggered if the dependee does not have any of the values.
  33 */
  34define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT', 5);
  35
  36/**
  37 * Dependency is triggered if the dependee values match a regular expression.
  38 */
  39define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX', 6);
  40
  41/**
  42 * Field view setting. Dependent is shown only if the dependency is triggered.
  43 */
  44define('CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE', 1);
  45
  46/**
  47 * Field view setting. Dependent is shown only if the dependee is shown as well.
  48 */
  49define('CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN', 2);
  50
  51/**
  52 * Field view setting. Dependent is highlighted if the dependency is not
  53 * triggered.
  54 */
  55define('CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT', 3);
  56
  57/**
  58 * Field view setting. Dependent has a textual description of the dependency.
  59 */
  60define('CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE', 4);
  61
  62/**
  63 * Field view setting. Dependent is shown only if the dependee is shown as well
  64 * and the dependency evaluates to TRUE.
  65 */
  66define('CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN', 5);
  67
  68/**
  69 * Field edit setting. Dependent is shown only if the dependee is shown as well.
  70 */
  71define('CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN', 1);
  72
  73/**
  74 * Field edit setting. Dependent is shown only if the dependee is shown as well
  75 * and the dependency evaluates to TRUE.
  76 */
  77define('CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN', 2);
  78
  79/**
  80 * Field edit setting. Dependent is reset to its default values if the
  81 * dependency was not triggered when the form is submitted.
  82 */
  83define('CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED', 3);
  84
  85/**
  86 * Implements hook_permission().
  87 */
  88function conditional_fields_permission() {
  89  return array(
  90    'administer dependencies' => array(
  91      'title' => t('Administer dependencies'),
  92      'description' => t('View, edit and delete field dependencies.'),
  93    ),
  94  );
  95}
  96
  97/**
  98 * Implements hook_menu().
  99 */
 100function conditional_fields_menu() {
 101  $items = array();
 102
 103  // Ensure the following is not executed until field_bundles is working and
 104  // tables are updated. Needed to avoid errors on initial installation.
 105  if (defined('MAINTENANCE_MODE')) {
 106    return $items;
 107  }
 108
 109  $items['admin/structure/dependencies'] = array(
 110    'title' => 'Field dependencies',
 111    'description' =>  'Administer field dependencies for the site.',
 112    'page callback' => 'conditional_fields_dependencies_overview_page',
 113    'access arguments' => array('administer dependencies'),
 114    'file' => 'includes/conditional_fields.admin.inc',
 115  );
 116
 117  $items['admin/structure/dependencies/overview'] = array(
 118    'title' => 'Overview',
 119    'type' => MENU_DEFAULT_LOCAL_TASK,
 120    'weight' => 1,
 121  );
 122
 123  $items['admin/structure/dependencies/edit/%conditional_fields_dependency'] = array(
 124    'title' => 'Edit dependency',
 125    'page callback' => 'drupal_get_form',
 126    'page arguments' => array('conditional_fields_dependency_edit_form', 4),
 127    'access arguments' => array('administer dependencies'),
 128    'file' => 'includes/conditional_fields.admin.inc',
 129  );
 130
 131  $items['admin/structure/dependencies/delete/%conditional_fields_dependency'] = array(
 132    'title' => 'Delete dependency',
 133    'page callback' => 'drupal_get_form',
 134    'page arguments' => array('conditional_fields_dependency_delete_form', 4),
 135    'access arguments' => array('administer dependencies'),
 136    'file' => 'includes/conditional_fields.admin.inc',
 137  );
 138
 139  // Some of the following code is copied from field_ui_menu().
 140
 141  // Create tabs for all possible bundles.
 142  foreach (entity_get_info() as $entity_type => $entity_info) {
 143    if ($entity_info['fieldable']) {
 144      $items["admin/structure/dependencies/$entity_type"] = array(
 145        'title' => $entity_info['label'],
 146        'page arguments' => array(NULL, 3),
 147        'access arguments' => array('administer dependencies'),
 148        'type' => MENU_LOCAL_TASK,
 149        'weight' => 2,
 150      );
 151
 152      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
 153        if (module_exists('field_ui') && isset($bundle_info['admin'])) {
 154          // Extract path information from the bundle and replace any "magic"
 155          // wildcard with a normal one.
 156          $path = preg_replace('/(%[a-z0-9_]*)/', '%', $bundle_info['admin']['path']);
 157
 158          if (isset($bundle_info['admin']['bundle argument'])) {
 159            $bundle_pos = $bundle_info['admin']['bundle argument'];
 160          }
 161          else {
 162            $bundle_pos = $bundle_name;
 163          }
 164
 165          $items["$path/dependencies"] = array(
 166            'title' => $entity_type == 'comment' ? 'Comment dependencies' : 'Manage dependencies',
 167            'page callback' => 'conditional_fields_dependencies_overview_page',
 168            'page arguments' => array($bundle_pos, $entity_type),
 169            'type' => MENU_LOCAL_TASK,
 170            'weight' => $entity_type == 'comment' ? 4 : 2,
 171            'file' => 'includes/conditional_fields.admin.inc',
 172            'access arguments' => array('administer dependencies'),
 173          );
 174        }
 175      }
 176    }
 177  }
 178
 179  return $items;
 180}
 181
 182/**
 183 * Implements hook_forms().
 184 *
 185 * Maps all dependency add forms to the same callback.
 186 */
 187function conditional_fields_forms() {
 188  foreach (entity_get_info() as $entity_type => $entity_info) {
 189    if ($entity_info['fieldable']) {
 190      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
 191        $forms['conditional_fields_dependency_add_form_' . $entity_type . '_' . $bundle_name] = array(
 192          'callback' => 'conditional_fields_dependency_add_form',
 193          'callback arguments' => array($entity_type, $bundle_name),
 194        );
 195      }
 196    }
 197  }
 198
 199  return $forms;
 200}
 201
 202/**
 203 * Implements hook_js_alter().
 204 *
 205 * Overrides core states API with a patched version that allows multiple
 206 * conditions and OR/XOR logic.
 207 */
 208function conditional_fields_js_alter(&$javascript) {
 209  // Since Drupal 7.14, states.js includes the OR/XOR patch.
 210  if (isset($javascript['misc/states.js']) && version_compare(VERSION, '7.14', '<')) {
 211    $javascript['misc/states.js']['data'] = drupal_get_path('module', 'conditional_fields') . '/js/states.js';
 212  }
 213}
 214
 215/**
 216 * Implements hook_element_info_alter().
 217 * Adds an #after_build function to all form elements.
 218 */
 219function conditional_fields_element_info_alter(&$types) {
 220  foreach ($types as $type => $info) {
 221    $types[$type]['#after_build'][] = 'conditional_fields_element_after_build';
 222  }
 223}
 224
 225/**
 226 * Processes form elements with dependencies.
 227 *
 228 * Just adds a #conditional_fields property to the form with the needed
 229 * data, which is used later in conditional_fields_form_after_build():
 230 * - The fields #parents property.
 231 * - Field dependencies data.
 232 */
 233function conditional_fields_element_after_build($element, &$form_state) {
 234  // Ensure that the element is a field.
 235  if (isset($element['#field_name'])) {
 236    $field = $element;
 237  }
 238  elseif (isset($element['#language'], $element[$element['#language']], $element[$element['#language']]['#field_name'])) {
 239    // Some fields are wrapped in containers before processing.
 240    $field = $element[$element['#language']];
 241  }
 242  else {
 243    return $element;
 244  }
 245
 246  $form = &$form_state['complete form'];
 247
 248  // Avoid processing fields in fields_ui administration pages.
 249  if (drupal_substr($form['#form_id'], 0, 9) == 'field_ui_') {
 250    return $element;
 251  }
 252
 253  // Some fields do not have entity type and bundle properties. In this case we
 254  // try to use the properties from the form. This is not an optimal solution,
 255  // since in case of fields in entities within entities they might not correspond,
 256  // and their dependencies will not be loaded.
 257  if (isset($field['#entity_type'], $field['#bundle'])) {
 258    $entity_type = $field['#entity_type'];
 259    $bundle = $field['#bundle'];
 260  }
 261  elseif (isset($form['#entity_type'], $form['#bundle'])) {
 262    $entity_type = $form['#entity_type'];
 263    $bundle = $form['#bundle'];
 264  }
 265  else {
 266    return $element;
 267  }
 268
 269  $dependencies = conditional_fields_load_dependencies($entity_type, $bundle);
 270
 271  if (!$dependencies) {
 272    return $element;
 273  }
 274
 275  // Attach dependent.
 276  if (isset($dependencies['dependents'][$field['#field_name']])) {
 277    foreach ($dependencies['dependents'][$field['#field_name']] as $id => $dependency) {
 278      if (!isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) {
 279        conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id);
 280      }
 281    }
 282  }
 283
 284  // Attach dependee.
 285  // TODO: collect information about every element of the dependee widget, not
 286  // just the first encountered. This bottom-up approach would allow us to
 287  // define per-element sets of dependency values.
 288  if (isset($dependencies['dependees'][$field['#field_name']])) {
 289    foreach ($dependencies['dependees'][$field['#field_name']] as $id => $dependency) {
 290      if (!isset($form['#conditional_fields'][$field['#field_name']]['dependents'][$id])) {
 291        conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id);
 292      }
 293    }
 294  }
 295
 296  return $element;
 297}
 298
 299/**
 300 * Attaches a single dependency to a form.
 301 *
 302 * Call this function when defining or altering a form to create dependencies
 303 * dynamically.
 304 *
 305 * @param $form
 306 *   The form where the dependency is attached.
 307 * @param $dependee
 308 *   The dependee field form element. Either a string identifying the element
 309 *   key in the form, or a fully built field array. Actually used properties of
 310 *   the array are #field_name and #parents.
 311 * @param $dependent
 312 *   The dependent field form element. Either a string identifying the element
 313 *   key in the form, or a fully built field array. Actually used properties of
 314 *   the array are #field_name and #field_parents.
 315 * @param $options
 316 *   An array of dependency options with the following key/value pairs:
 317 *   - state: The state applied to the dependent when the dependency is
 318 *     triggered. See conditional_fields_states() for available states.
 319 *   - condition: The condition for the dependency to be triggered. See
 320 *     conditional_fields_conditions() for available conditions.
 321 *   - values_set: One of the following constants:
 322 *     - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET: Dependency is
 323 *       triggered if the dependee has a certain value defined in 'value'.
 324 *     - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND: Dependency is triggered if
 325 *       the dependee has all the values defined in 'values'.
 326 *     - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR: Dependency is triggered if the
 327 *       dependee has any of the values defined in 'values'.
 328 *     - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR: Dependency is triggered if
 329 *       the dependee has only one of the values defined in 'values'.
 330 *     - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT: Dependency is triggered if
 331 *       the dependee does not have any of the values defined in 'values'.
 332 *   - value: The value to be tested when 'values_set' is
 333 *     CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET. An associative array with
 334 *     the same structure of the dependee field values as found in
 335 *     $form_states['values] when the form is submitted. You can use
 336 *     field_default_extract_form_values() to extract this array.
 337 *   - values: The array of values to be tested when 'values_set' is not
 338 *     CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET.
 339 *   - value_form: An associative array with the same structure of the dependee
 340 *     field values as found in $form_state['input']['value']['field'] when the
 341 *     form is submitted.
 342 *   - effect: The jQuery effect associated to the state change. See
 343 *     conditional_fields_effects() for available effects and options.
 344 *   - effect_options: The options for the active effect.
 345 *   - element_view: An associative array of field view behaviors with
 346 *     CONDITIONAL_FIELDS_FIELD_VIEW_* constants as keys and the same constants
 347 *     as values for enabled behaviors and 0 for disabled behaviors.
 348 *     See conditional_fields_behaviors() for descriptions.
 349 *   - element_view_per_role: Set to 1 to activate field view settings per role.
 350 *   - element_view_roles: An associative array of field view settings per role
 351 *     where the keys are role ids and the values are arrays with the same
 352 *     structure of 'element_view'.
 353 *   - element_edit: An associative array of field edit behaviors with
 354 *     CONDITIONAL_FIELDS_FIELD_EDIT_* constants as keys and the same constants
 355 *     as values for enabled behaviors and 0 for disabled behaviors.
 356 *     See conditional_fields_behaviors() for descriptions.
 357 *   - element_edit_per_role: Set to 1 to activate field edit settings per role.
 358 *   - element_edit_roles: An associative array of field edit settings per role
 359 *     where the keys are role ids and the values are arrays with the same
 360 *     structure of 'element_edit'.
 361 *   - selector: (optional) Custom jQuery selector for the dependee.
 362 * @param $id
 363 *   (internal use) The identifier for the dependency. Omit this parameter when
 364 *   attaching a custom dependency.
 365 *
 366 *   Note that you don't need to manually set all these options, since default
 367 *   settings are always provided.
 368 */
 369function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $options, $id = 0) {
 370  $options += conditional_fields_dependency_default_options();
 371
 372  // The absence of the $id parameter identifies a custom dependency.
 373  if (!$id) {
 374    // String values are accepted to simplify usage of this function with custom
 375    // forms.
 376    if (is_string($dependee) && is_string($dependent)) {
 377      $dependee = array(
 378        '#field_name' => $dependee,
 379        '#parents'    => array($dependee),
 380      );
 381      $dependent = array(
 382        '#field_name'    => $dependent,
 383        '#field_parents' => array($dependent),
 384      );
 385
 386      // Custom dependencies have automatically assigned a progressive id.
 387      static $current_id;
 388      if (!$current_id) {
 389        $current_id = 1;
 390      }
 391      $id = $current_id;
 392      $current_id++;
 393    }
 394  }
 395
 396  // Attach dependee.
 397  // Use the #parents property of the dependee instead of #field_parents since
 398  // we will need access to the full structure of the widget.
 399  if (isset($dependee['#parents'])) {
 400    $form['#conditional_fields'][$dependee['#field_name']]['parents'] = $dependee['#parents'];
 401    $form['#conditional_fields'][$dependee['#field_name']]['dependents'][$id] = array(
 402      'dependent' => $dependent['#field_name'],
 403      'options'   => $options,
 404    );
 405  }
 406
 407  // Attach dependent.
 408  if (isset($dependent['#field_parents'])) {
 409    $dependent_parents = $dependent['#field_parents'];
 410  }
 411  elseif (isset($dependent['#parents'])) {
 412    $dependent_parents = $dependent['#parents'];
 413  }
 414  if (isset($dependent_parents)) {
 415    $form['#conditional_fields'][$dependent['#field_name']]['field_parents'] = $dependent_parents;
 416    $form['#conditional_fields'][$dependent['#field_name']]['dependees'][$id] = array(
 417      'dependee' => $dependee['#field_name'],
 418      'options'  => $options,
 419    );
 420  }
 421
 422  // Actual processing is done in conditional_fields_form_after_build().
 423  // Append the property so the callback runs last.
 424  _conditional_fields_element_add_property($form, '#after_build', 'conditional_fields_form_after_build', 'append');
 425}
 426
 427/**
 428 * after_build callback for forms with dependencies.
 429 *
 430 * Builds and attaches #states properties to dependent fields, adds additional
 431 * visual effects handling to the States API and attaches a validation callback
 432 * to the form that handles validation of dependent fields.
 433 */
 434function conditional_fields_form_after_build($form, &$form_state) {
 435  // Dependencies data is attached in conditional_fields_element_after_build().
 436  if (empty($form['#conditional_fields'])) {
 437    return $form;
 438  }
 439
 440  $effects = array();
 441  $state_handlers = conditional_fields_states_handlers();
 442
 443  // Cycle all dependents.
 444  foreach ($form['#conditional_fields'] as $dependent => $dependent_info) {
 445    $states = array();
 446
 447    if (empty($dependent_info['dependees'])) {
 448      continue;
 449    }
 450
 451    $dependent_location = array_merge($dependent_info['field_parents'], array($dependent));
 452    $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location);
 453
 454    // Cycle the dependant's dependees.
 455    foreach ($dependent_info['dependees'] as $dependency) {
 456      $dependee = $dependency['dependee'];
 457
 458      if (empty($form['#conditional_fields'][$dependee])) {
 459        continue;
 460      }
 461
 462      $dependee_info = $form['#conditional_fields'][$dependee];
 463      $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']);
 464      $options = $dependency['options'];
 465
 466      // Load field edit behaviors.
 467      // If this dependent has multiple dependees, only the logic of the first
 468      // dependency will be taken into account.
 469      if (!isset($behaviors)) {
 470        $behaviors = conditional_fields_field_behaviors('edit', $options);
 471      }
 472
 473      // Determine if the dependee is in the form.
 474      if (empty($dependee_form_field) || (isset($dependee_form_field['#access']) && $dependee_form_field['#access'] == FALSE)) {
 475        // Apply orphan dependent behaviors.
 476        /*
 477        if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN, $behaviors)) {
 478          // TODO
 479          $is_triggered = TRUE;
 480
 481          if ($is_orphan && !$is_triggered) {
 482            $form[$dependent]['#access'] = FALSE;
 483          }
 484        }
 485        */
 486        if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN, $behaviors)) {
 487          $dependent_form_field['#access'] = FALSE;
 488        }
 489        unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]);
 490        unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]);
 491        continue;
 492      }
 493
 494      unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]);
 495      unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]);
 496
 497      // Build a jQuery selector if it was not overridden by a custom value.
 498      // Note that this may be overridden later by a state handler.
 499      if (!$options['selector']) {
 500        $options['selector'] = conditional_fields_field_selector($dependee_form_field);
 501      }
 502      else {
 503        // Replace the language placeholder in the selector with current language.
 504        $options['selector'] = str_replace('%lang', $dependee_form_field['#language'], $options['selector']);
 505      }
 506
 507      if ($options['condition'] != 'value') {
 508        // Conditions different than "value" are always evaluated against TRUE.
 509        $state = array($options['state'] => array($options['selector'] => array($options['condition'] => TRUE)));
 510      }
 511      else {
 512        // Build the values that trigger the dependency.
 513        $values = array();
 514
 515        if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) {
 516          $values[$options['condition']] = $options['value_form'];
 517        }
 518        elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) {
 519          $values[$options['condition']] = $options['value'];
 520        }
 521        elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND) {
 522          $values[$options['condition']] = count($options['values']) == 1 ? $options['values'][0] : $options['values'];
 523        }
 524        else {
 525          if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR) {
 526            // XOR behaves like OR with added 'xor' element.
 527            $values[] = 'xor';
 528          }
 529          elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT) {
 530            // NOT behaves like OR with switched state.
 531            $options['state'] = strpos($options['state'], '!') === 0 ? drupal_substr($options['state'], 1) : '!' . $options['state'];
 532          }
 533
 534          // OR, NOT and XOR conditions are obtained with a nested array.
 535          foreach ($options['values'] as $value) {
 536            $values[] = array($options['condition'] => $value);
 537          }
 538        }
 539
 540        $state = array($options['state'] => array($options['selector'] => $values));
 541        $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;
 542
 543        // Execute special handler for fields that need further processing.
 544        // The handler has no return value. Modify the $state parameter by
 545        // reference if needed.
 546        foreach ($state_handlers as $handler => $handler_conditions) {
 547          if (array_intersect_assoc($handler_conditions, $dependee_form_field) == $handler_conditions) {
 548            $handler($dependee_form_field, $dependee_form_state, $options, $state);
 549          }
 550        }
 551
 552        // Add validation callback to element.
 553        _conditional_fields_element_add_property($dependent_form_field, '#element_validate', 'conditional_fields_dependent_validate', 'append');
 554      }
 555
 556      // Add the $state into the correct logic group in $states.
 557      foreach ($state as $key => $constraints) {
 558        if (empty($states[$key][$options['grouping']])) {
 559          $states[$key][$options['grouping']] = $constraints;
 560        }
 561        else {
 562          $states[$key][$options['grouping']] = array_merge($states[$key][$options['grouping']], $constraints);
 563        }
 564      }
 565
 566      // Build effect settings for effects with options.
 567      // TODO: add dependee key to allow different effects on the same selector.
 568      if ($options['effect'] && $options['effect'] != 'show') {
 569        $selector = conditional_fields_field_selector(drupal_array_get_nested_value($form, array($dependent_location[0])));
 570        // Convert numeric strings to numbers.
 571        foreach ($options['effect_options'] as &$effect_option) {
 572          if (is_numeric($effect_option)) {
 573            $effect_option += 0;
 574          }
 575        }
 576        $effects[$selector] = array(
 577          'effect' => $options['effect'],
 578          'options' => $options['effect_options'],
 579        );
 580      }
 581
 582      // Apply reset dependent to default if untriggered behavior.
 583      if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED, $behaviors)) {
 584        // Add property to element so conditional_fields_dependent_validate() can
 585        // pick it up.
 586        $dependent_form_field['#conditional_fields_reset_if_untriggered'] = TRUE;
 587        unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]);
 588      }
 589    }
 590
 591    // Execute custom behaviors.
 592    if (!empty($behaviors)) {
 593      foreach ($behaviors as $behavior) {
 594        // Custom behaviors are callbacks.
 595        if (function_exists($$behavior)) {
 596          $$behavior('edit', $form, $form_state, $dependent, $dependencies);
 597        }
 598      }
 599    }
 600    unset($behaviors);
 601
 602    if (empty($states)) {
 603      continue;
 604    }
 605
 606    // Save the modified field back into the form.
 607    drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field);
 608
 609    // Map the states based on the conjunctions.
 610    $states_new = array();
 611    foreach ($states as $state_key => $value) {
 612      // As the main object is ANDed together we can add the AND items directly.
 613      if (!empty($states[$state_key]['AND'])) {
 614        $states_new[$state_key] = $states[$state_key]['AND'];
 615      }
 616      // The OR and XOR groups are moved into a sub-array that has numeric keys
 617      // so that we get a JSON array and not an object, as required by the States
 618      // API for OR and XOR groupings.
 619      if (!empty($states[$state_key]['OR'])) {
 620        $or = array();
 621        foreach ($states[$state_key]['OR'] as $constraint_key => $constraint_value) {
 622          $or[] = array($constraint_key => $constraint_value);
 623        }
 624        // '1' as a string so that we get an object (which means logic groups 
 625        // are ANDed together).
 626        $states_new[$state_key]['1'] = $or;
 627      }
 628      if (!empty($states[$state_key]['XOR'])) {
 629        $xor = array('xor');
 630        foreach ($states[$state_key]['XOR'] as $constraint_key => $constraint_value) {
 631          $xor[] = array($constraint_key => $constraint_value);
 632        }
 633        // '2' as a string so that we get an object.
 634        $states_new[$state_key]['2'] = $xor;
 635      }
 636    }
 637    $states = $states_new;
 638
 639    // Add the #states property to the dependent field.
 640    drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states);
 641
 642    $has_states = TRUE;
 643  }
 644
 645  if (empty($has_states)) {
 646    return $form;
 647  }
 648
 649  $form['#attached']['js'][] = drupal_get_path('module', 'conditional_fields') . '/js/conditional_fields.js';
 650
 651  // Add effect settings to the form.
 652  if ($effects) {
 653    $form['#attached']['js'][] = array(
 654      'data' => array(
 655        'conditionalFields' => array(
 656          'effects' => $effects,
 657        ),
 658      ),
 659      'type' => 'setting',
 660    );
 661  }
 662
 663  // Validation callback to manage dependent fields validation.
 664  $form['#validate'][] = 'conditional_fields_form_validate';
 665  // Initialize validation information every time the form is rendered to avoid
 666  // stale data after a failed submission.
 667  $form_state['conditional_fields_untriggered_dependents'] = array();
 668
 669  return $form;
 670}
 671
 672/**
 673 * Dependent field validation callback.
 674 *
 675 * If the dependencies of a dependent field are not triggered, the validation
 676 * errors that it might have thrown must be removed, together with its submitted
 677 * values. This will simulate the field not being present in the form at all.
 678 * In this field-level callback we just collect needed information and store it
 679 * in $form_state. Values and errors will be removed in a single sweep in
 680 * conditional_fields_form_validate(), which runs at the end of the validation
 681 * cycle.
 682 *
 683 * @see conditional_fields_form_validate()
 684 */
 685function conditional_fields_dependent_validate($element, &$form_state, $form) {
 686  $dependent = $element[$element['#language']];
 687
 688  // Check if this field's dependencies were triggered.
 689  if (conditional_fields_evaluate_dependencies($dependent, $form, $form_state)) {
 690    return;
 691  }
 692
 693  // Mark submitted values for removal. We have to remove them after all fields
 694  // have been validated to avoid collision between dependencies.
 695  $form_state_addition['parents'] = $dependent['#array_parents'];
 696
 697
 698  // Optional behavior: reset the field to its default values.
 699  // Default values are always valid, so it's safe to skip validation.
 700  if (!empty($element['#conditional_fields_reset_if_untriggered'])) {
 701    $form_state_addition['reset'] = TRUE;
 702  }
 703
 704  // Tag validation errors previously set on this field for removal in
 705  // conditional_fields_form_validate().
 706  $errors = form_get_errors();
 707
 708  if ($errors) {
 709    $error_key = implode('][', $dependent['#parents']);
 710    foreach ($errors as $name => $error) {
 711      // An error triggered by this field might have been set on a descendant
 712      // element. This also means that so there can be multiple errors on the
 713      // same field (even though Drupal doesn't support multiple errors on the
 714      // same element).
 715      if (strpos($name, $error_key) === 0) {
 716        $field_errors[$name] = $error;
 717      }
 718    }
 719  }
 720
 721  if (!empty($field_errors)) {
 722    $form_state_addition['errors'] = $field_errors;
 723  }
 724
 725  $form_state['conditional_fields_untriggered_dependents'][] = $form_state_addition;
 726}
 727
 728/**
 729 * Extracts submitted field values during form validation.
 730 *
 731 * @return
 732 *   The requested field values parent. Actual field vales are stored under the
 733 *   key $element['#field_name'].
 734 */
 735function conditional_fields_form_field_get_values($element, $form_state) {
 736  // Fall back to #parents to support custom dependencies.
 737  $parents = isset($element['#field_parents']) ? $element['#field_parents'] : $element['#parents'];
 738  return drupal_array_get_nested_value($form_state['values'], $parents);
 739}
 740
 741/**
 742 * Validation callback for any form with conditional fields.
 743 *
 744 * This validation callback is added to all forms that contain fields with
 745 * dependencies. It removes all validation errors from dependent fields whose
 746 * dependencies are not triggered, which were collected at field-level
 747 * validation in conditional_fields_dependent_validate().
 748 *
 749 * @see conditional_fields_dependent_validate()
 750 */
 751function conditional_fields_form_validate($form, &$form_state) {
 752  if (empty($form_state['conditional_fields_untriggered_dependents'])) {
 753    return;
 754  }
 755
 756  $untriggered_dependents_errors = array();
 757
 758  foreach ($form_state['conditional_fields_untriggered_dependents'] as $field) {
 759    $dependent = drupal_array_get_nested_value($form, $field['parents']);
 760    $field_values_location = conditional_fields_form_field_get_values($dependent, $form_state);
 761
 762    // If we couldn't find a location for the field's submitted values, let the
 763    // validation errors pass through to avoid security holes.
 764    if (!isset($field_values_location[$dependent['#field_name']])) {
 765      continue;
 766    }
 767
 768    if (empty($field['reset'])) {
 769      unset($field_values_location[$dependent['#field_name']]);
 770    }
 771    else {
 772      $dependent_info = field_form_get_state($dependent['#field_parents'], $dependent['#field_name'], $dependent['#language'], $form_state);
 773      $field_values_location[$dependent['#field_name']][$dependent['#language']] = field_get_default_value($dependent_info['instance']['entity_type'], NULL, $dependent_info['field'], $dependent_info['instance'], $dependent['#language']);
 774    }
 775
 776    // Save the changed array back in place.
 777    // Do not use form_set_value() since it assumes that the values are located at
 778    // $form_state['values'][ ... $element['#parents'] ... ], while the
 779    // documentation of hook_field_widget_form() states that field values are
 780    // $form_state['values'][ ... $element['#field_parents'] ... ].
 781    drupal_array_set_nested_value($form_state['values'], $dependent['#field_parents'], $field_values_location);
 782
 783    if (!empty($field['errors'])) {
 784      $untriggered_dependents_errors = array_merge($untriggered_dependents_errors, $field['errors']);
 785    }
 786  }
 787
 788  if (!empty($untriggered_dependents_errors)) {
 789    // Since Drupal provides no clean way to selectively remove error messages,
 790    // we have to store all current form errors and error messages, clear them,
 791    // filter out from our stored values the errors originating from untriggered
 792    // dependent fields, and then reinstate remaining errors and messages.
 793    $errors = array_diff_assoc((array) form_get_errors(), $untriggered_dependents_errors);
 794    form_clear_error();
 795    $error_messages = drupal_get_messages('error');
 796    $removed_messages = array_values($untriggered_dependents_errors);
 797
 798    // Reinstate remaining errors.
 799    foreach ($errors as $name => $error) {
 800      form_set_error($name, $error);
 801      // form_set_error() calls drupal_set_message(), so we have to filter out
 802      // these from the messages to avoid duplicates.
 803      $removed_messages[] = $error;
 804    }
 805
 806    // Reinstate remaining error messages (which, at this point, are messages that
 807    // were originated outside of the validation process).
 808    foreach (array_diff($error_messages['error'], $removed_messages) as $message) {
 809      drupal_set_message($message, 'error');
 810    }
 811  }
 812}
 813
 814/**
 815 * Helper function to add a property/value pair to a render array safely without
 816 * overriding any pre-existing value.
 817 *
 818 * @param $position
 819 *   'append' if $value should be inserted at the end of the $element[$property]
 820 *   array, any other value to insert it at the beginning.
 821 *
 822 */
 823function _conditional_fields_element_add_property(&$element, $property, $value, $position = 'prepend') {
 824  // Avoid overriding default element properties that might not yet be set.
 825  if (!isset($element[$property])) {
 826    $element[$property] = isset($element['#type']) ? element_info_property($element['#type'], $property, array()) : array();
 827  }
 828
 829  if (in_array($value, $element[$property])) {
 830    return;
 831  }
 832
 833  switch ($position) {
 834    case 'append':
 835      $element[$property] = array_merge($element[$property], (array) $value);
 836      break;
 837
 838    case 'prepend':
 839    default:
 840      $element[$property] = array_merge((array) $value, $element[$property]);
 841      break;
 842  }
 843}
 844
 845/**
 846 * Implements hook_entity_view_alter().
 847 *
 848 * Applies entity view logic to conditional fields.
 849 */
 850function conditional_fields_entity_view_alter(&$build, $type) {
 851  if (!(isset($build['#entity_type'], $build['#bundle']) && $dependencies = conditional_fields_load_dependencies($build['#entity_type'], $build['#bundle']))) {
 852    return;
 853  }
 854
 855  $evaluated_dependents = array();
 856
 857  foreach ($dependencies['dependents'] as $dependent => $dependency) {
 858    if (empty($build[$dependent]['#access'])) {
 859      continue;
 860    }
 861
 862    foreach ($dependency as $dependency_options) {
 863      $dependee = $dependency_options['dependee'];
 864      $options = $dependency_options['options'];
 865
 866      // We can interface with the States API only through the Value condition.
 867      if ($options['condition'] != 'value') {
 868        continue;
 869      }
 870
 871      // Determine field view behaviors.
 872      // If this dependent has multiple dependencies, only the logic of the
 873      // first dependency will be taken into account.
 874      if (!isset($behaviors)) {
 875        $behaviors = conditional_fields_field_behaviors('view', $options);
 876      }
 877
 878      // Manage orphan fields (dependents with no dependees).
 879      $evaluate = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE, $behaviors);
 880      $hide_orphan = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN, $behaviors);
 881      $hide_untriggered_orphan = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN, $behaviors);
 882      $is_orphan = empty($build[$dependee]['#access']);
 883      if ($is_orphan) {
 884        // Hide the dependent. No need to evaluate the dependency.
 885        if ($hide_orphan) {
 886          $build[$dependent]['#access'] = FALSE;
 887          continue;
 888        }
 889        if ($hide_untriggered_orphan) {
 890          $evaluate = TRUE;
 891        }
 892        if ($evaluate) {
 893          // We have to look for the dependee in the entity object.
 894          // TODO: Is it possible to avoid hardcoding this?
 895          switch ($type) {
 896            case 'node':
 897              $entity_property = '#node';
 898              break;
 899            case 'user':
 900              $entity_property = '#account';
 901              break;
 902            case 'term':
 903              $entity_property = '#term';
 904              break;
 905            case 'field_collection_item':
 906            case 'profile2':
 907            default:
 908              $entity_property = '#entity';
 909          }
 910          // If we didn't find the field, there is nothing more we can do.
 911          if (!isset($build[$entity_property]->$dependee)) {
 912            continue;
 913          }
 914          $items = $build[$entity_property]->$dependee;
 915          // Values are keyed by language here, remove it.
 916          $items = array_shift($items);
 917        }
 918      }
 919      else {
 920        $items = $build[$dependee]['#items'];
 921      }
 922
 923      if ($evaluate) {
 924        $evaluated_dependents[$dependent][$options['grouping']][] = conditional_fields_evaluate_dependency('view', $items, $options);
 925      }
 926    }
 927    if (isset($evaluated_dependents[$dependent])) {
 928      $is_triggered = conditional_fields_evaluate_grouping($evaluated_dependents[$dependent]);
 929
 930      foreach ($behaviors as $behavior) {
 931        switch ($behavior) {
 932          case CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE:
 933            // Hide the dependent if it is not triggered.
 934            if (!$is_triggered) {
 935              $build[$dependent]['#access'] = FALSE;
 936            }
 937            break;
 938
 939          case CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN:
 940            // This case was handled while looking for the field.
 941            break;
 942
 943          case CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN:
 944            // Hide the dependent if the dependee is not viewable and the dependency is not triggered.
 945            if ($is_orphan && !$is_triggered) {
 946              $build[$dependent]['#access'] = FALSE;
 947            }
 948            break;
 949
 950          case CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT:
 951            // Show the dependent themed like an error message if it is not triggered.
 952            if (!$is_triggered) {
 953              $build[$dependent]['#prefix'] = isset($build[$dependent]['#prefix']) ? '<div class="messages error">' . $build[$dependent]['#prefix'] : '<div class="messages error">';
 954              $build[$dependent]['#suffix'] = isset($build[$dependent]['#suffix']) ? $build[$dependent]['#suffix'] . '</div>' : '</div>';
 955            }
 956            break;
 957
 958          case CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE:
 959            // Show a textual description of the dependency under the dependent field.
 960            if ($build[$dependent]['#access']) {
 961              $dependee_title = isset($build[$dependee]['#title']) ? $build[$dependee]['#title'] : $dependee;
 962              $dependent_title = isset($build[$dependent]['#title']) ? $build[$dependent]['#title'] : $dependent;
 963              $description = conditional_fields_dependency_description($dependee_title, $dependent_title, $options);
 964              if (isset($build[$dependent]['#suffix'])) {
 965                $description = $build[$dependent]['#suffix'] . $description;
 966              }
 967              $build[$dependent]['#suffix'] = $description;
 968            }
 969            break;
 970
 971          default:
 972            // Custom behaviors are callbacks.
 973            $$behavior('view', $dependee, $dependent, $is_triggered, $dependencies, $build, $type);
 974            break;
 975        }
 976
 977        if (empty($build[$dependent]['#access'])) {
 978          break;
 979        }
 980      }
 981    }
 982
 983    unset($behaviors);
 984  }
 985}
 986
 987/**
 988 * Evaluates an array with 'AND', 'OR' and 'XOR' groupings,
 989 * each containing a list of boolean values.
 990 */
 991function conditional_fields_evaluate_grouping($groups) {
 992  $or = $and = $xor = TRUE;
 993  if (!empty($groups['OR'])) {
 994    $or = in_array(TRUE, $groups['OR']);
 995  }
 996  if (!empty($groups['AND'])) {
 997    $and = !in_array(FALSE, $groups['AND']);
 998  }
 999  if (!empty($groups['XOR'])) {
1000    $xor = array_sum($groups['XOR']) == 1;
1001  }
1002  return $or && $and && $xor;
1003}
1004
1005/**
1006 * Evaluate a set of dependencies for a dependent field.
1007 *
1008 * @param $dependent
1009 *   The field form element in the current language.
1010 */
1011function conditional_fields_evaluate_dependencies($dependent, $form, $form_state) {
1012  $dependencies = $form['#conditional_fields'][$dependent['#field_name']]['dependees'];
1013  $evaluated_dependees = array();
1014
1015  foreach ($dependencies as $dependency_id => $dependency) {
1016    // Extract field values from submitted values.
1017    $dependee = $dependency['dependee'];
1018    $dependee_parents = $form['#conditional_fields'][$dependee]['parents'];
1019
1020    // We have the parents of the field, but depending on the entity type and
1021    // the widget type, they may include additional elements that are actually
1022    // part of the value. So we find the depth of the field inside the form
1023    // structure and use the parents only up to that depth.
1024    $dependee_parents_keys = array_flip($dependee_parents);
1025    $dependee_parent = drupal_array_get_nested_value($form, array_slice($dependee_parents, 0, $dependee_parents_keys[$dependee]));
1026    $values = conditional_fields_form_field_get_values($dependee_parent[$dependee], $form_state);
1027
1028    // Remove the language key.
1029    if (isset($dependee_parent[$dependee]['#language'], $values[$dependee_parent[$dependee]['#language']])) {
1030      $values = $values[$dependee_parent[$dependee]['#language']];
1031    }
1032
1033    $evaluated_dependees[$dependent['#field_name']][$dependency['options']['grouping']][] = conditional_fields_evaluate_dependency('edit', $values, $dependency['options']);
1034  }
1035
1036  return conditional_fields_evaluate_grouping($evaluated_dependees[$dependent['#field_name']]);
1037}
1038
1039/**
1040 * Evaluate if a dependency meets the requirements to be triggered.
1041 *
1042 * @param $context
1043 *   'edit' if $values are extracted from $form_state or 'view' if
1044 *   $values are extracted from an entity.
1045 */
1046function conditional_fields_evaluate_dependency($context, $values, $options) {
1047  if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) {
1048    $dependency_values = $context == 'view' ? $options['value'] : $options['value_form'];
1049
1050    // Simple case: both values are strings or integers. Should never happen in
1051    // view context, but does no harm to check anyway.
1052    if (!is_array($values)) {
1053      // Options elements consider "_none" value same as empty.
1054      $values = $values === '_none' ? '' : $values;
1055
1056      if (!is_array($dependency_values)) {
1057        // Some widgets store integers, but values saved in $dependency_values
1058        // are always strings. Convert them to integers because we want to do a
1059        // strict equality check to differentiate empty strings from '0'.
1060        if (is_int($values) && is_numeric($dependency_values)) {
1061          $dependency_values = (int) $dependency_values;
1062        }
1063
1064        return $dependency_values === $values;
1065      }
1066
1067      // If $values is a string and $dependency_values an array, convert $values
1068      // to the standard field array form format. This covers cases like single
1069      // value textfields.
1070      $values = array(array('value' => $values));
1071    }
1072
1073    // If we are in form context, we are almost done.
1074    if ($context == 'edit') {
1075      // If $dependency_values is not an array, we can only assume that it
1076      // should map to the first key of the first value of $values.
1077      if (!is_array($dependency_values)) {
1078        $key = current(array_keys((array) current($values)));
1079        $dependency_values = array(array($key => $dependency_values));
1080      }
1081
1082      // Compare arrays recursively ignoring keys, since multiple select widgets
1083      // values have numeric keys in form format and string keys in storage
1084      // format.
1085      return array_values($dependency_values) == array_values($values);
1086    }
1087
1088    // $values, when viewing fields, may contain all sort of additional
1089    // information, so filter out from $values the keys that are not present in
1090    // $dependency_values.
1091    // Values here are alway keyed by delta (regardless of multiple value
1092    // settings).
1093    foreach ($values as $delta => &$value) {
1094      if (isset($dependency_values[$delta])) {
1095        $value = array_intersect_key($value, $dependency_values[$delta]);
1096
1097        foreach ($value as $key => &$element_value) {
1098          if (isset($dependency_values[$delta][$key]) && is_int($dependency_values[$delta][$key]) && is_numeric($element_value)) {
1099            $element_value = (int) $element_value;
1100          }
1101        }
1102      }
1103    }
1104
1105    // Compare values.
1106    foreach ($dependency_values as $delta => $dependency_value) {
1107      if (!isset($values[$delta])) {
1108        return FALSE;
1109      }
1110      foreach ($dependency_value as $key => $dependency_element_value) {
1111        // Ignore keys set in the field and not in the dependency.
1112        if (isset($values[$delta][$key]) && $values[$delta][$key] !== $dependency_element_value) {
1113          return FALSE;
1114        }
1115      }
1116    }
1117
1118    return TRUE;
1119  }
1120
1121  // Flatten array of values.
1122  $reference_values = array();
1123  foreach ((array) $values as $value) {
1124    // TODO: support multiple values.
1125    $reference_values[] = is_array($value) ? array_shift($value) : $value;
1126  }
1127
1128  // Regular expression method.
1129  if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) {
1130    foreach ($reference_values as $reference_value) {
1131      if (!preg_match('/' . $options['value']['RegExp'] . '/', $reference_value)) {
1132        return FALSE;
1133      }
1134    }
1135    return TRUE;
1136  }
1137
1138  switch ($options['values_set']) {
1139    case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND:
1140      $diff = array_diff($options['values'], $reference_values);
1141      return empty($diff);
1142
1143    case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR:
1144      $intersect = array_intersect($options['values'], $reference_values);
1145      return !empty($intersect);
1146
1147    case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR:
1148      $intersect = array_intersect($options['values'], $reference_values);
1149      return count($intersect) == 1;
1150
1151    case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT:
1152      $intersect = array_intersect($options['values'], $reference_values);
1153      return empty($intersect);
1154  }
1155}
1156
1157/**
1158 * Determine which dependency behaviors should be used in forms and content
1159 * display, depending on dependency options and user roles.
1160 *
1161 * @param $op
1162 *   'view' or 'edit'.
1163 * @param $options
1164 *    Dependency options.
1165 *
1166 * @return
1167 *   An array of behaviors.
1168 *
1169 */
1170function conditional_fields_field_behaviors($op, $options) {
1171  global $user;
1172
1173  if ($options['element_' . $op . '_per_role']) {
1174    foreach ($options['element_' . $op . '_roles'] as $rid => $role_behaviors) {
1175      if (isset($user->roles[$rid])) {
1176        $behaviors = $role_behaviors;
1177        break;
1178      }
1179    }
1180  }
1181  else {
1182    $behaviors = $options['element_' . $op];
1183  }
1184
1185  // Filter out inactive behaviors.
1186  $behaviors = array_filter($behaviors);
1187
1188  return $behaviors;
1189}
1190
1191/**
1192 * Builds a jQuery selector from the name attribute of a field.
1193 * TODO: support custom selectors with %lang and %key placeholders.
1194 */
1195function conditional_fields_field_selector($field) {
1196  if (isset($field['#attributes']['name'])) {
1197    return '[name="' . $field['#attributes']['name'] . '"]';
1198  }
1199
1200  if (isset($field['#name'])) {
1201    return '[name="' . $field['#name'] . '"]';
1202  }
1203
1204  // Try with id if name is not found.
1205  if (isset($field['#attributes']['id'])) {
1206    return '#' . $field['#attributes']['id'];
1207  }
1208
1209  if (isset($field['#id'])) {
1210    return '#' . $field['#id'];
1211  }
1212
1213  return FALSE;
1214}
1215
1216/**
1217 * Provides default options for a dependency.
1218 *
1219 * For an explanation of available options,
1220 * @see conditional_fields_field_attach_dependency()
1221 */
1222function conditional_fields_dependency_default_options() {
1223  return array(
1224    'state'                 => 'visible',
1225    'condition'             => 'value',
1226    'grouping'              => 'AND',
1227    'values_set'            => CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET,
1228    'value'                 => array(),
1229    'values'                => array(),
1230    'value_form'            => array(),
1231    'effect'                => 'show',
1232    'effect_options'        => array(),
1233    'element_view'          => array(
1234      CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE => CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE,
1235      CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN => CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN,
1236      CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN => 0,
1237      CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT => 0,
1238      CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE => 0,
1239    ),
1240    'element_view_per_role' => 0,
1241    'element_view_roles'    => array(),
1242    'element_edit'          => array(
1243      CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN => CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN,
1244      CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN => 0,
1245      CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED => 0,
1246    ),
1247    'element_edit_per_role' => 0,
1248    'element_edit_roles'    => array(),
1249    'selector'              => '',
1250  );
1251}
1252
1253/**
1254 * Loads all dependencies from the database.
1255 *
1256 * The result can be filtered by providing an entity type and a bundle name.
1257 */
1258function conditional_fields_load_dependencies($entity_type = NULL, $bundle = NULL) {
1259  // Use the advanced drupal_static() pattern.
1260  static $dependencies;
1261  if (!isset($dependencies)) {
1262    $dependencies = &drupal_static(__FUNCTION__);
1263  }
1264
1265  if (!$dependencies) {
1266    $dependencies = array();
1267  }
1268
1269  if (!isset($dependencies[$entity_type][$bundle])) {
1270    if (!empty($entity_type) && !empty($bundle)) {
1271      $dependencies[$entity_type][$bundle] = array();
1272    }
1273    $default_options = conditional_fields_dependency_default_options();
1274
1275    $select = db_select('conditional_fields', 'cf')
1276      ->fields('cf', array('id', 'options'))
1277      ->orderBy('cf.dependent');
1278    $fci_depende = $select->join('field_config_instance', 'fci_dependee', 'cf.dependee = fci_dependee.id');
1279    $fci_dependent = $select->join('field_config_instance', 'fci_dependent', 'cf.dependent = fci_dependent.id');
1280    $select->addField($fci_depende, 'field_name', 'dependee');
1281    $select->addField($fci_dependent, 'field_name', 'dependent');
1282    $select->addField($fci_depende, 'entity_type');
1283    $select->addField($fci_depende, 'bundle');
1284
1285    if ($entity_type) {
1286      $select->condition(
1287        db_and()
1288          ->condition('fci_dependee.entity_type', $entity_type)
1289          ->condition('fci_dependent.entity_type', $entity_type)
1290      );
1291    }
1292
1293    if ($bundle) {
1294      $select->condition(
1295        db_and()
1296          ->condition('fci_dependee.bundle', $bundle)
1297          ->condition('fci_dependent.bundle', $bundle)
1298      );
1299    }
1300
1301    $result = $select->execute();
1302
1303    foreach ($result as $dependency) {
1304      $result_entity_type = $entity_type ? $entity_type : $dependency->entity_type;
1305      $result_bundle = $bundle ? $bundle : $dependency->bundle;
1306
1307      $options = unserialize($dependency->options);
1308      $options += $default_options…

Large files files are truncated, but you can click here to view the full file