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;
}
@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