You are here

function form_hierarchical_select_process in Hierarchical Select 7.3

Hierarchical select form element type #process callback.

1 string reference to 'form_hierarchical_select_process'
hierarchical_select_element_info in ./hierarchical_select.module
Implements hook_element_info().

File

./hierarchical_select.module, line 753
This module defines the "hierarchical_select" form element, which is a greatly enhanced way for letting the user select items in a hierarchy.

Code

function form_hierarchical_select_process($element, &$form_state, $complete_form) {
  if (arg(0) != 'hierarchical_select_ajax') {

    // Get unique identifier using parents of the field.
    $cid = isset($element['#parents']) ? implode("-", $element['#parents']) : implode("-", $element['#field_parents']);

    // Verify if hsid is present.
    $elhsid = drupal_array_get_nested_value($element, array(
      '#value',
      'hsid',
    ));
    if (!isset($elhsid)) {

      // Retrieve previous element from form_state.
      $cached = drupal_array_get_nested_value($form_state, array(
        'storage',
        'hs',
        'hs_fields',
        $cid,
      ));
    }
    if (empty($cached)) {
      $docache = TRUE;
    }
    else {

      // Switch current element with the "cached".
      return $cached;
    }
  }

  // Determine the HSID.
  $hsid = _hs_process_determine_hsid($element, $form_state);

  // Config.
  $config = $element['#config'];

  // Attach CSS/JS files and JS settings.
  $element = _hs_process_attach_css_js($element, $hsid, $form_state, $complete_form);

  // Developer mode diagnostics, return immediately in case of a config error.
  if (!_hs_process_developer_mode_log_diagnostics($element)) {
    return $element;
  }

  // Calculate the selections in both the hierarchical select and the dropbox,
  // we need these before we can render anything.
  $hs_selection = $db_selection = array();
  list($hs_selection, $db_selection) = _hierarchical_select_process_calculate_selections($element, $hsid, $form_state);

  // Developer mode logging: log selections.
  _hs_process_developer_mode_log_selections($config, $hs_selection, $db_selection);

  // Dynamically disable the dropbox when an exclusive item has been selected.
  // When this happens, the configuration is dynamically altered. Hence, we
  // need to update $config.
  list($element, $hs_selection, $db_selection) = _hs_process_exclusive_lineages($element, $hs_selection, $db_selection);
  $config = $element['#config'];

  // Generate the $hierarchy and $dropbox objects using the selections that
  // were just calculated.
  $dropbox = !$config['dropbox']['status'] ? FALSE : _hierarchical_select_dropbox_generate($config, $db_selection);
  $hierarchy = _hierarchical_select_hierarchy_generate($config, $hs_selection, $element['#required'], $dropbox);

  // Developer mode logging: log $hierarchy and $dropbox objects.
  _hs_process_developer_mode_log_hierarchy_and_dropbox($config, $hierarchy, $dropbox);

  // Finally, calculate the return value of this hierarchical_select form
  // element. This will be set in _hierarchical_select_validate(). (If we'd
  // set it now, it would be overridden again.)
  $element['#return_value'] = _hierarchical_select_process_calculate_return_value($hierarchy, $config['dropbox']['status'] ? $dropbox : FALSE, $config['module'], $config['params'], $config['save_lineage']);
  if (!is_array($element['#return_value'])) {
    $element['#return_value'] = array(
      $element['#return_value'],
    );
  }

  // Add a validate callback, which will:
  // - validate that the dropbox limit was not exceeded.
  // - set the return value of this form element.
  // Also make sure it is the *first* validate callback.
  $element['#element_validate'] = isset($element['#element_validate']) ? $element['#element_validate'] : array();
  $element['#element_validate'] = array_merge(array(
    '_hierarchical_select_validate',
  ), $element['#element_validate']);

  // Ensure the form is cached, for AJAX to work.
  $form_state['cache'] = TRUE;

  //
  // Rendering.
  //
  // Ensure that #tree is enabled!
  $element['#tree'] = TRUE;

  // Store the HSID in a hidden form element; when an AJAX callback comes in,
  // we'll know which HS was updated.
  $element['hsid'] = array(
    '#type' => 'hidden',
    '#value' => $hsid,
  );

  // If render_flat_select is enabled, render a flat select.
  if ($config['render_flat_select']) {
    $element['flat_select'] = _hs_process_render_flat_select($hierarchy, $dropbox, $config);

    // See https://www.drupal.org/node/994820
    if (empty($element['flat_select']['#options'])) {
      unset($element['flat_select']);
    }
  }

  // Render the hierarchical select.
  $element['hierarchical_select'] = array(
    '#theme' => 'hierarchical_select_selects_container',
  );
  $size = isset($element['#size']) ? $element['#size'] : 0;
  $element['hierarchical_select']['selects'] = _hs_process_render_hs_selects($hsid, $hierarchy, $size);

  // When the special "create_new_item" value is passed in a level, replace
  // that level with an inline modal form to create a new item, and hide all
  // subsequent selects.
  list($element, $creating_new_item) = _hs_process_render_create_new_item($element, $hierarchy);

  // Render the dropbox, if enabled.
  // Automatically hides the "Add" button when creating a new item.
  // Automatically disables HS' selects when reaching the dropbox limit.
  // Stores the currently selected lineages of the dropbox in storage.
  list($element, $form_state) = _hs_process_render_dropbox($element, $hsid, $creating_new_item, $dropbox, $form_state);

  // Render the HTML that allows for graceful degradation.
  $element = _hs_process_render_nojs($element, $config);

  // Ensure the render order is correct.
  $element['hierarchical_select']['#weight'] = 0;
  $element['dropbox_limit_warning']['#weight'] = 1;
  $element['dropbox']['#weight'] = 2;
  $element['nojs']['#weight'] = 3;

  // If the form item is marked as disabled, disable all child form items as
  // well.
  if (isset($element['#disabled']) && $element['#disabled']) {
    _hierarchical_select_mark_as_disabled($element);
  }

  // This prevents values from in $form_state['input'] to be used instead of
  // the generated default values (#default_value).
  // For example: $element['hierarchical_select']['selects']['0']['#default_value']
  // is set to 'label_0' after an "Add" operation. When $form_state['input']
  // is NOT erased, the corresponding value in $form_state['input'] will be
  // used instead of the default value that was set. This would result in
  // undesired behavior.
  // This, however, must not be called on node preview, becuase in that case
  // the node will be rebuilt and we need the values inside $form_state['input']
  // to recreate the edited form properly.
  // @TODO: If the form is rebuilt by some other action than a node preview, we
  // might lose data again, we should see if there's any way to prevent this from
  // happening by setting this value after the form has been flagged to be rebuilt,
  // but as far as I checked, there's not.
  // Another option might be to rework the need of this function to prevent
  // the undesired behaviors of not having it with some other logic that would
  // work as well if the form is rebuilt.
  if (empty($docache)) {
    if (!isset($form_state['triggering_element']) || $form_state['triggering_element']['#value'] != t('Preview') && $form_state['triggering_element']['#value'] != t('View changes')) {
      if (isset($form_state['input']) && is_array($form_state['input'])) {
        drupal_array_set_nested_value($form_state['input'], $element['#array_parents'], array());
      }
    }
  }
  else {

    // Store new element in cache.
    $form_state['storage']['hs']['hs_fields'][$cid] = $element;
  }

  // Send the collected developer mode logs (by using #attached JS).
  $element = _hs_process_developer_mode_send_log_js($element, $hsid);
  return $element;
}