I'm using Hierarchical Select in a custom form being built with the CTools Form Wizard, and I'm running into an issue where Drupal's Form API isn't generating a #parameters option in the array, probably because of whatever CTools is doing to the form internally. Since hierarchical_select_json() uses the #parameters array to generate the form element upon update, it was giving me a "Received an invalid response from the server." I realize that this may be related to the numerous other issues dealing with this response, but I couldn't find a common thread in any of them to determine if this was related or not.

For documentation purposes, here is a list of (potentially) related issues...

#538022: JS alert: "Received an invalid response from the server."
#697172: "Invalid Response from Server", possible causes and solution
#784078: Incompatibility with formblock.module (node.pages.inc is missing): add node.pages.inc precautionary on all hs_taxonomy forms

Since grokking this module seemed simpler, I decided to attempt a patch here first. Patch to follow.

Comments

Brandonian’s picture

Assigned: Brandonian » Unassigned
Status: Active » Needs review
StatusFileSize
new1.5 KB

Patch sets $storage['parameters'] based on $_POST information, which in my use case is valid. Not sure how this will conflict with the subform_element problem described in the comment, but it should default to the prior version's implantation. There's still a potential fail if $storage isn't set and $_POST['form_id'] isn't set.

crea’s picture

Confirming and subscribing. I've noticed that too while working with the form wizard.

alekm’s picture

Trying to create a custom multistep form using the CTools form wizard
Same issue here, no answer found yet. CTools 1.8 with HS 3.6 & 3.x-dev. Patch above did not work.

Nick Lewis’s picture

Status: Needs review » Needs work

@Brandonian This patch didn't fix the issue for me either. There must be some configuration factors at play as well... Could you please share the wizard setup you used that got fixed by this patch?

Adding to the discussion...
The failure that causes this bug happens in the ahah callback function hierarchical_select_json(); (line 291 hierarchial_select module).
Specifically this process fails:

  // Retrieve and process the form.
  $form = call_user_func_array('drupal_retrieve_form', $storage['parameters']);
  drupal_prepare_form($form_id, $form, $form_state);
  $form['#post'] = $_POST;
  $form = form_builder($form_id, $form, $form_state);

  // Render only the relevant part of the form (i.e. the hierarchical_select
  // form item that has triggered this AJAX callback).
  $hsid = $_POST['hsid'];
  $name = $storage['#names'][$hsid];
  $part_of_form = _hierarchical_select_get_form_item($form, $name);
  $output = drupal_render($part_of_form);

The issue is that the hierarchial select element doesn't get returned from form_builder(). There are many causes for this, most notably:
1. ctools wizard $form array doesn't return but is passed by reference. This could be partially solved by simply returning $form when when arg(0) == hierarchical select callback.
2. Even when you do return the form array it fails for some reason. This may have something to do with the fact that you can't call drupal_get_form('ctools_wizard_form') and expect it to work (but in some sense, that's what the callback is trying to do when it rebuilds the form element).

Seems like there's two routes to fixing this:
1. create a new callback that is compatible with ctools wizards (this I've explored - its not looking easy).
2. figure out why other ahah form elements work fine in ctools wizards but this one doesn't.

wim leers’s picture

Priority: Normal » Minor

Probably it's mostly because HS uses:
- virtually all FAPI functionality and something trips up deep down in the FAPI dungeons of doom
- its own form cache (which was necessary to be able to work in Views exposed filters forms)

In any case, I won't be spending any time on solving this, because it's most likely an excruciating experience. It also seems to affect few people, so it doesn't seem to be very important (hence marking as minor).

vectoroc’s picture

subscribing

wim leers’s picture

Status: Needs work » Closed (won't fix)

Closing due to lack of response.

msumme’s picture

This works w/ the ctools wizard... I just run some checks, and load the form from the form id instaed of the parameters...


 function my_module_hierarchical_select_json() {
	// We are returning Javascript, so tell the browser. Ripped from Drupal 6's
  // drupal_json() function.
  drupal_set_header('Content-Type: text/javascript; charset=utf-8');

  $hs_form_build_id = $_POST['hs_form_build_id'];

  // In this context, we're in a node selection mode.
  // See http://drupal.org/node/683574#comment-3117926.
  if (module_exists('i18n') && isset($_POST['language'])) {
    i18n_selection_mode('node', $_POST['language']);
  }

  // Collect all necessary variables.
  $cached = cache_get($hs_form_build_id, 'cache');
  $storage = $cached->data;
	
  // Ensure that the form id in the POST array is the same as the one of the
  // stored parameters of the original form. For 99% of the forms, this step
  // is not necessary, but when a hierarchical_select form item is inside a
  // form in a subform_element in a form, then it is necessary.
//THIS IS THE BIG CHANGE BELOW
  if(isset($storage['parameters'][0]) && !empty($storage['parameters'][0]))
  	$form_id = $_POST['form_id'] = $storage['parameters'][0];
  else 
  	$form_id = $_POST['form_id'];
	
  //this doesn't really do anything....
  $form_state = &$storage['parameters'][1];

  // Include the file in which the form definition function lives.
  if (!empty($storage['file'])) {
    require_once($storage['file']);
  }

  // Also include files set in $form_state['form_load_files']. Set by CTools
  // Delegator, which is used by Panels (i.e. this is necessary for Panels
  // compatibility).
  if (isset($form_state['form_load_files'])) {
    foreach ($form_state['form_load_files'] as $file) {
      require_once './' . $file;
    }
  }
//THIS IS ANOTHER CHANGE
  // Retrieve and process the form.
  $form = drupal_retrieve_form($form_id,$form_state);
  
  drupal_prepare_form($form_id, $form, $form_state);
  $form['#post'] = $_POST;
  $form = form_builder($form_id, $form, $form_state);

  // Render only the relevant part of the form (i.e. the hierarchical_select
  // form item that has triggered this AJAX callback).
  $hsid = $_POST['hsid'];
  $name = $storage['#names'][$hsid];

  $part_of_form = _hierarchical_select_get_form_item($form, $name);
  $output = drupal_render($part_of_form);

  // If the user's browser supports the active cache system, then send the
  // currently requested hierarchy in an easy-to-manage form.
  $cache = array();
  if (isset($_POST['client_supports_caching'])) {
    if ($_POST['client_supports_caching'] == 'true') {
      $cache = _hierarchical_select_json_convert_hierarchy_to_cache($part_of_form['hierarchy']['#value']);
    }
    else if ($_POST['client_supports_caching'] == 'false') {
      // This indicates that a client-side cache is installed, but not working
      // properly.
      // TODO: figure out a clean way to notify the administrator.
    }
  }

  print drupal_to_js(array(
    'cache'  => $cache,
    'output' => $output,
    'log'    => (isset($part_of_form['log']['#value'])) ? $part_of_form['log']['#value'] : NULL,
  ));
  exit;
}

msumme’s picture

Almost forgot... w/ the code i just put up there, you'll need to have your ctools wizard function return something... which doesn't seem to be a problem.

ayalon’s picture

In my case, I wanted to use node_form and hierarchical select together.

you have to add pages.inc to the files, that get loades, else you receive an invalid callback:

http://drupal.org/node/640534

$form_state['form_load_files'] = array(drupal_get_path('module', 'node') . '/pages.inc');

cosolom’s picture

Issue summary: View changes

Problem is still actual for D6 (6.28) + HS (3.8) + Ctools (1.10) and may not will be fixed. Additional info about proble can find here.
But you can setup your ctools wizard form in a way to work all.
Need write additional wrapper for form callback and on form wizard page write at the end of form:

<?php
function mymodule_wizard_checkout(&$form, &$form_state) {
  /*here form definition with hierarchical select field*/
  $form['#parameters'] = array(
    'mymodule_wizard_checkout_wrapper', 
    $form_state
  );
}

function mymodule_wizard_checkout_wrapper(&$form_state) {
  $form = array();
  mymodule_wizard_checkout($form, $form_state);
  return $form;
}
?>

It is very simple, but not obvious.