I am creating a node module, and I have a form that contains a file field. This file field is processed during my form's top level #submit handler. Since hook_validate() is called prior to my submit handler, I cannot use hook_validate() to validate the file field. For example, what if during the submit handler, the file upload fails? In that case, I need to set a form error and rebuild the form. I thought of 2 solutions for this, but neither one is working for me.

(1) I initially assumed that I could call form_set_error() in my submit handler and the form would rebuild and the node creation process would stop, but this is not the case: the form does not rebuild and the node content is created in the database.

(2) I tried using hook_nodeapi() to catch the error: when $op == 'presave' and the file did not upload correctly, I call form_set_error(). However, again, this did not rebuild the form or stop the node creation processes.

I tried to get method (1) to, at least, rebuild the form by setting $form_state['rebuild'] = TRUE, but that did not work; however, setting $form_state['storage'] does cause a form rebuild (but the node is still created in the database when the 'save' button is pressed).

I find it odd that calling form_set_error() from a submit handler will display a message to the user, but will not rebuild the form or stop the node creation process. Could someone explain to me why this is?

Am I approaching this issue correctly or is there a standard way of handling this?

Thanks for you help.

Comments

gforce301’s picture

The part I think you are misunderstanding is that before the hook_validate is called the technical upload of the file has already completed. The file and all other form data has been sent to the server, therefore the file field can and should be validated in hook_validate. Then in hook_submit (since the node has passed validation) you can take care of moving the temp file that is created during upload to where you are storing the file permanently.

sweb’s picture

Thanks for your reply.

Yes, I think you are right if I follow the pattern of the upload module: have the user click 'browse', then click 'upload', then click 'save'. In that case, I can execute my submit handler (and upload the file) when the user clicks 'upload' ... and then I can validate the upload when the user clicks 'save'. Thanks for pointing that out because that now works for me and gives me a workable solution.

However, I was trying to make a more streamlined form (with only 1 file upload) that allows the user to type a title, click 'browse' and select a file, then click 'save' (skipping the 'upload' button entirely). In this scenario, I only get one chance to validate: before the submit handler.

Am I stuck using the "browse, upload, save" pattern? Or is there a solution for the "browse, save" pattern?

Thanks again.

gforce301’s picture

In the upload module a user is not required to click the "upload" button. You can "browse" and then just click save.

sweb’s picture

Yes, you are right. In the upload module, if you 'browse' for a file, then directly hit 'save', it will upload your file ... providing there are no errors.

However, if there are errors, it will report the error, but it will create the node anyway. If I was a user, I would expect that it would report the error and rebuild the form without creating the node. So, as a user, to fix an upload error like this, I would then have to hit 'edit' and try again ... maybe it's just me, but that seems a bit odd. I would expect that on any form errors the form would rebuild.

FYI, I tested this by using a regular user account, browsing to a exe file (my site blocks those on upload), then (skipping the 'upload' button) I directly hit the 'save' button.

For my project, each node will have exactly 1 file attachment; so, I suppose I could force the user to first hit 'upload' by initially hiding the 'save' button. Then, I would reveal the 'save' button after the file has been successfully uploaded and validated. Of course, I started out looking for a way to avoid the extra click of the 'upload' button.

sweb’s picture

I have discovered a work around: use the after_build handler to upload the file. Although it is not consistent with the intention of the Drupal after_build handler, it does get the job done. The after_build handler occurs just before the validate handler, and it receives $form_state by reference.

So, you can upload the file and store the results (if the file uploads correctly) in something like $form_state['values']['upload_file']. Then, Drupal will build the $node object using $form_state['values'], and pass $node to your validate handler. Now, you can check $node for the presence 'upload_file' ... if it exists, you are OK; if not, call form_set_error().

Of course, you could also pass more specific errors in the after_build handler by setting something like $form_state['values']['errors'].

If anyone has a cleaner solution to this, I would like to hear it.

Thanks to gforce301 for the insights.

gforce301’s picture

That is a pretty good solution. I was reading some forms api stuff and found this --> #element_validate

What it describes is custom validate functions for a single form element. I have not tested this but it might do what you need.