Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active February 20, 2022 11:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/3501016b0d44af45d878067b1856e023 to your computer and use it in GitHub Desktop.
Save westonruter/3501016b0d44af45d878067b1856e023 to your computer and use it in GitHub Desktop.
Initial support for forms provided by Contact Form 7 when used with the official AMP plugin
<?php
/**
* Plugin Name: AMP Contact Form 7 Support
*
* @package AMP_Contact_Form_7
* @author Weston Ruter, Google
* @license GPL-2.0-or-later
* @copyright 2019 Google Inc.
*
* @wordpress-plugin
* Plugin Name: AMP Contact Form 7 Support
* Description: Ensure that success/error messages are displayed when submitting forms provided by Contact Form 7 on AMP pages generated by the <a href="https://wordpress.org/plugins/amp/">official AMP plugin</a>.
* Plugin URI: https://gist.github.com/westonruter/3501016b0d44af45d878067b1856e023
* Version: 0.3.0
* Author: Weston Ruter, Google
* Author URI: https://weston.ruter.net/
* License: GNU General Public License v2 (or later)
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Gist Plugin URI: https://gist.github.com/westonruter/3501016b0d44af45d878067b1856e023
*/
namespace AMP_Contact_Form_7;
/**
* Determine whether the current page is AMP.
*
* @return bool Whether it is an AMP endpoint.
*/
function is_amp_endpoint() {
return function_exists( 'is_amp_endpoint' ) && \is_amp_endpoint();
}
/**
* Dequeue scripts on AMP responses.
*
* @see \wpcf7_do_enqueue_scripts()
*/
function dequeue_scripts_on_amp_responses() {
if ( is_amp_endpoint() ) {
wp_dequeue_script( 'contact-form-7' );
}
}
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\dequeue_scripts_on_amp_responses', 20 );
/**
* Filter whether novalidate attribute is used on the form.
*
* @param bool $novalidate Use form novalidate.
* @return bool Whether to use novalidate.
*/
function filter_form_novalidate( $novalidate ) {
if ( is_amp_endpoint() ) {
$novalidate = false;
}
return $novalidate;
}
add_filter( 'wpcf7_form_novalidate', __NAMESPACE__ . '\filter_form_novalidate' );
/**
* Replace aria-required="true" with required on AMP pages, since client-side jQuery validation will not work in AMP.
*
* @param string $elements Form elements.
* @return string Filtered form elements.
*/
function filter_form_elements_for_required( $elements ) {
if ( is_amp_endpoint() ) {
$elements = str_replace( 'aria-required="true"', 'required', $elements );
}
return $elements;
}
add_filter( 'wpcf7_form_elements', __NAMESPACE__ . '\filter_form_elements_for_required' );
/**
* Handle submission.
*
* @param \WPCF7_ContactForm $contact_form Contact Form.
* @param array $result Result.
*/
function handle_submit( $contact_form, $result ) {
if ( ! is_amp_endpoint() ) {
return;
}
unset( $contact_form );
if ( ! function_exists( 'wp_is_json_request' ) ) {
_doing_it_wrong( __FUNCTION__, 'Please update to WordPress 5.0', '0.1' );
return;
}
if ( wp_is_json_request() ) {
$success = 'mail_sent' === $result['status'];
$response = array(
'message' => $result['message'],
);
// @todo Add full support for verify-xhr.
if ( ! empty( $result['invalid_fields'] ) ) {
foreach ( $result['invalid_fields'] as $name => $invalid_field ) {
$response['verifyErrors'][] = array(
'name' => $name,
'message' => $invalid_field['reason'],
);
}
}
wp_send_json(
$response,
$success ? 200 : 400
);
}
}
add_action( 'wpcf7_submit', __NAMESPACE__ . '\handle_submit', 11, 2 );
/**
* Add RECAPTCHA support.
*/
function maybe_add_recaptcha_support() {
if ( ! class_exists( '\WPCF7_RECAPTCHA' ) ) {
return;
}
$service = \WPCF7_RECAPTCHA::get_instance();
if ( ! $service->is_active() ) {
return;
}
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\handle_recaptcha_scripts_on_amp_responses', 20 );
add_filter( 'wpcf7_form_hidden_fields', __NAMESPACE__ . '\filter_recaptcha_hidden_fields', 200, 1 );
add_filter( 'wpcf7_form_elements', __NAMESPACE__ . '\filter_form_elements_for_recaptcha' );
}
add_action( 'wpcf7_init', __NAMESPACE__ . '\maybe_add_recaptcha_support' );
/**
* Dequeue RECAPTCHA scripts on AMP pages and add amp-recaptcha-input script.
*
* @see \wpcf7_do_enqueue_scripts()
*/
function handle_recaptcha_scripts_on_amp_responses() {
if ( is_amp_endpoint() ) {
wp_dequeue_script( 'google-recaptcha' );
wp_dequeue_script( 'wpcf7-recaptcha' );
wp_enqueue_script( 'amp-recaptcha-input', 'https://cdn.ampproject.org/v0/amp-recaptcha-input-0.1.js' );
}
}
/**
* Remove hidden field for recaptcha on AMP pages.
*
* @param array $fields Form hidden fields.
* @return array Filtered form hidden fields.
*/
function filter_recaptcha_hidden_fields( $fields ) {
if ( is_amp_endpoint() ) {
unset( $fields['_wpcf7_recaptcha_response'] );
}
return $fields;
}
/**
* Add amp-recaptcha-input on AMP pages.
*
* @param string $elements Form elements.
* @return string Filtered form elements.
*/
function filter_form_elements_for_recaptcha( $elements ) {
if ( ! is_amp_endpoint() ) {
return $elements;
}
$actions = apply_filters( 'wpcf7_recaptcha_actions', array(
'homepage' => 'homepage',
'contactform' => 'contactform',
) );
if ( ! isset( $actions['contactform'] ) ) {
return $elements;
}
$service = \WPCF7_RECAPTCHA::get_instance();
return sprintf(
'<amp-recaptcha-input layout="nodisplay" name="_wpcf7_recaptcha_response" data-sitekey="%s" data-action="%s"></amp-recaptcha-input>',
esc_attr( $service->get_sitekey() ),
esc_attr( $actions['contactform'] )
) . $elements;
}
@rsmith4321
Copy link

I can't believe this works! I've been searching for a long time for a way to get contact forms working in AMP. My favorite is Contact Form 7 but I've never got it working. I wish there was a way to get this to show up first in Google when searching for AMP Contact Form 7. Could you please add this to the official wordpress plugins directory? Please try to keep this updated it's greatly appreciated.

@westonruter
Copy link
Author

@rsmith4321 The ideal is for Contact Form 7 to include support for this directly, perhaps as another module, similar to how it has modules for Jetpack, Flamingo, Akismet, etc.

@rsmith4321
Copy link

I will try to mention this code if I see anyone looking for a solution. If CF7 isn't going to add this as a module, perhaps you could reconsider making a plugin. There is basically only an expensive paid plugin as an alternative. It would solve the last real issue with switching to native amp in wordpress. One other question, would you have a suggestion to how to do a redirect to a url on form submit? Without javascript I don't know how this could be done.

@westonruter
Copy link
Author

@rsmith4321 Redirection would be easy to do. You can use this PHP code in your custom theme's functions.php or a custom plugin:

add_action(
	'wpcf7_submit',
	function ( \WPCF7_ContactForm $contact_form, $result ) {
		if ( wp_is_json_request() && 'mail_sent' === $result['status'] ) {
			wp_safe_redirect( home_url( '/some-success-page/' ) );
			exit;
		}
	},
	9, // Before \AMP_Contact_Form_7\handle_submit().
	2
);

You could switch between various redirection URLs based on the value of $contact_form->id().

@rsmith4321
Copy link

Great thanks. I was also having some other issues. It doesn't seem like Flamingo database will work when using amp. Also I think to get recaptcha v3 working it would require modifying the form fields so that might be beyond what you can do without support from CF7. But if it was possible to get Flamingo and Recaptcha working correctly with CF7 that would be great.

@Gonzcarrasco
Copy link

@rsmith4321 Redirection would be easy to do. You can use this PHP code in your custom theme's functions.php or a custom plugin:

add_action(
	'wpcf7_submit',
	function ( \WPCF7_ContactForm $contact_form, $result ) {
		if ( wp_is_json_request() && 'mail_sent' === $result['status'] ) {
			wp_safe_redirect( home_url( '/some-success-page/' ) );
			exit;
		}
	},
	9, // Before \AMP_Contact_Form_7\handle_submit().
	2
);

You could switch between various redirection URLs based on the value of $contact_form->id().

Hello! and thanks :)
I tried to implement this plugin, but i have a question. We have various page´s whit forms that i need redirects to thanks page, you mention use '$contact_form->id()', where i can add this code to set differents thanks page´s for each form?

Thanks so much for you answer

@westonruter
Copy link
Author

For example:

if ( wp_is_json_request() && 'mail_sent' === $result['status'] && $contact_form->id() === $some_form_id ) {

@westonruter
Copy link
Author

@westonruter
Copy link
Author

@maharrax You've already raised this concern. Responded here: ampproject/amp-wp#1356 (comment)

@Invader444
Copy link

Invader444 commented Jan 9, 2021

Thanks @westonruter for this! I have made an adjustment to handle_submit that was required for me (I forget the exact reason but I believe it was breaking ajax functionality on non-AMP pages) as well as added reCaptcha integration support! Here is my modification, if you would like to update the snippet above :)

The handle_submit change is to first check for is_amp_endpoint() and return immediately if false.

/**
 * Handle submission.
 *
 * @param \WPCF7_ContactForm $contact_form Contact Form.
 * @param array              $result       Result.
 */
function handle_submit( $contact_form, $result ) {
	if ( ! is_amp_endpoint() ) {
		return;
	}

	unset( $contact_form );

	if ( ! function_exists( 'wp_is_json_request' ) ) {
		_doing_it_wrong( __FUNCTION__, 'Please update to WordPress 5.0', '0.1' );
		return;
	}

	if ( wp_is_json_request() ) {
		$success = 'mail_sent' === $result['status'];

		$response = array(
			'message' => $result['message'],
		);

		// @todo Add full support for verify-xhr.
		if ( ! empty( $result['invalid_fields'] ) ) {
			foreach ( $result['invalid_fields'] as $name => $invalid_field ) {
				$response['verifyErrors'][] = array(
					'name'    => $name,
					'message' => $invalid_field['reason'],
				);
			}
		}

		wp_send_json(
			$response,
			$success ? 200 : 400
		);
	}
}
add_action( 'wpcf7_submit', __NAMESPACE__ . '\handle_submit', 11, 2 );

/**
 * Add RECAPTCHA support.
 */
function maybe_add_recaptcha_support() {
	if ( ! class_exists( '\WPCF7_RECAPTCHA' ) ) {
		return;
	}

	$service = \WPCF7_RECAPTCHA::get_instance();

	if ( ! $service->is_active() ) {
		return;
	}

	add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\handle_recaptcha_scripts_on_amp_responses', 20 );
	add_filter( 'wpcf7_form_hidden_fields', __NAMESPACE__ . '\filter_recaptcha_hidden_fields', 200, 1 );
	add_filter( 'wpcf7_form_elements', __NAMESPACE__ . '\filter_form_elements_for_recaptcha' );
}
add_action( 'wpcf7_init', __NAMESPACE__ . '\maybe_add_recaptcha_support' );

/**
 * Dequeue RECAPTCHA scripts on AMP pages and add amp-recaptcha-input script.
 *
 * @see \wpcf7_do_enqueue_scripts()
 */
function handle_recaptcha_scripts_on_amp_responses() {
	if ( is_amp_endpoint() ) {
		wp_dequeue_script( 'google-recaptcha' );
		wp_dequeue_script( 'wpcf7-recaptcha' );
		wp_enqueue_script( 'amp-recaptcha-input', 'https://cdn.ampproject.org/v0/amp-recaptcha-input-0.1.js' );
	}
}


/**
 * Remove hidden field for recaptcha on AMP pages.
 *
 * @param array $fields Form hidden fields.
 * @return array Filtered form hidden fields.
 */
function filter_recaptcha_hidden_fields( $fields ) {
	if ( is_amp_endpoint() ) {
		unset( $fields['_wpcf7_recaptcha_response'] );
	}

	return $fields;
}

/**
 * Add amp-recaptcha-input on AMP pages.
 *
 * @param string $elements Form elements.
 * @return string Filtered form elements.
 */
function filter_form_elements_for_recaptcha( $elements ) {
	if ( ! is_amp_endpoint() ) {
		return $elements;
	}

	$actions = apply_filters( 'wpcf7_recaptcha_actions', array(
		'homepage' => 'homepage',
		'contactform' => 'contactform',
	) );

	if ( ! isset( $actions['contactform'] ) ) {
		return $elements;
	}

	$service = \WPCF7_RECAPTCHA::get_instance();

	return sprintf(
		'<amp-recaptcha-input layout="nodisplay" name="_wpcf7_recaptcha_response" data-sitekey="%s" data-action="%s"></amp-recaptcha-input>',
		esc_attr( $service->get_sitekey() ),
		esc_attr( $actions['contactform'] )
	) . $elements;
}

@westonruter
Copy link
Author

@Invader444 Cool! I've committed your revision: https://gist.github.com/westonruter/3501016b0d44af45d878067b1856e023/revisions#diff-600d6b0eae3f931155e800b523327eab18f03f13f5e186d99b9d39612764dccaHR192

I haven't tested it though. Nevertheless, it looks good. I'll trust it works based on your testing.

@Invader444
Copy link

@Invader444 Cool! I've committed your revision: https://gist.github.com/westonruter/3501016b0d44af45d878067b1856e023/revisions#diff-600d6b0eae3f931155e800b523327eab18f03f13f5e186d99b9d39612764dccaHR192

Awesome!

I haven't tested it though. Nevertheless, it looks good. I'll trust it works based on your testing.

Famous last words! ;)

I have tested it only on the latest CF7 and AMP plugin versions, 5.3.2 and 2.0.9 respectively, and WordPress 5.6. I do not know the extent of backwards compatibility for older versions; AMP itself only supports recaptcha v3 it seems (as layout attribute for amp-recaptcha-input only supports the nodisplay value), which would limit it to CF7 versions supporting recaptcha v3, at the very least.

Normally I would try to make it more robust, but the above works for my requirements and figured I would share :)

@Invader444
Copy link

Invader444 commented Jan 11, 2021

Also, for anyone coming here, be aware that after creating a v3 recaptcha site token, you need to go back into its settings to enable its use via AMP! That option is not on the initial setup page and is currently (at the time of writing) disabled by default. Tripped me up at first :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment