Using image submit buttons in 5.x with $_POST
There are several places scattered around Drupal.org discussing how to make image submit buttons work. None of them seem to do it this way, and this only requires a few extra lines in your form builder function. You may wonder if it's a hack to use the $_POST array, but this is the current way to handle Back/Cancel buttons without validating forms. So why not here?
The code:
<?php
function imagebutton_test_form() {
// Check to see if the image button was clicked.
if (isset($_POST['submit_x'])) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'submit',
);
}
$form['submit_image'] = array(
'#value' => '<input name="submit" type="image" title="'. t('Submit the form.') .'" src="/files/mysubmit.gif">',
);
return $form;
}
?>The explanation:
This is the builder function for a form that submits using an image button. Ignoring for a moment the check to see if the button was clicked, notice that the second part of the function simply adds the image button using straight HTML. This is just the quick and easy way to do it without creating a whole new form element type. Just make sure you're using good HTML and you won't have a problem.
When an image button is clicked to submit a form, a couple extra variables are submitted representing x and y coordinates for where on the image you clicked. These will be named using the name of the image element with an _x or _y after it. In the case of the example, the coords are named submit_x and submit_y.
So, you can use a simple check at the beginning of the form function to see if the form was submitted using our image button. If so, we want to add a submit element to the form array to fake a submission through the button. You must make sure the name of the image element and the value of the submit element are the same. If they are, the Forms API will then process the form like it was submitted using the fake submit element!
In this case, whatever validation or submit function you're wanting to process would follow the normal Forms API naming convention: imagebutton_test_form_validate() and imagebutton_test_form_submit().
This method has been tested and is currently in use for the PayPal module in Ubercart. You can see it in action by going to the Livetest, adding something to the cart, and clicking the PayPal Express Checkout button.

A few more references
Just adding some potentially useful reading links:
http://drupal.org/node/144758 ("Change submit buttons to images for a given form")
http://drupal.org/node/62647 ("HOWTO: Create image submit button")
Drew Gorton
Gorton Studios
$form['#post']['Submit_x'] versus $_POST['submit_x']
One can use
$form['#post']['Submit_x']instead of$_POST['submit_x']The outcome is same, but using a Drupal internal structure seemed possibly more future-proof (or not?)
Two days of my life have produced this:
I was struggling with the same issue (and unfortunately stumbled across this most excellent post only 10 hours into my VERY frustrated adventure), and want to share a few observations:
My goal was rather specific: I wanted to have pretty Submit and Delete buttons in the node/xxx/edit form.
There are two problems with it:
i) the one described above (only it did not quite work for me), and
ii) It appears to be a multipage form, where the Delete branch does not go through validation. It is triggered by submission of op=Delete, which does not happen in IE (IE submits op_x and op_y with the click coordinates instead). I am not aware of a politically correct way to modify $_POST values prior to a form being built, so I had to fall back to jQuery :(
For some reason the technique above was not working out, so I came up with the following variation:
1) In template.php I styled the form: told Drupal that the Submit and Delete buttons have been printed and called a template where I manually entered my "pretty" HTML
In template.php:
function phptemplate_post_node_form($form) {$form['submit']['#printed'] = 1;
$form['delete']['#printed'] = 1;
return _phptemplate_callback('form_edit_post', array('form' => $form));
}
Simplified form_edit_post.tpl.php:
<?php
print drupal_render($form);
?>
<input name="Submit" id="edit-submit" class="clear" value="<?=$form['submit']['#value']?>" class="form-<?=$form['submit']['#type']?>" type="image" src="http://test.mysite.com/images/submit.gif" />
<?php
if ($form['delete']) {
?>
<input name="Delete" id="edit-delete" value="Delete" class="form-submit" type="image" src="http://test.mysite.com/images/delete-button.png">
<?php } ?>
Notice that I tried to copy the original Drual statements as closely as possible (who needs extra headache!).
I also changed the name of the inputs from "op" to "Submit" and "Delete" respectively.
2) I used hook_form_alter to
a) fake the proper inputs for "civilized" browsers
b) insert a hidden input field named "op" and a jQuery script for IE
function my_form_alter($form_id, &$form) {
if ($form_id == 'post_node_form'){
if (isset($form['#post']['Submit_x']) || ($form['#post']['Submit'] == 'Submit')) {
$horrible_hack['#parents'] = array ('op');
form_set_value($horrible_hack,'Submit');
global $form_submitted;
$form_submitted = TRUE;
}
if (isset($form['#post']['Delete_x']) || ($form['#post']['Delete'] == 'Delete')) {
$horrible_hack['#parents'] = array ('op');
form_set_value($horrible_hack,'Delete');
global $form_submitted;
$form_submitted = TRUE;
}
// now we create the hidden 'op' field, because IE expects it
$form['op'] = array(
'#type' => 'hidden',
'#value' => ''
);
// add a script that will populate the field
$script = '$(function() {$("#edit-delete").click(function(){ $("input[name=\'op\']").attr("value","Delete");});})';
drupal_add_js($script, 'inline');
}
}
Notice that I did not have to apply jQuery magik to Submit image, since it is processed properly and can be tackled by hook_form_alter.
The $horrible_hack is there because function form_set_value is not documented well and I probably handled it wrong. But it worked!
It is possible to move most of the code from hook_form_alter to a custom validation function. The manuals suggest that it is an improper use of the validation mechanism, but this whole solution is ugly as sin anyway.
Parts of this code are probably redundant...
I really hope that someone smarter than me can take this little code-monster and make it all generic, pretty, and proper.
Bit confused: if an error is
Bit confused: if an error is generated, the form is re-rendered but since the image was clicked and $_POST['submit_x'] is set, the fake submit button is also rendered. Have I missed something?
http://www.drupalnotes.org
Submit Button with Image that also works with IE
After reviewing many solutions offered for Submit Buttons with images, such as
http://drupal.org/node/153902
http://drupal.org/node/51526
http://drupal.org/node/144758
I came up with a simple one that uses little bit from all those ideas (except CSS and backgrounds).
The idea was to make use of coordinate variable 'op_x' or 'op_y' which is returned through $_POST by all browsers, including IE.
I took all snippets from http://drupal.org/node/51526 but made few changes:
1. Completely removed function hook_imagebutton_process($form)
2. Removed line '#name' => 'op' from function hook_elements()
3. Added line '#name' => 'delete_' . $op_index into my own hook_form() function:
<?php
$form['group'][$op_index]['delete'] = array(
'#type' => 'imagebutton',
'#image' => 'action_del.gif',
'#default_value' => 'Delete' ,
'#title' => t('Delete this item'),
'#name' => 'delete_' . $op_index,
);
?>
Here, $op_index is an index from my foreach loop i use to generate a sequence of groups in my long form. Each group of items can be moved up, down or deleted from the form by user using icons (images) . I have similar elements for 'up', 'down' and 'delete'.
Before concatenating '#name' => 'delete_' . '$op_index' there was no way to tell which group user wants to move or delete in hook_submit().
Now IE returns me ready to use unique button name: [delete_1_x] or [delete_2_x] etc. :
<?php
function hook_form_submit($form_id, $form_values) {
/****************************************************************************
* we use $_POST to avoid IE annoyance: IE doesn't pass values from image inputs!
* so we cannot use [op] from an image button.
* As a workaround, the #name attribute of all image buttons contains operation
* and group index: "down_" . $op_index. IE returns coordinates using this name:
* [down_1_x] => 8, [down_1_y] => 5 etc.
* We have to parse all $_POST variables from this form to capture this information
*****************************************************************************/
if (!$form_values['op']) {
foreach($_POST as $post_var => $post_data) {
if (substr($post_var,-2) == "_x") { // x coordinate variable
$op = explode("_", $post_var);
switch ($op[0]) { // $op[0] is operation name, $op[1] - button index
case 'delete':
drupal_set_message($op[0] . ' index: ' . $op[1]);
break;
case 'up':
drupal_set_message($op[0] . ' index: ' . $op[1]);
break;
case 'down':
drupal_set_message($op[0] . ' index: ' . $op[1]);
break;
}
}
}
}
if ($form_values['op']) {
// Put you standard Submit button processing here;
}
}
?>
Now it works with all browsers for any type of operations and number of repeating blocks.
Hope this helps.