Skip to content

Commit

Permalink
Merge pull request #923 from Automattic/add/911-support-form-post-sub…
Browse files Browse the repository at this point in the history
…missions

Handle general form posts
  • Loading branch information
westonruter authored and Ryan Kienstra committed Feb 7, 2018
2 parents 215860d + ee75de2 commit a493975
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 17 deletions.
5 changes: 1 addition & 4 deletions amp.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ function amp_after_setup_theme() {
* @global string $pagenow
*/
function amp_init() {
global $pagenow;

/**
* Triggers on init when AMP plugin is active.
Expand All @@ -127,9 +126,7 @@ function amp_init() {
if ( class_exists( 'Jetpack' ) && ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
require_once AMP__DIR__ . '/jetpack-helper.php';
}
if ( isset( $pagenow ) && 'wp-comments-post.php' === $pagenow ) {
amp_prepare_comment_post();
}
amp_handle_xhr_request();
}

// Make sure the `amp` query var has an explicit value.
Expand Down
71 changes: 61 additions & 10 deletions includes/amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,23 +485,36 @@ function amp_print_schemaorg_metadata() {
}

/**
* Hook into a comment submission of an AMP XHR post request.
*
* This only runs on wp-comments-post.php.
* Hook into a form submissions, such as comment the form or some other .
*
* @since 0.7.0
* @global string $pagenow
*/
function amp_prepare_comment_post() {
function amp_handle_xhr_request() {
global $pagenow;
if ( ! isset( $_GET['__amp_source_origin'] ) ) { // WPCS: CSRF ok. Beware of AMP_Theme_Support::purge_amp_query_vars().
return;
}

// Add amp comment hooks.
add_filter( 'comment_post_redirect', function() {
if ( isset( $pagenow ) && 'wp-comments-post.php' === $pagenow ) {
// We don't need any data, so just send a success.
wp_send_json_success();
}, PHP_INT_MAX, 2 );
add_filter( 'comment_post_redirect', function() {
// We don't need any data, so just send a success.
wp_send_json_success();
}, PHP_INT_MAX, 2 );
amp_handle_xhr_headers_output();
} elseif ( isset( $_GET['_wp_amp_action_xhr_converted'] ) ) { // WPCS: CSRF ok.
add_filter( 'wp_redirect', 'amp_intercept_post_request_redirect', PHP_INT_MAX, 2 );
amp_handle_xhr_headers_output();
}
}

/**
* Handle the AMP XHR headers and output errors.
*
* @since 0.7.0
*/
function amp_handle_xhr_headers_output() {
// Add die handler for AMP error display.
add_filter( 'wp_die_handler', function() {
/**
Expand All @@ -514,12 +527,50 @@ function amp_prepare_comment_post() {
if ( is_wp_error( $error ) ) {
$error = $error->get_error_message();
}
$error = strip_tags( $error, 'strong' );
wp_send_json( compact( 'error' ) );
$amp_mustache_allowed_html_tags = array( 'strong', 'b', 'em', 'i', 'u', 's', 'small', 'mark', 'del', 'ins', 'sup', 'sub' );
wp_send_json( array(
'error' => wp_kses( $error, array_fill_keys( $amp_mustache_allowed_html_tags, array() ) ),
) );
};
} );

// Send AMP header.
$origin = esc_url_raw( wp_unslash( $_GET['__amp_source_origin'] ) ); // WPCS: CSRF ok.
header( 'AMP-Access-Control-Allow-Source-Origin: ' . $origin, true );
}

/**
* Intercept the response to a non-comment POST request.
*
* @since 0.7.0
* @param string $location The location to redirect to.
*/
function amp_intercept_post_request_redirect( $location ) {

// Make sure relative redirects get made absolute.
$parsed_location = array_merge(
array(
'scheme' => 'https',
'host' => wp_parse_url( home_url(), PHP_URL_HOST ),
'path' => strtok( wp_unslash( $_SERVER['REQUEST_URI'] ), '?' ),
),
wp_parse_url( $location )
);

$absolute_location = $parsed_location['scheme'] . '://' . $parsed_location['host'];
if ( isset( $parsed_location['port'] ) ) {
$absolute_location .= ':' . $parsed_location['port'];
}
$absolute_location .= $parsed_location['path'];
if ( isset( $parsed_location['query'] ) ) {
$absolute_location .= '?' . $parsed_location['query'];
}
if ( isset( $parsed_location['fragment'] ) ) {
$absolute_location .= '#' . $parsed_location['fragment'];
}

header( 'AMP-Redirect-To: ' . $absolute_location );
header( 'Access-Control-Expose-Headers: AMP-Redirect-To' );
// Send json success as no data is required.
wp_send_json_success();
}
3 changes: 2 additions & 1 deletion includes/class-amp-theme-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static function init() {
self::register_paired_hooks();
}

self::purge_amp_query_vars(); // Note that amp_prepare_comment_post() still looks at $_GET['__amp_source_origin'].
self::purge_amp_query_vars(); // Note that amp_prepare_xhr_post() still looks at $_GET['__amp_source_origin'].
self::register_hooks();
self::$embed_handlers = self::register_content_embed_handlers();
self::$sanitizer_classes = amp_get_content_sanitizers();
Expand Down Expand Up @@ -202,6 +202,7 @@ public static function register_hooks() {
public static function purge_amp_query_vars() {
$query_vars = array(
'__amp_source_origin',
'_wp_amp_action_xhr_converted',
'amp_latest_update_time',
);

Expand Down
30 changes: 30 additions & 0 deletions includes/sanitizers/class-amp-form-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ public function sanitize() {
} elseif ( 'post' === $method ) {
$node->removeAttribute( 'action' );
if ( ! $xhr_action ) {
// record that action was converted tp action-xhr.
$action_url = add_query_arg( '_wp_amp_action_xhr_converted', 1, $action_url );
$node->setAttribute( 'action-xhr', $action_url );
// Append error handler if not found.
$this->ensure_submit_error_element( $node );
} elseif ( 'http://' === substr( $xhr_action, 0, 7 ) ) {
$node->setAttribute( 'action-xhr', substr( $xhr_action, 5 ) );
}
Expand All @@ -108,4 +112,30 @@ public function sanitize() {
}
}
}

/**
* Checks if the form has an error handler else create one if not.
*
* @link https://www.ampproject.org/docs/reference/components/amp-form#success/error-response-rendering
* @since 0.7
*
* @param DOMElement $form The form node to check.
*/
public function ensure_submit_error_element( $form ) {
$templates = $form->getElementsByTagName( 'template' );
for ( $i = $templates->length - 1; $i >= 0; $i-- ) {
if ( $templates->item( $i )->parentNode->hasAttribute( 'submit-error' ) ) {
return; // Found error template, do nothing.
}
}

$div = $this->dom->createElement( 'div' );
$template = $this->dom->createElement( 'template' );
$mustache = $this->dom->createTextNode( '{{{error}}}' );
$div->setAttribute( 'submit-error', '' );
$template->setAttribute( 'type', 'amp-mustache' );
$template->appendChild( $mustache );
$div->appendChild( $template );
$form->appendChild( $div );
}
}
4 changes: 2 additions & 2 deletions tests/test-amp-form-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function get_data() {
),
'form_with_post_method_http_action_and_no_target' => array(
'<form method="post" action="http://example.org/example-page/"></form>',
'<form method="post" action-xhr="//example.org/example-page/" target="_top"></form>',
'<form method="post" action-xhr="//example.org/example-page/?_wp_amp_action_xhr_converted=1" target="_top"><div submit-error=""><template type="amp-mustache">{{{error}}}</template></div></form>',
),
'form_with_post_method_http_action_and_blank_target' => array(
'<form method="post" action-xhr="http://example.org/example-page/" target="_blank"></form>',
Expand All @@ -58,7 +58,7 @@ public function get_data() {
),
'form_with_post_method_https_action_and_custom_target' => array(
'<form method="post" action="https://example.org/" target="some_other_target"></form>',
'<form method="post" target="_blank" action-xhr="https://example.org/"></form>',
'<form method="post" target="_blank" action-xhr="https://example.org/?_wp_amp_action_xhr_converted=1"><div submit-error=""><template type="amp-mustache">{{{error}}}</template></div></form>',
),
);
}
Expand Down
65 changes: 65 additions & 0 deletions tests/test-amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,69 @@ public function test_amp_get_schemaorg_metadata() {
$this->assertArrayHasKey( 'did_amp_schemaorg_metadata', $metadata );
$this->assertEquals( 'George', $metadata['author']['name'] );
}

/**
* Test amp_intercept_post_request_redirect().
*
* @runInSeparateProcess
* @preserveGlobalState disabled
* @covers amp_intercept_post_request_redirect()
*/
public function test_amp_intercept_post_request_redirect() {
if ( ! function_exists( 'xdebug_get_headers' ) ) {
$this->markTestSkipped( 'xdebug is required for this test' );
}

add_theme_support( 'amp' );
$url = get_home_url();

add_filter( 'wp_doing_ajax', '__return_true' );
add_filter( 'wp_die_ajax_handler', function () {
return '__return_false';
} );

ob_start();
amp_intercept_post_request_redirect( $url );
$this->assertEquals( '{"success":true}', ob_get_clean() );

$this->assertContains( 'AMP-Redirect-To: ' . $url, xdebug_get_headers() );
$this->assertContains( 'Access-Control-Expose-Headers: AMP-Redirect-To', xdebug_get_headers() );

ob_start();
amp_intercept_post_request_redirect( '/new-location/' );
$this->assertEquals( '{"success":true}', ob_get_clean() );
$this->assertContains( 'AMP-Redirect-To: https://example.org/new-location/', xdebug_get_headers() );

ob_start();
amp_intercept_post_request_redirect( '//example.com/new-location/' );
$this->assertEquals( '{"success":true}', ob_get_clean() );
$headers = xdebug_get_headers();
$this->assertContains( 'AMP-Redirect-To: https://example.com/new-location/', $headers );

ob_start();
amp_intercept_post_request_redirect( '' );
$this->assertEquals( '{"success":true}', ob_get_clean() );
$this->assertContains( 'AMP-Redirect-To: https://example.org', xdebug_get_headers() );
}

/**
* Test amp_handle_xhr_request().
*
* @runInSeparateProcess
* @preserveGlobalState disabled
* @covers amp_handle_xhr_headers_output()
*/
public function test_amp_handle_xhr_request() {
global $pagenow;
if ( ! function_exists( 'xdebug_get_headers' ) ) {
$this->markTestSkipped( 'xdebug is required for this test' );
}

$_GET['__amp_source_origin'] = 'https://example.org';
$pagenow = 'wp-comments-post.php';

amp_handle_xhr_request();
$this->assertContains( 'AMP-Access-Control-Allow-Source-Origin: https://example.org', xdebug_get_headers() );

}
}

0 comments on commit a493975

Please sign in to comment.