All Drupal module developers would probably be aware of Drupal Form API’s form_set_value() method, which allows you to “change submitted form values during the form processing cycle.

You can use this method in the form validate handler to process a different value in the form submit handler, than what was submitted by the browser. However, remember the new value would be used for processing only, and would not be rendered to the browser if you have not specified a redirect url for the form, and the same form renders again after submission.

Now, here was a big problem for me, while working on the next version of my open source Drupal module, Take Control. In the Admin section of this module’s upcoming version, there is an option to generate new Public/Private key pair to be used by module in async Ajax calls.

I needed to generate new key pairs in the submit handler (not validate handler) of this form, and somehow get the new keys generated to be rendered to the browser instead of the original ones. An extensive Google search threw nothing up. However, a small hint in this comment, was enough to validate that what I wanted can be achieved, albeit with some work.

You can set ‘rebuild’=TRUE on your form state to ask Drupal to build your form from scratch.

What this meant was that I can generate new Key Pair in the submit handler, ask Drupal to rebuild the form, and use the new Pair during the rebuild process, as default values for the form fields.

The last piece in the puzzle (rather second last) was how to decide while form rebuilding, which key pair to use, original one or the new one. The solution was simple here, presence of “rebuild” attribute in the form state with a value of true meant that the new pair was to be used.

The absolutely final part of the solution for the problem here was a bit complex. Remember, after Drupal rebuilt your form, all data submitted by the user would be over-written by default values you specify during rebuilding. This is desirable for the actual fields that you want to change, but highly non-user friendly for other fields, where user submitted data should have persisted.

This obviously meant you would need to copy $form_state to your $form fields manually. And this process needed to be recursive due to the probability of presence of fields having #tree=> TRUE which meant that the submitted data in $form_state would be nested.

So, here’s the step-by-step solution to be above problems (the code has been extracted in bits and pieces from the Take Control module as suitable):

1) You need to overwrite data of some fields in the form’s submit handler.

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function take_control_generate_new_handler($form, &$form_state) {
/// Other code
$keys = take_control_new_keys($algo, $passphrase);
form_set_value($form[‘key’][‘take_control_private_key’], $keys[‘priv’], $form_state);
form_set_value($form[‘key’][‘take_control_public_key’], $keys[‘pub’], $form_state);

$form_state[‘rebuild’] = TRUE;

drupal_set_message(‘New Public /Private Key Pair generated.’, ‘status’);
}{/syntaxhighlighter}

There are 2 important things to note above, i) we still use form_set_value() to overwrite submitted data in $form_state (this data would be used later for rebuilding the form), and ii) we ask Drupal to rebuild the form by setting: $form_state['rebuild']=TRUE.

 

2) You need to detect manually in your module_form() hook, whether this was en explicit request for rebuild:

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function take_control_settings_form(&$form_state = array()) {
/// Other code

$rebuild = FALSE;
if (!empty($form_state[‘rebuild’])) {
$rebuild = $form_state[‘rebuild’];
}
if ($rebuild == TRUE) {
// Trigger recursive state restore. Remember, our values overwritten in $form_state would be used for appropriate fields.
take_control_restore_user_input($form, $form_state);
$form_state[‘rebuild’] = FALSE;
}

return system_settings_form($form);
}{/syntaxhighlighter}

3) Restore the form values recursively. You can simply copy-paste the code below for your purposes, just changing the method names:

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function take_control_restore_user_input(&$form, &$form_state) {
$values = $form_state[‘values’];
foreach ($form as $key => &$parent) {
if (!empty($parent[‘#type’])) {
if (!empty($parent[‘#tree’]) && $parent[‘#tree’] == FALSE) {
$values = $values[$key];
}
take_control_restore_user_input_recursive($key, $parent, $values);
}
}
}

function take_control_restore_user_input_recursive($key, &$parent, &$values) {
$value = $values[$key];
if (!empty($value)) {
$parent[‘#default_value’] = $value;
}
foreach ($parent as $key => &$child) {
if (is_array($child) && !empty($child[‘#type’])) {
if (!empty($child[‘#tree’]) && $child[‘#tree’] == FALSE) {
$values = $values[$key];
}
take_control_restore_user_input_recursive($key, $child, $values);
}
}
}{/syntaxhighlighter}

Hmmm, well, that is lots of code for what should have been a simple method call in my opinion. But as you know, frameworks have their ways of doing things, and its best to stick to their ways for what you are trying to do, for maintenance and consistency.