Skip Navigation

[Resolved] Hook 'cred_form_validate' is called twice when custom validation fails

This support ticket is created 6 years, 10 months ago. There's a good chance that you are reading advice that it now obsolete.

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- - 14:00 – 20:00 14:00 – 20:00 14:00 – 20:00 14:00 – 20:00 14:00 – 20:00
- - - - - - -

Supporter timezone: Asia/Ho_Chi_Minh (GMT+07:00)

This topic contains 17 replies, has 2 voices.

Last updated by Beda 5 years, 6 months ago.

Assisted by: Beda.

Author
Posts
#623913

Hello,

I'm building a classified ad site with Toolset.

I'm using 'cred_form_validate' hook to intercept the ad submission if the poster doesn't have enough ad credits and to display an error message.

I noticed that when the poster didn't have enough ad credits to post the ad, the hook would be called twice (bad). When they had enough credit, it was called once (good).

How can I prevent the hook from being called multiple times?

I searched your support site and found this page (https://toolset.com/errata/cred_form_validate-api-hook-called-twice-when-javascript-is-disabled/) that says the error had been fixed in CRED 1.9.4.

But I'm using 1.9.5 and still having the issue. Unlike the case above, however, JavaScript in my browser was enabled when the issue occurred.

Thank you very much.

#623970

This issue was solved, correct, but is still present if there are more than one forms.

Do you have more than one form on that page?

As well, that validation (requirement of field) is hooked to JS.
So if it's enabled, it doesn't even need PHP, as it's simply not possible to submit the form without filling a required form.

Validating that field again is double work.

So, I assume this is not the same issue we are speaking of.

Can you please explain the exact steps you take, the code you use, and when you see the issue?

I can then replicate this and provide a workaround or fix.

#624456

Hello Beda,

Thanks for your quick reply.

I checked the HTML of the page and found there were 2 forms: One was the form I created with Toolset CRED with the 'Publish' submit button to publish the ad (the one I have a problem with), and the other was for the search field in the side column that came with WordPress or maybe my theme 'Astra'. Could this be a problem?

This is my workflow:

1. User comes to the ad submission form and fills it out
2. User clicks the "Save & Preview" submit button to save and preview the ad. On this preview page, which is basically the post page itself, there is a 'Publish' submit button
3. User clicks the 'Publish' button to publish the ad, but my code would check first if they have enough ad credits to publish it

I created a dummy field called 'ad-credit-insufficient-dummy' in the CRED form for the 'Publish button' above and made the dummy field hidden. When the user doesn't have enough ad credit, my code would assign an error message to this dummy field to stop the form submission and display the error message.

This is the code I'm using for the 'Publish' button (I simplified it to make it easier to understand, but the logic is the same):

function k__can_member_publish_ad($error_fields, $form_data){

	//form with publish button and hidden dummy field only
	if ( $form_data['id'] == 100 ) {
		
		if ( get_member_available_ad_credits() > 0 ) {
			// member has enough ad credits
			
			// let it through unchanged
			return $error_fields; 
			
		} else {
			// member doesn't have enough ad credits
			
			list($fields, $errors) = $error_fields;
			
			// assign an error message to the dummy field
			$errors['wpcf-ad-credit-insufficient-dummy'] = "Please upgrade your package to get more ad credits to publish this ad.";
			
			return array($fields, $errors);
		}
	}
}

add_filter('cred_form_validate','k__can_member_publish_ad',10,2);

I hope this can help you figure out a workaround or fix. Please let me know if you need anything else.

Thanks.

chris

#624599

Yes, as stated here, the issue is still happening if more than one form is used on the same URL:
https://toolset.com/forums/topic/hook-cred_form_validate-is-called-twice-when-custom-validation-fails/#post-623970

I am not entirely sure though what you mean by the second form:

the other was for the search field in the side column that came with WordPress or maybe my theme 'Astra'

CRED is not creating Search Fields, so I do not know what you mean by this.

Anyway, if there is more than one CRED Form on the page, the bug is still happening and has no solution.
If there is only one form, then the bug is solved in the current stable release.

#624619
2_forms.png

Hello Beda,

Thanks for your reply.

Sorry, I wasn't clear before. The second form is for the WordPress search field. This form is created by WordPress or the Astra theme I'm using, not by CRED. So I have only 1 form created by CRED on the page. Please see the attached screenshot showing the 2 forms at the top of my page.

So I guess the issue still occurs when I have only 1 CRED form on the page. Is there a way I can fix this?

Please let me know if you need anything else.

Thank you very much.

#624643

OK.

I need a copy of the site, I will then debug this locally with the appropriate methods and technology, then I will update you with the solution.

This DOC explains how you can share a copy of the site:
https://toolset.com/faq/provide-supporters-copy-site/

#627275

I did not receive the link.

Please, can you paste it in the next message again?

#627774

Ok, I replicated the site, but I need instructions on what and where to look at exactly.

I have some generic feedback to the setup.

1. On the example "hidden link", when you edit that post, you will see that a Content Template AND a Template Layout is assigned.

Please remove either of both.

2. Then, in the Layout applied, it clearly shows that you have more than one CRED Form on that post.

And that is the exact issue as elaborated here:
https://toolset.com/forums/topic/hook-cred_form_validate-is-called-twice-when-custom-validation-fails/#post-623970

The issue is persisting if there is more than one form, and there are more than one form on your Post:
[cred_form form="edit-content-get-me-customers-ads-1"]
[cred_form form="unpublish-get-me-customers-ad"]

If I remove one of both (in my case, I removed the one that shows if the post is published), the issue is not happening anymore and I see the JS error log "Heloooo" only once.

#628616

Hello Beda,

Thanks for your response.

For your #1, I went to Toolset > Content Templates and clicked on "Show Pictures on Single Post - Content Template" and then unchecked "Get-Me-Customers Ads" checkbox under Usage. Is this what you meant in your #1? Forgive me, I'm still learning WordPress and Toolset.

As for your #2, there's actually 1 CRED form at a time on that page. If you look at my layout, it will say this:

      <!-- publish button -->
      [wpv-conditional if="( '[wpv-post-status]' eq 'draft' )"]
          <td>
              [cred_form form="edit-content-get-me-customers-ads-1"]
          </td>
      [/wpv-conditional]

      <!-- unpublish button -->
      [wpv-conditional if="( '[wpv-post-status]' eq 'publish' )"]
          <td>
              [cred_form form="unpublish-get-me-customers-ad"]
          </td>
      [/wpv-conditional]

So CRED form "edit-content-get-me-customers-ads-1" will appear on the page only when [wpv-post-status] = 'draft'. When [wpv-post-status] = 'publish', CRED form "unpublish-get-me-customers-ad" will appear instead.

So the 2 CRED forms never appear on the page at the same time. Or this doesn't matter?

Thank you very much.

chris

#628707

Well, as I tested and debugged in my previous report the issue is exactly as described by me and the Erratum.

There are 2 forms, and as soon you remove one, it works.

Please do this, to have the forms working.

It does not matter if you hide this with a conditional, as you will see.

Unassigning the Template like in #1 above is not correct like this.
This will just make sure the Template is not assigned to future posts, but it'll not remove it from old posts.

Old posts will need to be updated like this:
- disable Toolset Layouts
- head to the post
- remove the Content Template assignment
- Re-enable Layouts and make sure the layout is applied.

If it works right now, you may leave it as is, but it is not correct and could generate future issues

#630801

Hello Beda,

So I removed my second form from the layout:

Before:

<!-- publish button starts -->
      [wpv-conditional if="( '[wpv-post-status]' eq 'draft' )"]
          <td>
              [cred_form form="edit-content-get-me-customers-ads-1"]
          </td>
      [/wpv-conditional]
<!-- publish button ends -->

<!-- unpublish button starts -->
      [wpv-conditional if="( '[wpv-post-status]' eq 'publish' )"]
          <td>
              [cred_form form="unpublish-get-me-customers-ad"]
          </td>
      [/wpv-conditional]
<!-- unpublish button ends -->

After

<!-- publish button starts -->
      [wpv-conditional if="( '[wpv-post-status]' eq 'draft' )"]
          <td>
              [cred_form form="edit-content-get-me-customers-ads-1"]
          </td>
      [/wpv-conditional]
<!-- publish button ends -->

<!-- unpublish button starts -->
<!-- unpublish button ends -->

But I still got the code executed twice when there was an error with the form validation.

In your test, did you also remove the unpublish button?

I checked the content of the $error_fields array from the 'cred_form_validate' hook and found that when it ran the first time, it would have an array with a key called 'form_submit_1' in it. When it ran the second time, it had 'form_submit_2'.

So I created a condition that only when the $error_fields array contained a key 'form_submit_1', it should run the script. It didn't work. The result was: When there was an error from the form validation, the error message wasn't displayed and the form submission wasn't cancelled. I tried it with 'form_submit_2' but it also didn't work.

It seemed that it worked only when the script was called twice. Why is that?

If I wanted to keep the 2 forms on the page, is there a way to prevent the script from being called twice?

What is the harm from having the script called twice? What issue will it create?

Thank you, Beda!

chris

#630995

I tried to replicate your site again and the results are not the same anymore.

Now I receive one "Heloooo" and one "Could not open scrap file."

I re-read this entire ticket and I am not sure this is the same issue as described in the Errata.

I rather suspect this is another issue or even a problem with the code.

1. In this code (https://toolset.com/forums/topic/hook-cred_form_validate-is-called-twice-when-custom-validation-fails/#post-624456) I suggest you only check if "invalid" and if so apply errors, as shown here:
https://toolset.com/documentation/programmer-reference/cred-api/#cred_form_validate

What you do right now is actuall return something even the validation passed.
This is not the idea of validation. There is no need for that if/else statement.
You can just chek if the value is 0 and if so, provide the errors.
If the value is more than 0, obviously that error will autmatically not apply.

According our example you return "array($fields,$errors)" if there is a failure, not when there is a success.

2. The erratum here does not involve Custom Code:
https://toolset.com/errata/cred_form_validate-api-hook-called-twice-when-javascript-is-disabled/

It's just that the public API you use is logically alos used by CRED - but in that case, it's not related to custom code validation.
it's about the native validation done in CRED/Types.

In my previous tests i logically removed any other CRED form from the layout, but my last test do not even confirm the issue anymore.
Something changed in the Updates, I assume (as there where some pending on that package).

I tried to replicate this problem (with one form only) several times now, but could not get close to a replication on a clean install.

Is it possible to receive steps that allow me to replicate this on a simple fresh install?

It seems to me this is not replicable, as long we have one form only.
As well, this issue does not need custom valdiations to be replicated, as stated in the erratum.

The BUG I am still able to replicate (if there are more than one form on the page) is replicated as follows:

- add a Custom Field in Types and make it required
- add a CRED Form with that field
- to your functions.php you add:

add_filter('cred_form_validate','my_validation',10,2);
function my_validation($error_fields, $form_data)
{
    //field data are field values and errors
    list($fields,$errors)=$error_fields;
    //uncomment this if you want to print the field values
    //print_r($fields);
    //validate if specific form
    if ($form_data['id']==18)
    {
        //check my_field value
        error_log('validate');
    }
    //return result
    return array($fields,$errors);
}

- in the front end you submit the CRED form without passing value to the required field
- the JS validation of CRED will trigger.
- now Disable JS in the browser console and repeat the submit form step.
- Watch error log and see that cred_form_validate was run twice.

What are the steps to replicate the issue report, in a similar step by step workflow?

#631736

Hello Beda,

Thanks so much for working on this case. I really appreciate it.

Guess what? After working for days and nights on this, I think I finally found a workaround!!

As I mentioned, the hook is called twice. In the first call, $error_fields array contains a key called 'form_submit_1' and in the second call, it contains 'form_submit_2'.

I had suspected that it was the second call that actually did what my custom code was intended to do. The first call was a bug that shouldn't have occurred, I think.

So first I created this code and put it at the top of my script:

	list($fields, $errors) = $error_fields;

	if ( isset( $error_fields_sub_array['form_submit_1'] ) ) {
		// skip this script
		return array($fields, $errors); 
	} else if ( isset( $error_fields_sub_array['form_submit_2'] ) ) {
		// let it through
	}

The script above did not work. As soon as it hit "return array($fields, $errors);", it exit the whole script and the second call was never made.

After trying millions of different things, I finally found out that the second call was made only when an error was set to the $errors array (another bug?). So I added a dummy error to my dummy field, and this was my final code:

	list($fields, $errors) = $error_fields;

	if ( isset( $error_fields_sub_array['form_submit_1'] ) ) {
		// skip this script
		$errors['wpcf-oops-dummy'] = 'dumbdumb'; // <--- needs this code to force it to make the second call
		return array($fields, $errors); 
	} else if ( isset( $error_fields_sub_array['form_submit_2'] ) ) {
		// let it through
	}

It works well, I think. The hook is still called twice, but on the first call, it exits the script right away, so it doesn't perform any other actions such as database calls. On the second call, the script is executed as expected.

What do you think of this workaround? Does it have any negative impact or potential issue that I'm not aware of?

Thank you so much for your help, Beda!

chris

#631848

The scripts are called because there is more than one form, as elaborated.

Skipping the validation just as skipping the form conditionally does not solve the issue.
It hence expects something to return.

As soon this issue with the double validation is solved for good (it is with single forms) then this problem should not happen anymore.
I think by then you could revert your code.

#632165

Hello Beda,

Thanks for your feedback.

Are you sure that having 2 CRED forms is the cause of the script called twice? That's because, as I mentioned before, even after deleting the second form, the script is still called twice.

Anyway, do you know when this is going to be fixed? For every new version you release, is there a release note accompanying it that I could read so I know what has been fixed? I couldn't find it on your site.

Thanks.