summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/modules/contact-form/grunion-contact-form.php')
-rw-r--r--plugins/jetpack/modules/contact-form/grunion-contact-form.php790
1 files changed, 587 insertions, 203 deletions
diff --git a/plugins/jetpack/modules/contact-form/grunion-contact-form.php b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
index fb893222..3eaa34c3 100644
--- a/plugins/jetpack/modules/contact-form/grunion-contact-form.php
+++ b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
@@ -4,10 +4,11 @@
* Add a contact form to any post, page or text widget.
* Emails will be sent to the post's author by default, or any email address you choose.
*
- * @package Jetpack
+ * @package automattic/jetpack
*/
use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Blocks;
use Automattic\Jetpack\Sync\Settings;
define( 'GRUNION_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
@@ -106,14 +107,14 @@ class Grunion_Contact_Form_Plugin {
public static function strip_tags( $data_with_tags ) {
if ( is_array( $data_with_tags ) ) {
foreach ( $data_with_tags as $index => $value ) {
- $index = sanitize_text_field( strval( $index ) );
- $value = wp_kses( strval( $value ), array() );
+ $index = sanitize_text_field( (string) $index );
+ $value = wp_kses( (string) $value, array() );
$value = str_replace( '&', '&', $value ); // undo damage done by wp_kses_normalize_entities()
$data_without_tags[ $index ] = $value;
}
} else {
- $data_without_tags = wp_kses( $data_with_tags, array() );
+ $data_without_tags = wp_kses( (string) $data_with_tags, array() );
$data_without_tags = str_replace( '&', '&', $data_without_tags ); // undo damage done by wp_kses_normalize_entities()
}
@@ -140,8 +141,8 @@ class Grunion_Contact_Form_Plugin {
add_filter( 'widget_text', array( $this, 'widget_shortcode_hack' ), 5 );
}
- add_filter( 'jetpack_contact_form_is_spam', array( $this, 'is_spam_blacklist' ), 10, 2 );
-
+ add_filter( 'jetpack_contact_form_is_spam', array( $this, 'is_spam_blocklist' ), 10, 2 );
+ add_filter( 'jetpack_contact_form_in_comment_disallowed_list', array( $this, 'is_in_disallowed_list' ), 10, 2 );
// Akismet to the rescue
if ( defined( 'AKISMET_VERSION' ) || function_exists( 'akismet_http_post' ) ) {
add_filter( 'jetpack_contact_form_is_spam', array( $this, 'is_spam_akismet' ), 10, 2 );
@@ -149,6 +150,7 @@ class Grunion_Contact_Form_Plugin {
}
add_action( 'loop_start', array( 'Grunion_Contact_Form', '_style_on' ) );
+ add_action( 'pre_amp_render_post', array( 'Grunion_Contact_Form', '_style_on' ) );
add_action( 'wp_ajax_grunion-contact-form', array( $this, 'ajax_request' ) );
add_action( 'wp_ajax_nopriv_grunion-contact-form', array( $this, 'ajax_request' ) );
@@ -161,23 +163,25 @@ class Grunion_Contact_Form_Plugin {
if ( is_admin() ) {
add_action( 'admin_init', array( $this, 'download_feedback_as_csv' ) );
add_action( 'admin_footer-edit.php', array( $this, 'export_form' ) );
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
- add_action( 'current_screen', array( $this, 'unread_count' ) );
}
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
+ add_action( 'current_screen', array( $this, 'unread_count' ) );
+
+ add_filter( 'use_block_editor_for_post_type', array( $this, 'use_block_editor_for_post_type' ), 10, 2 );
// custom post type we'll use to keep copies of the feedback items
register_post_type(
'feedback', array(
'labels' => array(
- 'name' => __( 'Feedback', 'jetpack' ),
- 'singular_name' => __( 'Feedback', 'jetpack' ),
- 'search_items' => __( 'Search Feedback', 'jetpack' ),
- 'not_found' => __( 'No feedback found', 'jetpack' ),
- 'not_found_in_trash' => __( 'No feedback found', 'jetpack' ),
+ 'name' => __( 'Form Responses', 'jetpack' ),
+ 'singular_name' => __( 'Form Responses', 'jetpack' ),
+ 'search_items' => __( 'Search Responses', 'jetpack' ),
+ 'not_found' => __( 'No responses found', 'jetpack' ),
+ 'not_found_in_trash' => __( 'No responses found', 'jetpack' ),
),
- // Matrial Ballot icon
- 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M13 7.5h5v2h-5zm0 7h5v2h-5zM19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM11 6H6v5h5V6zm-1 4H7V7h3v3zm1 3H6v5h5v-5zm-1 4H7v-3h3v3z"/></svg>'),
+ 'menu_icon' => 'dashicons-feedback',
'show_ui' => true,
+ 'show_in_menu' => false,
'show_in_admin_bar' => false,
'public' => false,
'rewrite' => false,
@@ -201,7 +205,7 @@ class Grunion_Contact_Form_Plugin {
)
);
- // Add to REST API post type whitelist
+ // Add to REST API post type allowed list.
add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_feedback_rest_api_type' ) );
// Add "spam" as a post status
@@ -243,58 +247,112 @@ class Grunion_Contact_Form_Plugin {
}
private static function register_contact_form_blocks() {
- jetpack_register_block( 'jetpack/contact-form', array(
- 'render_callback' => array( __CLASS__, 'gutenblock_render_form' ),
- ) );
+ Blocks::jetpack_register_block(
+ 'jetpack/contact-form',
+ array(
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_form' ),
+ )
+ );
// Field render methods.
- jetpack_register_block( 'jetpack/field-text', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_text' ),
- ) );
- jetpack_register_block( 'jetpack/field-name', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_name' ),
- ) );
- jetpack_register_block( 'jetpack/field-email', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_email' ),
- ) );
- jetpack_register_block( 'jetpack/field-url', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_url' ),
- ) );
- jetpack_register_block( 'jetpack/field-date', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_date' ),
- ) );
- jetpack_register_block( 'jetpack/field-telephone', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_telephone' ),
- ) );
- jetpack_register_block( 'jetpack/field-textarea', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_textarea' ),
- ) );
- jetpack_register_block( 'jetpack/field-checkbox', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_checkbox' ),
- ) );
- jetpack_register_block( 'jetpack/field-checkbox-multiple', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_checkbox_multiple' ),
- ) );
- jetpack_register_block( 'jetpack/field-radio', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_radio' ),
- ) );
- jetpack_register_block( 'jetpack/field-select', array(
- 'parent' => array( 'jetpack/contact-form' ),
- 'render_callback' => array( __CLASS__, 'gutenblock_render_field_select' ),
- ) );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-text',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_text' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-name',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_name' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-email',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_email' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-url',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_url' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-date',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_date' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-telephone',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_telephone' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-textarea',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_textarea' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-checkbox',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_checkbox' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-checkbox-multiple',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_checkbox_multiple' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-radio',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_radio' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-select',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_select' ),
+ )
+ );
+ Blocks::jetpack_register_block(
+ 'jetpack/field-consent',
+ array(
+ 'parent' => array( 'jetpack/contact-form' ),
+ 'render_callback' => array( __CLASS__, 'gutenblock_render_field_consent' ),
+ )
+ );
}
public static function gutenblock_render_form( $atts, $content ) {
+
+ // Render fallback in other contexts than frontend (i.e. feed, emails, API, etc.), unless the form is being submitted.
+ if ( ! jetpack_is_frontend() && ! isset( $_POST['contact-form-id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ return sprintf(
+ '<div class="%1$s"><a href="%2$s" target="_blank" rel="noopener noreferrer">%3$s</a></div>',
+ esc_attr( Blocks::classes( 'contact-form', $atts ) ),
+ esc_url( get_the_permalink() ),
+ esc_html__( 'Submit a form.', 'jetpack' )
+ );
+ }
+
return Grunion_Contact_Form::parse( $atts, do_blocks( $content ) );
}
@@ -359,21 +417,59 @@ class Grunion_Contact_Form_Plugin {
}
/**
- * Add the 'Export' menu item as a submenu of Feedback.
+ * Render the consent field.
+ *
+ * @param string $atts consent attributes.
+ * @param string $content html content.
+ */
+ public static function gutenblock_render_field_consent( $atts, $content ) {
+ $atts = self::block_attributes_to_shortcode_attributes( $atts, 'consent' );
+
+ if ( ! isset( $atts['implicitConsentMessage'] ) ) {
+ $atts['implicitConsentMessage'] = __( "By submitting your information, you're giving us permission to email you. You may unsubscribe at any time.", 'jetpack' );
+ }
+
+ if ( ! isset( $atts['explicitConsentMessage'] ) ) {
+ $atts['explicitConsentMessage'] = __( 'Can we send you an email from time to time?', 'jetpack' );
+ }
+
+ return Grunion_Contact_Form::parse_contact_field( $atts, $content );
+ }
+
+ /**
+ * Add the 'Form Responses' menu item as a submenu of Feedback.
*/
public function admin_menu() {
+ $slug = 'feedback';
+
+ add_menu_page(
+ __( 'Feedback', 'jetpack' ),
+ __( 'Feedback', 'jetpack' ),
+ 'edit_pages',
+ $slug,
+ null,
+ 'dashicons-feedback',
+ 45
+ );
+
add_submenu_page(
+ $slug,
+ __( 'Form Responses', 'jetpack' ),
+ __( 'Form Responses', 'jetpack' ),
+ 'edit_pages',
'edit.php?post_type=feedback',
- __( 'Export feedback as CSV', 'jetpack' ),
- __( 'Export CSV', 'jetpack' ),
- 'export',
- 'feedback-export',
- array( $this, 'export_form' )
+ null,
+ 0
+ );
+
+ remove_submenu_page(
+ $slug,
+ $slug
);
}
/**
- * Add to REST API post type whitelist
+ * Add to REST API post type allowed list.
*/
function allow_feedback_rest_api_type( $post_types ) {
$post_types[] = 'feedback';
@@ -391,14 +487,16 @@ class Grunion_Contact_Form_Plugin {
if ( isset( $screen->post_type ) && 'feedback' == $screen->post_type ) {
update_option( 'feedback_unread_count', 0 );
} else {
- global $menu;
- if ( isset( $menu ) && is_array( $menu ) && ! empty( $menu ) ) {
- foreach ( $menu as $index => $menu_item ) {
+ global $submenu;
+ if ( isset( $submenu['feedback'] ) && is_array( $submenu['feedback'] ) && ! empty( $submenu['feedback'] ) ) {
+ foreach ( $submenu['feedback'] as $index => $menu_item ) {
if ( 'edit.php?post_type=feedback' == $menu_item[2] ) {
$unread = get_option( 'feedback_unread_count', 0 );
if ( $unread > 0 ) {
- $unread_count = current_user_can( 'publish_pages' ) ? " <span class='feedback-unread count-{$unread} awaiting-mod'><span class='feedback-unread-count'>" . number_format_i18n( $unread ) . '</span></span>' : '';
- $menu[ $index ][0] .= $unread_count;
+ $unread_count = current_user_can( 'publish_pages' ) ? " <span class='feedback-unread count-{$unread} awaiting-mod'><span class='feedback-unread-count'>" . number_format_i18n( $unread ) . '</span></span>' : '';
+
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $submenu['feedback'][ $index ][0] .= $unread_count;
}
break;
}
@@ -417,9 +515,13 @@ class Grunion_Contact_Form_Plugin {
add_filter( 'contact_form_subject', array( $this, 'replace_tokens_with_input' ), 10, 2 );
$id = stripslashes( $_POST['contact-form-id'] );
- $hash = isset( $_POST['contact-form-hash'] ) ? $_POST['contact-form-hash'] : null;
+ $hash = isset( $_POST['contact-form-hash'] ) ? $_POST['contact-form-hash'] : '';
$hash = preg_replace( '/[^\da-f]/i', '', $hash );
+ if ( ! is_string( $id ) || ! is_string( $hash ) ) {
+ return false;
+ }
+
if ( is_user_logged_in() ) {
check_admin_referer( "contact-form_{$id}" );
}
@@ -458,24 +560,26 @@ class Grunion_Contact_Form_Plugin {
$post = get_post( $id );
// Process the content to populate Grunion_Contact_Form::$last
- /** This filter is already documented in core. wp-includes/post-template.php */
- apply_filters( 'the_content', $post->post_content );
+ if ( $post ) {
+ /** This filter is already documented in core. wp-includes/post-template.php */
+ apply_filters( 'the_content', $post->post_content );
+ }
}
$form = isset( Grunion_Contact_Form::$forms[ $hash ] ) ? Grunion_Contact_Form::$forms[ $hash ] : null;
// No form may mean user is using do_shortcode, grab the form using the stored post meta
- if ( ! $form ) {
+ if ( ! $form && is_numeric( $id ) && $hash ) {
// Get shortcode from post meta
- $shortcode = get_post_meta( $_POST['contact-form-id'], "_g_feedback_shortcode_{$hash}", true );
+ $shortcode = get_post_meta( $id, "_g_feedback_shortcode_{$hash}", true );
// Format it
if ( $shortcode != '' ) {
// Get attributes from post meta.
$parameters = '';
- $attributes = get_post_meta( $_POST['contact-form-id'], "_g_feedback_shortcode_atts_{$hash}", true );
+ $attributes = get_post_meta( $id, "_g_feedback_shortcode_atts_{$hash}", true );
if ( ! empty( $attributes ) && is_array( $attributes ) ) {
foreach ( array_filter( $attributes ) as $param => $value ) {
$parameters .= " $param=\"$value\"";
@@ -488,10 +592,10 @@ class Grunion_Contact_Form_Plugin {
// Recreate form
$form = Grunion_Contact_Form::$last;
}
+ }
- if ( ! $form ) {
- return false;
- }
+ if ( ! $form ) {
+ return false;
}
if ( is_wp_error( $form->errors ) && $form->errors->get_error_codes() ) {
@@ -553,7 +657,10 @@ class Grunion_Contact_Form_Plugin {
}
static function sanitize_value( $value ) {
- return preg_replace( '=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', null, $value );
+ if ( null === $value ) {
+ return '';
+ }
+ return preg_replace( '=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', '', $value );
}
/**
@@ -634,8 +741,8 @@ class Grunion_Contact_Form_Plugin {
}
/**
- * Check if a submission matches the Comment Blacklist.
- * The Comment Blacklist is a means to moderate discussion, and contact
+ * Check if a submission matches the Comment Blocklist.
+ * The Comment Blocklist is a means to moderate discussion, and contact
* forms are 1:1 discussion forums, ripe for abuse by users who are being
* removed from the public discussion.
* Attached to `jetpack_contact_form_is_spam`
@@ -644,12 +751,37 @@ class Grunion_Contact_Form_Plugin {
* @param array $form
* @return bool TRUE => spam, FALSE => not spam
*/
- function is_spam_blacklist( $is_spam, $form = array() ) {
+ public function is_spam_blocklist( $is_spam, $form = array() ) {
if ( $is_spam ) {
return $is_spam;
}
- if ( wp_blacklist_check( $form['comment_author'], $form['comment_author_email'], $form['comment_author_url'], $form['comment_content'], $form['user_ip'], $form['user_agent'] ) ) {
+ return $this->is_in_disallowed_list( false, $form );
+ }
+
+ /**
+ * Check if a submission matches the comment disallowed list.
+ * Attached to `jetpack_contact_form_in_comment_disallowed_list`.
+ *
+ * @param boolean $in_disallowed_list Whether the feedback is in the disallowed list.
+ * @param array $form The form array.
+ * @return bool Returns true if the form submission matches the disallowed list and false if it doesn't.
+ */
+ public function is_in_disallowed_list( $in_disallowed_list, $form = array() ) {
+ if ( $in_disallowed_list ) {
+ return $in_disallowed_list;
+ }
+
+ if (
+ wp_check_comment_disallowed_list(
+ $form['comment_author'],
+ $form['comment_author_email'],
+ $form['comment_author_url'],
+ $form['comment_content'],
+ $form['user_ip'],
+ $form['user_agent']
+ )
+ ) {
return true;
}
@@ -666,8 +798,8 @@ class Grunion_Contact_Form_Plugin {
function prepare_for_akismet( $form ) {
$form['comment_type'] = 'contact_form';
$form['user_ip'] = $_SERVER['REMOTE_ADDR'];
- $form['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
- $form['referrer'] = $_SERVER['HTTP_REFERER'];
+ $form['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ $form['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
$form['blog'] = get_option( 'home' );
foreach ( $_SERVER as $key => $value ) {
@@ -687,7 +819,16 @@ class Grunion_Contact_Form_Plugin {
}
}
- return $form;
+ /**
+ * Filter the values that are sent to Akismet for the spam check.
+ *
+ * @module contact-form
+ *
+ * @since 10.2.0
+ *
+ * @param array $form The form values being sent to Akismet.
+ */
+ return apply_filters( 'jetpack_contact_form_akismet_values', $form );
}
/**
@@ -794,13 +935,13 @@ class Grunion_Contact_Form_Plugin {
?>
<div id="feedback-export" style="display:none">
- <h2><?php _e( 'Export feedback as CSV', 'jetpack' ); ?></h2>
+ <h2><?php esc_html_e( 'Export responses as CSV', 'jetpack' ); ?></h2>
<div class="clear"></div>
<form action="<?php echo admin_url( 'admin-post.php' ); ?>" method="post" class="form">
<?php wp_nonce_field( 'feedback_export', 'feedback_export_nonce' ); ?>
<input name="action" value="feedback_export" type="hidden">
- <label for="post"><?php _e( 'Select feedback to download', 'jetpack' ); ?></label>
+ <label for="post"><?php esc_html_e( 'Select responses to download', 'jetpack' ); ?></label>
<select name="post">
<option value="all"><?php esc_html_e( 'All posts', 'jetpack' ); ?></option>
<?php echo $this->get_feedbacks_as_options(); ?>
@@ -854,6 +995,17 @@ class Grunion_Contact_Form_Plugin {
$md['feedback_date'] = get_the_date( DATE_RFC3339, $post_id );
$content_fields = self::parse_fields_from_content( $post_id );
$md['feedback_ip'] = ( isset( $content_fields['_feedback_ip'] ) ) ? $content_fields['_feedback_ip'] : 0;
+
+ // add the email_marketing_consent to the post meta.
+ $md['email_marketing_consent'] = 0;
+ if ( isset( $content_fields['_feedback_all_fields'] ) ) {
+ $all_fields = $content_fields['_feedback_all_fields'];
+ // check if the email_marketing_consent field exists.
+ if ( isset( $all_fields['email_marketing_consent'] ) ) {
+ $md['email_marketing_consent'] = $all_fields['email_marketing_consent'];
+ }
+ }
+
return $md;
}
@@ -1584,6 +1736,17 @@ class Grunion_Contact_Form_Plugin {
public static function get_ip_address() {
return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null;
}
+
+ /**
+ * Disable Block Editor for feedbacks.
+ *
+ * @param bool $can_edit Whether the post type can be edited or not.
+ * @param string $post_type The post type being checked.
+ * @return bool
+ */
+ public function use_block_editor_for_post_type( $can_edit, $post_type ) {
+ return 'feedback' === $post_type ? false : $can_edit;
+ }
}
/**
@@ -1651,9 +1814,9 @@ class Crunion_Contact_Form_Shortcode {
function parse_content( $content ) {
if ( is_null( $content ) ) {
$this->body = null;
+ } else {
+ $this->body = do_shortcode( $content );
}
-
- $this->body = do_shortcode( $content );
}
/**
@@ -1697,7 +1860,8 @@ class Crunion_Contact_Form_Shortcode {
// For back-compat with old Grunion encoding
// Also, unencode commas
$value = strtr(
- $value, array(
+ (string) $value,
+ array(
'%26' => '&',
'%25' => '%',
)
@@ -1814,7 +1978,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$this->hash = sha1( json_encode( $attributes ) . $content );
self::$forms[ $this->hash ] = $this;
- // Set up the default subject and recipient for this form
+ // Set up the default subject and recipient for this form.
$default_to = '';
$default_subject = '[' . get_option( 'blogname' ) . ']';
@@ -1833,7 +1997,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$default_to .= $post_author->user_email;
}
- // Keep reference to $this for parsing form fields
+ // Keep reference to $this for parsing form fields.
self::$current_form = $this;
$this->defaults = array(
@@ -1845,20 +2009,22 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
'submit_button_text' => __( 'Submit', 'jetpack' ),
// These attributes come from the block editor, so use camel case instead of snake case.
'customThankyou' => '', // Whether to show a custom thankyou response after submitting a form. '' for no, 'message' for a custom message, 'redirect' to redirect to a new URL.
+ 'customThankyouHeading' => __( 'Message Sent', 'jetpack' ), // The text to show above customThankyouMessage.
'customThankyouMessage' => __( 'Thank you for your submission!', 'jetpack' ), // The message to show when customThankyou is set to 'message'.
'customThankyouRedirect' => '', // The URL to redirect to when customThankyou is set to 'redirect'.
+ 'jetpackCRM' => true, // Whether Jetpack CRM should store the form submission.
);
$attributes = shortcode_atts( $this->defaults, $attributes, 'contact-form' );
- // We only enable the contact-field shortcode temporarily while processing the contact-form shortcode
+ // We only enable the contact-field shortcode temporarily while processing the contact-form shortcode.
Grunion_Contact_Form_Plugin::$using_contact_form_field = true;
parent::__construct( $attributes, $content );
// There were no fields in the contact form. The form was probably just [contact-form /]. Build a default form.
if ( empty( $this->fields ) ) {
- // same as the original Grunion v1 form
+ // same as the original Grunion v1 form.
$default_form = '
[contact-field label="' . __( 'Name', 'jetpack' ) . '" type="name" required="true" /]
[contact-field label="' . __( 'Email', 'jetpack' ) . '" type="email" required="true" /]
@@ -1874,10 +2040,10 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$this->parse_content( $default_form );
- // Store the shortcode
+ // Store the shortcode.
$this->store_shortcode( $default_form, $attributes, $this->hash );
} else {
- // Store the shortcode
+ // Store the shortcode.
$this->store_shortcode( $content, $attributes, $this->hash );
}
@@ -1981,16 +2147,17 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
if ( isset( $_GET['contact-form-id'] )
- && $_GET['contact-form-id'] == self::$last->get_attribute( 'id' )
- && isset( $_GET['contact-form-sent'], $_GET['contact-form-hash'] )
- && hash_equals( $form->hash, $_GET['contact-form-hash'] ) ) {
- // The contact form was submitted. Show the success message/results
+ && (int) $_GET['contact-form-id'] === (int) self::$last->get_attribute( 'id' )
+ && isset( $_GET['contact-form-sent'], $_GET['contact-form-hash'] )
+ && is_string( $_GET['contact-form-hash'] )
+ && hash_equals( $form->hash, $_GET['contact-form-hash'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ // The contact form was submitted. Show the success message/results.
$feedback_id = (int) $_GET['contact-form-sent'];
$back_url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', '_wpnonce' ) );
$r_success_message =
- '<h3>' . __( 'Message Sent', 'jetpack' ) .
+ '<h3>' . esc_html( $form->get_attribute( 'customThankyouHeading' ) ) .
' (<a href="' . esc_url( $back_url ) . '">' . esc_html__( 'go back', 'jetpack' ) . '</a>)' .
"</h3>\n\n";
@@ -2034,50 +2201,63 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
* @param $post $GLOBALS['post'] Post global variable.
* @param int $id Contact Form ID.
*/
- $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id );
+ $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id );
+ $has_submit_button_block = ! ( false === strpos( $content, 'wp-block-jetpack-button' ) );
+ $form_classes = 'contact-form commentsblock';
+
+ if ( $has_submit_button_block ) {
+ $form_classes .= ' wp-block-jetpack-contact-form';
+ }
+
+ $r .= "<form action='" . esc_url( $url ) . "' method='post' class='" . esc_attr( $form_classes ) . "'>\n";
+ $r .= self::get_script_for_form();
- $r .= "<form action='" . esc_url( $url ) . "' method='post' class='contact-form commentsblock'>\n";
$r .= $form->body;
- $r .= "\t<p class='contact-submit'>\n";
- $gutenberg_submit_button_classes = '';
- if ( ! empty( $attributes['submitButtonClasses'] ) ) {
- $gutenberg_submit_button_classes = ' ' . $attributes['submitButtonClasses'];
- }
+ // In new versions of the contact form block the button is an inner block
+ // so the button does not need to be constructed server-side.
+ if ( ! $has_submit_button_block ) {
+ $r .= "\t<p class='contact-submit'>\n";
- /**
- * Filter the contact form submit button class attribute.
- *
- * @module contact-form
- *
- * @since 6.6.0
- *
- * @param string $class Additional CSS classes for button attribute.
- */
- $submit_button_class = apply_filters( 'jetpack_contact_form_submit_button_class', 'pushbutton-wide' . $gutenberg_submit_button_classes );
+ $gutenberg_submit_button_classes = '';
+ if ( ! empty( $attributes['submitButtonClasses'] ) ) {
+ $gutenberg_submit_button_classes = ' ' . $attributes['submitButtonClasses'];
+ }
- $submit_button_styles = '';
- if ( ! empty( $attributes['customBackgroundButtonColor'] ) ) {
- $submit_button_styles .= 'background-color: ' . $attributes['customBackgroundButtonColor'] . '; ';
- }
- if ( ! empty( $attributes['customTextButtonColor'] ) ) {
- $submit_button_styles .= 'color: ' . $attributes['customTextButtonColor'] . ';';
- }
- if ( ! empty( $attributes['submitButtonText'] ) ) {
- $submit_button_text = $attributes['submitButtonText'];
- } else {
- $submit_button_text = $form->get_attribute( 'submit_button_text' );
- }
+ /**
+ * Filter the contact form submit button class attribute.
+ *
+ * @module contact-form
+ *
+ * @since 6.6.0
+ *
+ * @param string $class Additional CSS classes for button attribute.
+ */
+ $submit_button_class = apply_filters( 'jetpack_contact_form_submit_button_class', 'pushbutton-wide' . $gutenberg_submit_button_classes );
+
+ $submit_button_styles = '';
+ if ( ! empty( $attributes['customBackgroundButtonColor'] ) ) {
+ $submit_button_styles .= 'background-color: ' . $attributes['customBackgroundButtonColor'] . '; ';
+ }
+ if ( ! empty( $attributes['customTextButtonColor'] ) ) {
+ $submit_button_styles .= 'color: ' . $attributes['customTextButtonColor'] . ';';
+ }
+ if ( ! empty( $attributes['submitButtonText'] ) ) {
+ $submit_button_text = $attributes['submitButtonText'];
+ } else {
+ $submit_button_text = $form->get_attribute( 'submit_button_text' );
+ }
- $r .= "\t\t<button type='submit' class='" . esc_attr( $submit_button_class ) . "'";
- if ( ! empty( $submit_button_styles ) ) {
- $r .= " style='" . esc_attr( $submit_button_styles ) . "'";
+ $r .= "\t\t<button type='submit' class='" . esc_attr( $submit_button_class ) . "'";
+ if ( ! empty( $submit_button_styles ) ) {
+ $r .= " style='" . esc_attr( $submit_button_styles ) . "'";
+ }
+ $r .= ">";
+ $r .= wp_kses(
+ $submit_button_text,
+ self::$allowed_html_tags_for_submit_button
+ ) . "</button>";
}
- $r .= ">";
- $r .= wp_kses(
- $submit_button_text,
- self::$allowed_html_tags_for_submit_button
- ) . "</button>";
if ( is_user_logged_in() ) {
$r .= "\t\t" . wp_nonce_field( 'contact-form_' . $id, '_wpnonce', true, false ) . "\n"; // nonce and referer
@@ -2089,13 +2269,26 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n";
$r .= "\t\t<input type='hidden' name='action' value='grunion-contact-form' />\n";
$r .= "\t\t<input type='hidden' name='contact-form-hash' value='" . esc_attr( $form->hash ) . "' />\n";
- $r .= "\t</p>\n";
+
+ if ( ! $has_submit_button_block ) {
+ $r .= "\t</p>\n";
+ }
+
$r .= "</form>\n";
}
$r .= '</div>';
- return $r;
+ /**
+ * Filter the contact form, allowing plugins to modify the HTML.
+ *
+ * @module contact-form
+ *
+ * @since 10.2.0
+ *
+ * @param string $r The contact form HTML.
+ */
+ return apply_filters( 'jetpack_contact_form_html', $r );
}
/**
@@ -2126,6 +2319,29 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
/**
+ * Returns a script that disables the contact form button after a form submission.
+ *
+ * @return string The script.
+ */
+ private static function get_script_for_form() {
+ return "<script>
+ ( function () {
+ const contact_forms = document.getElementsByClassName('contact-form');
+
+ for ( const form of contact_forms ) {
+ form.onsubmit = function() {
+ const buttons = form.getElementsByTagName('button');
+
+ for( const button of buttons ) {
+ button.setAttribute('disabled', true);
+ }
+ }
+ }
+ } )();
+ </script>";
+ }
+
+ /**
* Returns a compiled form with labels and values in a form of an array
* of lines.
*
@@ -2150,7 +2366,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$compiled_form = array();
- // "Standard" field whitelist
+ // "Standard" field allowed list.
foreach ( $field_value_map as $type => $meta_key ) {
if ( isset( $field_ids[ $type ] ) ) {
$field = $form->fields[ $field_ids[ $type ] ];
@@ -2166,6 +2382,11 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$value = trim( $value );
}
+ // If we still do not have any value, bail.
+ if ( empty( $value ) ) {
+ continue;
+ }
+
$field_index = array_search( $field_ids[ $type ], $field_ids['all'] );
$compiled_form[ $field_index ] = sprintf(
'<b>%1$s:</b> %2$s<br /><br />',
@@ -2228,6 +2449,34 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
/**
+ * Escape a shortcode value.
+ *
+ * Shortcode attribute values have a number of unfortunate restrictions, which fortunately we
+ * can get around by adding some extra HTML encoding.
+ *
+ * The output HTML will have a few extra escapes, but that makes no functional difference.
+ *
+ * @since 9.1.0
+ * @param string $val Value to escape.
+ * @return string
+ */
+ private static function esc_shortcode_val( $val ) {
+ return strtr(
+ esc_html( $val ),
+ array(
+ // Brackets in attribute values break the shortcode parser.
+ '[' => '&#091;',
+ ']' => '&#093;',
+ // Shortcode parser screws up backslashes too, thanks to calls to `stripcslashes`.
+ '\\' => '&#092;',
+ // The existing code here represents arrays as comma-separated strings.
+ // Rather than trying to change representations now, just escape the commas in values.
+ ',' => '&#044;',
+ )
+ );
+ }
+
+ /**
* The contact-field shortcode processor
* We use an object method here instead of a static Grunion_Contact_Form_Field class method to parse contact-field shortcodes so that we can tie them to the contact-form object.
*
@@ -2245,18 +2494,18 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
foreach ( $attributes as $att => $val ) {
if ( is_numeric( $att ) ) { // Is a valueless attribute
- $att_strs[] = esc_html( $val );
+ $att_strs[] = self::esc_shortcode_val( $val );
} elseif ( isset( $val ) ) { // A regular attr - value pair
if ( ( $att === 'options' || $att === 'values' ) && is_string( $val ) ) { // remove any empty strings
$val = explode( ',', $val );
}
- if ( is_array( $val ) ) {
+ if ( is_array( $val ) ) {
$val = array_filter( $val, array( __CLASS__, 'remove_empty' ) ); // removes any empty strings
- $att_strs[] = esc_html( $att ) . '="' . implode( ',', array_map( 'esc_html', $val ) ) . '"';
+ $att_strs[] = esc_html( $att ) . '="' . implode( ',', array_map( array( __CLASS__, 'esc_shortcode_val' ), $val ) ) . '"';
} elseif ( is_bool( $val ) ) {
- $att_strs[] = esc_html( $att ) . '="' . esc_html( $val ? '1' : '' ) . '"';
+ $att_strs[] = esc_html( $att ) . '="' . ( $val ? '1' : '' ) . '"';
} else {
- $att_strs[] = esc_html( $att ) . '="' . esc_html( $val ) . '"';
+ $att_strs[] = esc_html( $att ) . '="' . self::esc_shortcode_val( $val ) . '"';
}
}
}
@@ -2299,59 +2548,75 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
static function get_default_label_from_type( $type ) {
+ $str = null;
switch ( $type ) {
case 'text':
- return __( 'Text', 'jetpack' );
+ $str = __( 'Text', 'jetpack' );
+ break;
case 'name':
- return __( 'Name', 'jetpack' );
+ $str = __( 'Name', 'jetpack' );
+ break;
case 'email':
- return __( 'Email', 'jetpack' );
+ $str = __( 'Email', 'jetpack' );
+ break;
case 'url':
- return __( 'Website', 'jetpack' );
+ $str = __( 'Website', 'jetpack' );
+ break;
case 'date':
- return __( 'Date', 'jetpack' );
+ $str = __( 'Date', 'jetpack' );
+ break;
case 'telephone':
- return __( 'Phone', 'jetpack' );
+ $str = __( 'Phone', 'jetpack' );
+ break;
case 'textarea':
- return __( 'Message', 'jetpack' );
+ $str = __( 'Message', 'jetpack' );
+ break;
case 'checkbox':
- return __( 'Checkbox', 'jetpack' );
+ $str = __( 'Checkbox', 'jetpack' );
+ break;
case 'checkbox-multiple':
- return __( 'Choose several', 'jetpack' );
+ $str = __( 'Choose several', 'jetpack' );
+ break;
case 'radio':
- return __( 'Choose one', 'jetpack' );
+ $str = __( 'Choose one', 'jetpack' );
+ break;
case 'select':
- return __( 'Select one', 'jetpack' );
+ $str = __( 'Select one', 'jetpack' );
+ break;
+ case 'consent':
+ $str = __( 'Consent', 'jetpack' );
+ break;
default:
- return null;
+ $str = null;
}
+ return $str;
}
/**
* Loops through $this->fields to generate a (structured) list of field IDs.
*
- * Important: Currently the whitelisted fields are defined as follows:
+ * Important: Currently the allowed fields are defined as follows:
* `name`, `email`, `url`, `subject`, `textarea`
*
* If you need to add new fields to the Contact Form, please don't add them
- * to the whitelisted fields and leave them as extra fields.
+ * to the allowed fields and leave them as extra fields.
*
* The reasoning behind this is that both the admin Feedback view and the CSV
* export will not include any fields that are added to the list of
- * whitelisted fields without taking proper care to add them to all the
+ * allowed fields without taking proper care to add them to all the
* other places where they accessed/used/saved.
*
* The safest way to add new fields is to add them to the dropdown and the
* HTML list ( @see Grunion_Contact_Form_Field::render ) and don't add them
- * to the list of whitelisted fields. This way they will become a part of the
+ * to the list of allowed fields. This way they will become a part of the
* `extra fields` which are saved in the post meta and will be properly
* handled by the admin Feedback view and the CSV Export without any extra
* work.
*
- * If there is need to add a field to the whitelisted fields, then please
+ * If there is need to add a field to the allowed fields, then please
* take proper care to add logic to handle the field in the following places:
*
- * - Below in the switch statement - so the field is recognized as whitelisted.
+ * - Below in the switch statement - so the field is recognized as allowed.
*
* - Grunion_Contact_Form::process_submission - validation and logic.
*
@@ -2372,10 +2637,10 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
*/
function get_field_ids() {
$field_ids = array(
- 'all' => array(), // array of all field_ids
- 'extra' => array(), // array of all non-whitelisted field IDs
+ 'all' => array(), // array of all field_ids.
+ 'extra' => array(), // array of all non-allowed field IDs.
- // Whitelisted "standard" field IDs:
+ // Allowed "standard" field IDs:
// 'email' => field_id,
// 'name' => field_id,
// 'url' => field_id,
@@ -2388,7 +2653,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$type = $field->get_attribute( 'type' );
if ( isset( $field_ids[ $type ] ) ) {
- // This type of field is already present in our whitelist of "standard" fields for this form
+ // This type of field is already present in our allowed list of "standard" fields for this form
// Put it in extra
$field_ids['extra'][] = $id;
continue;
@@ -2403,6 +2668,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
case 'url':
case 'subject':
case 'textarea':
+ case 'consent':
$field_ids[ $type ] = $id;
break;
default:
@@ -2427,7 +2693,8 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$to = $this->get_attribute( 'to' );
$widget = $this->get_attribute( 'widget' );
- $contact_form_subject = $this->get_attribute( 'subject' );
+ $contact_form_subject = $this->get_attribute( 'subject' );
+ $email_marketing_consent = false;
$to = str_replace( ' ', '', $to );
$emails = explode( ',', $to );
@@ -2528,6 +2795,13 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
}
}
+ if ( isset( $field_ids['consent'] ) ) {
+ $field = $this->fields[ $field_ids['consent'] ];
+ if ( $field->value ) {
+ $email_marketing_consent = true;
+ }
+ }
+
$all_values = $extra_values = array();
$i = 1; // Prefix counter for stored metadata
@@ -2566,7 +2840,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' );
foreach ( $vars as $var ) {
- $$var = str_replace( array( "\n", "\r" ), '', $$var );
+ $$var = str_replace( array( "\n", "\r" ), '', (string) $$var );
}
// Ensure that Akismet gets all of the relevant information from the contact form,
@@ -2615,6 +2889,18 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$spam = '***SPAM*** ';
}
+ /**
+ * Filter whether a submitted contact form is in the comment disallowed list.
+ *
+ * @module contact-form
+ *
+ * @since 8.9.0
+ *
+ * @param bool $result Is the submitted feedback in the disallowed list.
+ * @param array $akismet_values Feedack values returned by the Akismet plugin.
+ */
+ $in_comment_disallowed_list = apply_filters( 'jetpack_contact_form_in_comment_disallowed_list', false, $akismet_values );
+
if ( ! $comment_author ) {
$comment_author = $comment_author_email;
}
@@ -2643,22 +2929,56 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$reply_to_addr = $comment_author_email;
}
- $headers = 'From: "' . $comment_author . '" <' . $from_email_addr . ">\r\n" .
- 'Reply-To: "' . $comment_author . '" <' . $reply_to_addr . ">\r\n";
+ /*
+ * The email headers here are formatted in a format
+ * that is the most likely to be accepted by wp_mail(),
+ * without escaping.
+ * More info: https://github.com/Automattic/jetpack/pull/19727
+ */
+ $headers = 'From: ' . $comment_author . ' <' . $from_email_addr . ">\r\n" .
+ 'Reply-To: ' . $comment_author . ' <' . $reply_to_addr . ">\r\n";
+
+ /**
+ * Allow customizing the email headers.
+ *
+ * Warning: DO NOT add headers or header data from the form submission without proper
+ * escaping and validation, or you're liable to allow abusers to use your site to send spam.
+ *
+ * Especially DO NOT take email addresses from the form data to add as CC or BCC headers
+ * without strictly validating each address against a list of allowed addresses.
+ *
+ * @module contact-form
+ *
+ * @since 10.2.0
+ *
+ * @param string|array $headers Email headers.
+ * @param string $comment_author Name of the author of the submitted feedback, if provided in form.
+ * @param string $reply_to_addr Email of the author of the submitted feedback, if provided in form.
+ * @param string|array $to Array of valid email addresses, or single email address, where the form is sent.
+ */
+ $headers = apply_filters(
+ 'jetpack_contact_form_email_headers',
+ $headers,
+ $comment_author,
+ $reply_to_addr,
+ $to
+ );
+
+ $all_values['email_marketing_consent'] = $email_marketing_consent;
// Build feedback reference
$feedback_time = current_time( 'mysql' );
$feedback_title = "{$comment_author} - {$feedback_time}";
$feedback_id = md5( $feedback_title );
- $all_values = array_merge(
- $all_values, array(
- 'entry_title' => the_title_attribute( 'echo=0' ),
- 'entry_permalink' => esc_url( get_permalink( get_the_ID() ) ),
- 'feedback_id' => $feedback_id,
- )
+ $entry_values = array(
+ 'entry_title' => the_title_attribute( 'echo=0' ),
+ 'entry_permalink' => esc_url( get_permalink( get_the_ID() ) ),
+ 'feedback_id' => $feedback_id,
);
+ $all_values = array_merge( $all_values, $entry_values );
+
/** 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 );
@@ -2667,8 +2987,14 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
$date_time_format = sprintf( $date_time_format, get_option( 'date_format' ), get_option( 'time_format' ) );
$time = date_i18n( $date_time_format, current_time( 'timestamp' ) );
- // keep a copy of the feedback as a custom post type
- $feedback_status = $is_spam === true ? 'spam' : 'publish';
+ // Keep a copy of the feedback as a custom post type.
+ if ( $in_comment_disallowed_list ) {
+ $feedback_status = 'trash';
+ } elseif ( $is_spam ) {
+ $feedback_status = 'spam';
+ } else {
+ $feedback_status = 'publish';
+ }
foreach ( (array) $akismet_values as $av_key => $av_value ) {
$akismet_values[ $av_key ] = Grunion_Contact_Form_Plugin::strip_tags( $av_value );
@@ -2720,6 +3046,20 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
update_post_meta( $post_id, '_feedback_akismet_values', $this->addslashes_deep( $akismet_values ) );
}
+ /**
+ * Fires after the feedback post for the contact form submission has been inserted.
+ *
+ * @module contact-form
+ *
+ * @since 8.6.0
+ *
+ * @param integer $post_id The post id that contains the contact form data.
+ * @param array $this->fields An array containg the form's Grunion_Contact_Form_Field objects.
+ * @param boolean $is_spam Whether the form submission has been identified as spam.
+ * @param array $entry_values The feedback entry values.
+ */
+ do_action( 'grunion_after_feedback_post_inserted', $post_id, $this->fields, $is_spam, $entry_values );
+
$message = self::get_compiled_form( $post_id, $this );
array_push(
@@ -2909,7 +3249,7 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
* Add a display name part to an email address
*
* SpamAssassin doesn't like addresses in HTML messages that are missing display names (e.g., `foo@bar.org`
- * instead of `"Foo Bar" <foo@bar.org>`.
+ * instead of `Foo Bar <foo@bar.org>`.
*
* @param string $address
*
@@ -2919,7 +3259,14 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
// If it's just the address, without a display name
if ( is_email( $address ) ) {
$address_parts = explode( '@', $address );
- $address = sprintf( '"%s" <%s>', $address_parts[0], $address );
+
+ /*
+ * The email address format here is formatted in a format
+ * that is the most likely to be accepted by wp_mail(),
+ * without escaping.
+ * More info: https://github.com/Automattic/jetpack/pull/19727
+ */
+ $address = sprintf( '%s <%s>', $address_parts[0], $address );
}
return $address;
@@ -3005,7 +3352,8 @@ class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
return addslashes( $value );
}
-}
+
+} // end class Grunion_Contact_Form
/**
* Class for the contact-field shortcode.
@@ -3038,15 +3386,19 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
function __construct( $attributes, $content = null, $form = null ) {
$attributes = shortcode_atts(
array(
- 'label' => null,
- 'type' => 'text',
- 'required' => false,
- 'options' => array(),
- 'id' => null,
- 'default' => null,
- 'values' => null,
- 'placeholder' => null,
- 'class' => null,
+ 'label' => null,
+ 'type' => 'text',
+ 'required' => false,
+ 'options' => array(),
+ 'id' => null,
+ 'default' => null,
+ 'values' => null,
+ 'placeholder' => null,
+ 'class' => null,
+ 'width' => null,
+ 'consenttype' => null,
+ 'implicitconsentmessage' => null,
+ 'explicitconsentmessage' => null,
), $attributes, 'contact-field'
);
@@ -3155,7 +3507,7 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
switch ( $field_type ) {
case 'email':
// Make sure the email address is valid
- if ( ! is_email( $field_value ) ) {
+ if ( ! is_string( $field_value ) || ! is_email( $field_value ) ) {
/* translators: %s is the name of a form field */
$this->add_error( sprintf( __( '%s requires a valid email address', 'jetpack' ), $field_label ) );
}
@@ -3169,7 +3521,7 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
break;
default:
// Just check for presence of any text
- if ( ! strlen( trim( $field_value ) ) ) {
+ if ( ! is_string( $field_value ) || ! strlen( trim( $field_value ) ) ) {
/* translators: %s is the name of a form field */
$this->add_error( sprintf( __( '%s is required', 'jetpack' ), $field_label ) );
}
@@ -3206,8 +3558,13 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
$field_label = $this->get_attribute( 'label' );
$field_required = $this->get_attribute( 'required' );
$field_placeholder = $this->get_attribute( 'placeholder' );
+ $field_width = $this->get_attribute( 'width' );
$class = 'date' === $field_type ? 'jp-contact-form-date' : $this->get_attribute( 'class' );
+ if ( ! empty( $field_width ) ) {
+ $class .= ' grunion-field-width-' . $field_width;
+ }
+
/**
* Filters the "class" attribute of the contact form input
*
@@ -3279,7 +3636,7 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
return apply_filters( 'grunion_contact_form_field_html', $rendered_field, $field_label, ( in_the_loop() ? get_the_ID() : null ) );
}
- function render_label( $type = '', $id, $label, $required, $required_field_text ) {
+ public function render_label( $type, $id, $label, $required, $required_field_text ) {
$type_class = $type ? ' ' .$type : '';
return
@@ -3366,6 +3723,29 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
return $field;
}
+ /**
+ * Render the consent field.
+ *
+ * @param string $id field id.
+ * @param string $class html classes (can be set by the admin).
+ */
+ private function render_consent_field( $id, $class ) {
+ $consent_type = 'explicit' === $this->get_attribute( 'consenttype' ) ? 'explicit' : 'implicit';
+ $consent_message = 'explicit' === $consent_type ? $this->get_attribute( 'explicitconsentmessage' ) : $this->get_attribute( 'implicitconsentmessage' );
+
+ $field = "<label class='grunion-field-label consent consent-" . $consent_type . "'>";
+
+ if ( 'implicit' === $consent_type ) {
+ $field .= "\t\t<input aria-hidden='true' type='checkbox' checked name='" . esc_attr( $id ) . "' value='" . esc_attr__( 'Yes', 'jetpack' ) . "' style='display:none;' /> \n";
+ } else {
+ $field .= "\t\t<input type='checkbox' name='" . esc_attr( $id ) . "' value='" . esc_attr__( 'Yes', 'jetpack' ) . "' " . $class . "/> \n";
+ }
+ $field .= "\t\t" . esc_html( $consent_message );
+ $field .= "</label>\n";
+ $field .= "<div class='clear-form'></div>\n";
+ return $field;
+ }
+
function render_checkbox_multiple_field( $id, $label, $value, $class, $required, $required_field_text ) {
$field = $this->render_label( '', $id, $label, $required, $required_field_text );
foreach ( (array) $this->get_attribute( 'options' ) as $optionIndex => $option ) {
@@ -3486,6 +3866,9 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
case 'date':
$field .= $this->render_date_field( $id, $label, $value, $field_class, $required, $required_field_text, $field_placeholder );
break;
+ case 'consent':
+ $field .= $this->render_consent_field( $id, $field_class );
+ break;
default: // text field
$field .= $this->render_default_field( $id, $label, $value, $field_class, $required, $required_field_text, $field_placeholder, $type );
break;
@@ -3493,6 +3876,7 @@ class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
$field .= "\t</div>\n";
return $field;
}
+
}
add_action( 'init', array( 'Grunion_Contact_Form_Plugin', 'init' ), 9 );
@@ -3588,7 +3972,7 @@ function jetpack_tracks_record_grunion_pre_message_sent( $post_id, $all_values,
$event_user = get_userdata( $event_user_id );
}
- require_lib( 'tracks/client' );
+ jetpack_require_lib( 'tracks/client' );
tracks_record_event( $event_user, $event_name, $event_props );
} else {
// If the form was sent by a logged out visitor, record event with Jetpack master user.