Skip to content

Commit

Permalink
Contact Form Block: Displays expected success message when used in FS…
Browse files Browse the repository at this point in the history
…E template, FSE template part, or widget block (#24727)

* Contact Form Block: Displays expected success message when used in FSE templates
  • Loading branch information
danielbachhuber authored Jun 20, 2022
1 parent a2303d7 commit 421a874
Show file tree
Hide file tree
Showing 3 changed files with 407 additions and 11 deletions.
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
* 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

0 comments on commit 421a874

Please sign in to comment.