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

Contact Form Block: Displays expected success message when used in FSE template, FSE template part, or widget block #24727

Merged
merged 13 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: bugfix

Contact Form Block: Displays expected success message when used in FSE header or footer
235 changes: 224 additions & 11 deletions projects/plugins/jetpack/modules/contact-form/grunion-contact-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,123 @@ function grunion_contact_form_require_endpoint() {
require_once GRUNION_PLUGIN_DIR . 'class-grunion-contact-form-endpoint.php';
}

/**
* Sets the 'block_template' attribute on all instances of wp:jetpack/contact-form in
wojtekn marked this conversation as resolved.
Show resolved Hide resolved
* the $_wp_current_template_content global variable.
*
* The $_wp_current_template_content global variable is hydrated immediately prior to
* 'template_include' in wp-includes/template-loader.php.
*
* This fixes Contact Form Blocks added to FSE _templates_ (e.g. Single or 404).
*
* @param string $template Template to be loaded.
*/
function grunion_contact_form_set_block_template_attribute( $template ) {
global $_wp_current_template_content;
if ( ABSPATH . WPINC . '/template-canvas.php' === $template ) {
$_wp_current_template_content = grunion_contact_form_apply_block_attribute(
$_wp_current_template_content,
array(
'block_template' => 'canvas',
)
);
}
return $template;
}
add_filter( 'template_include', 'grunion_contact_form_set_block_template_attribute' );

/**
* Sets the $grunion_block_template_part_id global.
*
* This is part of the fix for Contact Form Blocks added to FSE _template parts_ (e.g footer).
* The global is processed in Grunion_Contact_Form::parse().
*
* @param string $template_part_id ID for the currently rendered template part.
*/
function grunion_contact_form_set_block_template_part_id_global( $template_part_id ) {
$GLOBALS['grunion_block_template_part_id'] = $template_part_id;
}
add_action( 'render_block_core_template_part_post', 'grunion_contact_form_set_block_template_part_id_global' );
add_action( 'render_block_core_template_part_file', 'grunion_contact_form_set_block_template_part_id_global' );
add_action( 'render_block_core_template_part_none', 'grunion_contact_form_set_block_template_part_id_global' );
add_action( 'gutenberg_render_block_core_template_part_post', 'grunion_contact_form_set_block_template_part_id_global' );
add_action( 'gutenberg_render_block_core_template_part_file', 'grunion_contact_form_set_block_template_part_id_global' );
add_action( 'gutenberg_render_block_core_template_part_none', 'grunion_contact_form_set_block_template_part_id_global' );

/**
* Unsets the global when block is done rendering.
*
* @param string $content Rendered block content.
* @param array $block The full block, including name and attributes.
* @return string
*/
function grunion_contact_form_unset_block_template_part_id_global( $content, $block ) {
if ( 'core/template-part' === $block['blockName']
&& isset( $GLOBALS['grunion_block_template_part_id'] ) ) {
unset( $GLOBALS['grunion_block_template_part_id'] );
}
return $content;
}
add_filter( 'render_block', 'grunion_contact_form_unset_block_template_part_id_global', 10, 2 );

/**
* Sets the 'widget' attribute on all instances of the contact form in the widget block.
*
* @param string $content Existing widget block content.
* @param array $instance Array of settings for the current widget.
* @param WP_Widget_Block $widget Current Block widget instance.
* @return string
*/
function grunion_contact_form_filter_widget_block_content( $content, $instance, $widget ) {
// Inject 'block_template' => <widget-id> into all instances of the contact form block.
return grunion_contact_form_apply_block_attribute(
$content,
array(
'widget' => $widget->id,
)
);
}
add_filter( 'widget_block_content', 'grunion_contact_form_filter_widget_block_content', 1, 3 );

/**
* Adds a given attribute to all instances of the Contact Form block.
*
* @param string $content Existing content to process.
* @param array $new_attr New attributes to add.
* @return string
*/
function grunion_contact_form_apply_block_attribute( $content, $new_attr ) {
if ( false === stripos( $content, 'wp:jetpack/contact-form' ) ) {
return $content;
}
return preg_replace_callback(
'/<!--\s+(?P<closer>\/)?wp:jetpack\/?contact-form\s+(?P<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?}\s+)?(?P<void>\/)?-->/s',
function ( $match ) use ( $new_attr ) {
// Ignore block closers.
if ( ! empty( $match['closer'] ) ) {
return $match[0];
}
// If block doesn't have attributes, add our own.
if ( empty( $match['attrs'] ) ) {
return str_replace(
'wp:jetpack/contact-form ',
'wp:jetpack/contact-form ' . wp_json_encode( $new_attr ) . ' ',
$match[0]
);
}
// $match['attrs'] includes trailing space: '{"customThankyou":"message"} '.
$attrs = json_decode( rtrim( $match['attrs'], ' ' ), true );
$attrs = array_merge( $attrs, $new_attr );
return str_replace(
$match['attrs'],
wp_json_encode( $attrs ) . ' ',
$match[0]
);
},
$content
);
}

/**
* Sets up various actions, filters, post types, post statuses, shortcodes.
*/
Expand Down Expand Up @@ -672,7 +789,9 @@ public function process_form_submission() {
check_admin_referer( "contact-form_{$id}" );
}

$is_widget = 0 === strpos( $id, 'widget-' );
$is_widget = 0 === strpos( $id, 'widget-' );
$is_block_template = 0 === strpos( $id, 'block-template-' );
$is_block_template_part = 0 === strpos( $id, 'block-template-part-' );

$form = false;

Expand Down Expand Up @@ -701,6 +820,70 @@ public function process_form_submission() {
call_user_func( $widget['callback'], $widget_args, $widget['params'][0] );
ob_end_clean();
}
} elseif ( $is_block_template ) {
/*
* Recreate the logic in wp-includes/template-loader.php
* that happens *after* 'template_redirect'.
*
* This logic populates the $_wp_current_template_content
* global, which we need in order to render the contact
* form for this block template.
*/
// start of copy-pasta from wp-includes/template-loader.php.
$tag_templates = array(
'is_embed' => 'get_embed_template',
'is_404' => 'get_404_template',
'is_search' => 'get_search_template',
'is_front_page' => 'get_front_page_template',
'is_home' => 'get_home_template',
'is_privacy_policy' => 'get_privacy_policy_template',
'is_post_type_archive' => 'get_post_type_archive_template',
'is_tax' => 'get_taxonomy_template',
'is_attachment' => 'get_attachment_template',
'is_single' => 'get_single_template',
'is_page' => 'get_page_template',
'is_singular' => 'get_singular_template',
'is_category' => 'get_category_template',
'is_tag' => 'get_tag_template',
'is_author' => 'get_author_template',
'is_date' => 'get_date_template',
'is_archive' => 'get_archive_template',
);
$template = false;
// Loop through each of the template conditionals, and find the appropriate template file.
// This is what calls locate_block_template() to hydrate $_wp_current_template_content.
foreach ( $tag_templates as $tag => $template_getter ) {
if ( call_user_func( $tag ) ) {
$template = call_user_func( $template_getter );
}
if ( $template ) {
if ( 'is_attachment' === $tag ) {
remove_filter( 'the_content', 'prepend_attachment' );
}
break;
}
}
if ( ! $template ) {
$template = get_index_template();
}
// end of copy-pasta from wp-includes/template-loader.php.

// Ensure 'block_template' attribute is added to any shortcodes in the template.
$template = grunion_contact_form_set_block_template_attribute( $template );

// Process the block template to populate Grunion_Contact_Form::$last
get_the_block_template_html();
} elseif ( $is_block_template_part ) {
$block_template_part_id = str_replace( 'block-template-part-', '', $id );
$bits = explode( '//', $block_template_part_id );
$block_template_part_slug = array_pop( $bits );
// Process the block part template to populate Grunion_Contact_Form::$last
$attributes = array(
'theme' => wp_get_theme()->get_stylesheet(),
'slug' => $block_template_part_slug,
'tagName' => 'div',
);
do_blocks( '<!-- wp:template-part ' . wp_json_encode( $attributes ) . ' /-->' );
} else {
// It's a form embedded in a post
$post = get_post( $id );
Expand Down Expand Up @@ -2234,17 +2417,30 @@ public function __construct( $attributes, $content = null ) {
$attributes = array();
}

if ( $post ) {
$default_subject = sprintf(
// translators: the blog name and post title.
_x( '%1$s %2$s', '%1$s = blog name, %2$s = post title', 'jetpack' ),
$default_subject,
Grunion_Contact_Form_Plugin::strip_tags( $post->post_title )
);
}

if ( ! empty( $attributes['widget'] ) && $attributes['widget'] ) {
$default_to .= get_option( 'admin_email' );
$attributes['id'] = 'widget-' . $attributes['widget'];
// translators: the blog name.
// translators: the blog name (and post name, if applicable).
$default_subject = sprintf( _x( '%1$s Sidebar', '%1$s = blog name', 'jetpack' ), $default_subject );
} elseif ( ! empty( $attributes['block_template'] ) && $attributes['block_template'] ) {
$default_to .= get_option( 'admin_email' );
$attributes['id'] = 'block-template-' . $attributes['block_template'];
} elseif ( ! empty( $attributes['block_template_part'] ) && $attributes['block_template_part'] ) {
$default_to .= get_option( 'admin_email' );
$attributes['id'] = 'block-template-part-' . $attributes['block_template_part'];
} elseif ( $post ) {
$attributes['id'] = $post->ID;
// translators: the blog name and post title.
$default_subject = sprintf( _x( '%1$s %2$s', '%1$s = blog name, %2$s = post title', 'jetpack' ), $default_subject, Grunion_Contact_Form_Plugin::strip_tags( $post->post_title ) );
$post_author = get_userdata( $post->post_author );
$default_to .= $post_author->user_email;
$post_author = get_userdata( $post->post_author );
$default_to .= $post_author->user_email;
}

// Keep reference to $this for parsing form fields.
Expand All @@ -2255,6 +2451,8 @@ public function __construct( $attributes, $content = null ) {
'subject' => $default_subject,
'show_subject' => 'no', // only used in back-compat mode
'widget' => 0, // Not exposed to the user. Works with Grunion_Contact_Form_Plugin::widget_atts()
'block_template' => null, // Not exposed to the user. Works with template_loader
'block_template_part' => null, // Not exposed to the user. Works with Grunion_Contact_Form::parse()
'id' => null, // Not exposed to the user. Set above.
'submit_button_text' => __( 'Submit', 'jetpack' ),
// These attributes come from the block editor, so use camel case instead of snake case.
Expand Down Expand Up @@ -2365,6 +2563,9 @@ public static function parse( $attributes, $content ) {
if ( Settings::is_syncing() ) {
return '';
}
if ( isset( $GLOBALS['grunion_block_template_part_id'] ) ) {
$attributes['block_template_part'] = $GLOBALS['grunion_block_template_part_id'];
}
// Create a new Grunion_Contact_Form object (this class)
$form = new Grunion_Contact_Form( $attributes, $content );

Expand Down Expand Up @@ -2432,7 +2633,9 @@ public static function parse( $attributes, $content ) {
$r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message );
} else {
// Nothing special - show the normal contact form
if ( $form->get_attribute( 'widget' ) ) {
if ( $form->get_attribute( 'widget' )
|| $form->get_attribute( 'block_template' )
|| $form->get_attribute( 'block_template_part' ) ) {
// Submit form to the current URL
$url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', 'action', '_wpnonce' ) );
} else {
Expand Down Expand Up @@ -2957,9 +3160,11 @@ public function process_submission() {

$plugin = Grunion_Contact_Form_Plugin::init();

$id = $this->get_attribute( 'id' );
$to = $this->get_attribute( 'to' );
$widget = $this->get_attribute( 'widget' );
$id = $this->get_attribute( 'id' );
$to = $this->get_attribute( 'to' );
$widget = $this->get_attribute( 'widget' );
$block_template = $this->get_attribute( 'block_template' );
$block_template_part = $this->get_attribute( 'block_template_part' );

$contact_form_subject = $this->get_attribute( 'subject' );
$email_marketing_consent = false;
Expand Down Expand Up @@ -2999,6 +3204,14 @@ public function process_submission() {
if ( isset( $_POST['contact-form-id'] ) && 'widget-' . $widget !== $_POST['contact-form-id'] ) { // phpcs:Ignore WordPress.Security.NonceVerification.Missing -- check done by caller process_form_submission()
return false;
}
} elseif ( $block_template ) {
if ( isset( $_POST['contact-form-id'] ) && 'block-template-' . $block_template !== $_POST['contact-form-id'] ) { // phpcs:Ignore WordPress.Security.NonceVerification.Missing -- check done by caller process_form_submission()
return false;
}
} elseif ( $block_template_part ) {
if ( isset( $_POST['contact-form-id'] ) && 'block-template-part-' . $block_template_part !== $_POST['contact-form-id'] ) { // phpcs:Ignore WordPress.Security.NonceVerification.Missing -- check done by caller process_form_submission()
return false;
}
} else {
if ( isset( $_POST['contact-form-id'] ) && $post->ID !== (int) $_POST['contact-form-id'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- check done by caller process_form_submission()
return false;
Expand Down Expand Up @@ -3246,7 +3459,7 @@ public function process_submission() {

/** This filter is already documented in modules/contact-form/admin.php */
$subject = apply_filters( 'contact_form_subject', $contact_form_subject, $all_values );
$url = $widget ? home_url( '/' ) : get_permalink( $post->ID );
$url = $block_template || $block_template_part || $widget ? home_url( '/' ) : get_permalink( $post->ID );

// translators: the time of the form submission.
$date_time_format = _x( '%1$s \a\t %2$s', '{$date_format} \a\t {$time_format}', 'jetpack' );
Expand Down
Loading