Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add opt-in to prevent POST forms from being converted to amp-form (with action-xhr) #6527

Merged
merged 8 commits into from
Aug 11, 2021

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Aug 11, 2021

Summary

See #6443.

One of the very frequent issues encountered by users of the AMP plugin is POST form submissions not behaving properly. In fact, we have a support page all about handling form submissions. The issue is that AMP requires POST forms to be submitted over XHR rather than a traditional page navigation, but this breaks if the endpoint sends back a Location header to redirect or if the endpoint is on another host that doesn't send CORS headers. There is a feature request to allow non-XHR POST forms in AMP (ampproject/amphtml#27638), but for now we should allow an opt-in to prevent conversion of such POST forms and to omit the amp-form component. This amp-form component must be excluded or else it will intercept any submissions on POST forms to block them and show an error message.

To opt-in to traditional non-XHR POST forms, do the following:

add_filter( 'amp_native_post_form_allowed', '__return_true' );

When this is done, page that have such a form will now emit a new validation error: FORM_HAS_POST_METHOD. When the user marks this validation error as kept, then:

  1. Conversion of the form is skipped.
  2. The data-ampdevmode attribute is added to the form element and the html root element (to prevent removal).
  3. The amp-form component script is omitted from the page.

A user may decide to opt-in to preventing any such POST form from being converted by default via the following code:

add_filter(
	'amp_validation_error_default_sanitized',
	static function ( $sanitized, $error ) {
		if ( isset( $error['code'] ) && 'FORM_HAS_POST_METHOD_WITHOUT_ACTION_XHR_ATTR' === $error['code'] ) {
			$sanitized = false;
		}
		return $sanitized;
	},
	10,
	2
);

Screen Shot 2021-08-10 at 20 27 35

Checklist

  • My code is tested and passes existing tests.
  • My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).

@westonruter westonruter added this to the v2.2 milestone Aug 11, 2021
Base automatically changed from add/native-img-opt-in to develop August 11, 2021 02:33
@westonruter westonruter force-pushed the add/post-form-allowance branch from 531de25 to 6550aa1 Compare August 11, 2021 02:34
@westonruter westonruter marked this pull request as ready for review August 11, 2021 02:34
@westonruter westonruter requested a review from pierlon August 11, 2021 02:34
@github-actions
Copy link
Contributor

github-actions bot commented Aug 11, 2021

Plugin builds for ef4653e are ready 🛎️!

@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

Noticed a bug while testing this; given the HTML:

<form method="get" action-xhr="https://example.com"></form>

It will be sanitized to become:

<form method="get" action-xhr="https://example.com" action="//amp-dev.test/test-6527/?amp=1" target="_top" rel="amphtml" novalidate="" class="i-amphtml-form"><input name="amp" value="1" type="hidden"></form>

The action attribute should not be in the output.

@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

When the amp_allowing_native_post_forms filter returns true and given the markup:

<form method="post" action-xhr="https://example.com"></form>

Marking the validation error for that markup to "Kept" produces:

<form method="post" action-xhr="https://example.com" data-ampdevmode="" novalidate="" class="i-amphtml-form"></form>

Should the action-xhr attribute instead be attribute, or is it on the user for using action-xhr?

*
* @param bool $use_native Whether to allow native `POST` forms.
*/
return (bool) apply_filters( 'amp_allowing_native_post_forms', false );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for using allowing instead of allow (or allows)? Just wondering since we don't have any hooks that use present participle verbs, AFAIK.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's amp_using_native_img 😄

If you think the filters make more sense as amp_use_native_img and amp_allow_native_form then that I'm happy with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had gone with singular for img since imgs looks bad.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then what about the function names?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think the filters make more sense as amp_use_native_img and amp_allow_native_form then that I'm happy with that.

I was only concerned with it as it doesn't follow the usual naming conventions we use for our hooks, but either naming works for me 😄.

But then what about the function names?

They can stay if we're keeping the hook names.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about:

Function Filter
amp_is_native_img_used() amp_native_img_used
amp_is_native_post_form_allowed() amp_native_post_form_allowed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on board with that.

@@ -1514,6 +1513,11 @@ public static function ensure_required_markup( Document $dom, $script_handles =
$superfluous_script_handles = array_diff( $superfluous_script_handles, [ 'amp-carousel' ] );
}

// When opting-in to POST forms, omit the amp-form component entirely since it blocks submission.
if ( amp_is_allowing_native_post_forms() && $dom->xpath->query( '//form[ @method = "post" and @action ]' )->length > 0 ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the value of @method be case sensitive here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in dfb5223.

@westonruter
Copy link
Member Author

Noticed a bug while testing this; given the HTML:

<form method="get" action-xhr="https://example.com"></form>

It will be sanitized to become:

<form method="get" action-xhr="https://example.com" action="//amp-dev.test/test-6527/?amp=1" target="_top" rel="amphtml" novalidate="" class="i-amphtml-form"><input name="amp" value="1" type="hidden"></form>

The action attribute should not be in the output.

That's tricky. It turns out in the case of a get form, it's the action attribute that's required and not the action-xhr. You can provide both, but the action-xhr is optional.

See the above is valid (playground):

image

Attempting to omit the action attribute results in an error:

image

@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

I see, that makes sense. The docs also confirm using both action and action-xhr for GET requests:

This attribute is required for method=POST, and is optional for method=GET.

The value for action-xhr can be the same or a different endpoint than action and has the same action requirements above.

@westonruter
Copy link
Member Author

When the amp_allowing_native_post_forms filter returns true and given the markup:

<form method="post" action-xhr="https://example.com"></form>

Marking the validation error for that markup to "Kept" produces:

<form method="post" action-xhr="https://example.com" data-ampdevmode="" novalidate="" class="i-amphtml-form"></form>

Should the action-xhr attribute instead be attribute, or is it on the user for using action-xhr?

Oops. Yeah, POST forms with action-xhr should be skipped. I believe this is fixed as of a017973.

Copy link
Contributor

@pierlon pierlon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :shipit:

@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

Oh wait there seems to be some failing PHP tests.

@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

Oh wait there seems to be some failing PHP tests.

The failing test highlighted a bug in Core. The issue resides in this block of code, where there is no check to verify $res is not a WP_Error before operating on it, which is why the tests throw the error:

Undefined property: WP_Error::$themes

@westonruter
Copy link
Member Author

Right, so it's a network issue.

@westonruter westonruter merged commit 8898527 into develop Aug 11, 2021
@westonruter westonruter deleted the add/post-form-allowance branch August 11, 2021 06:24
@pierlon
Copy link
Contributor

pierlon commented Aug 11, 2021

The failing test highlighted a bug in Core...

Created WordPress/wordpress-develop#1571 to fix the issue and it was just recently merged.

@milindmore22
Copy link
Collaborator

QA Passed

Input for Post method
<form method="post" action-xhr="https://example.com"></form>
Output for post method
<form method="post" action-xhr="https://example.com" target="_top" novalidate="" class="i-amphtml-form"></form>

Input for Get Method
<form method="get" action-xhr="https://example.com"></form>
Output for Post method
<form method="get" action-xhr="https://example.com" action="//amp-local.test/form-test/amp/" target="_top" novalidate="" class="i-amphtml-form"></form>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bento Changelogged Whether the issue/PR has been added to release notes. Sandboxing Experiment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants