From daa737be3c35736f3386450133c55f6b32866273 Mon Sep 17 00:00:00 2001
From: Enej Bajgoric
Date: Tue, 8 Jul 2025 15:01:44 -0700
Subject: [PATCH 01/48] Forms: Adds a new Form_Responses class
---
.../changelog/update-form-response-storage | 4 +
.../src/contact-form/class-contact-form.php | 12 +-
.../src/contact-form/class-form-response.php | 806 ++++++++++++
.../src/contact-form/class-response-field.php | 157 +++
.../php/contact-form/Contact_Form_Test.php | 14 +-
.../php/contact-form/Form_Response_Test.php | 1130 +++++++++++++++++
.../php/contact-form/Response_Field_Test.php | 110 ++
.../tests/php/contact-form/class-utility.php | 137 ++
8 files changed, 2360 insertions(+), 10 deletions(-)
create mode 100644 projects/packages/forms/changelog/update-form-response-storage
create mode 100644 projects/packages/forms/src/contact-form/class-form-response.php
create mode 100644 projects/packages/forms/src/contact-form/class-response-field.php
create mode 100644 projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
create mode 100644 projects/packages/forms/tests/php/contact-form/Response_Field_Test.php
create mode 100644 projects/packages/forms/tests/php/contact-form/class-utility.php
diff --git a/projects/packages/forms/changelog/update-form-response-storage b/projects/packages/forms/changelog/update-form-response-storage
new file mode 100644
index 0000000000000..bc56c7d94b590
--- /dev/null
+++ b/projects/packages/forms/changelog/update-form-response-storage
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fixed
+
+Forms: Adds a new Form_Responses class that keeps consistency between DB form response and the one before we submit it
diff --git a/projects/packages/forms/src/contact-form/class-contact-form.php b/projects/packages/forms/src/contact-form/class-contact-form.php
index 6a43e0e60fa08..ca3211cd003e8 100644
--- a/projects/packages/forms/src/contact-form/class-contact-form.php
+++ b/projects/packages/forms/src/contact-form/class-contact-form.php
@@ -330,6 +330,14 @@ public static function get_default_subject( $attributes ) {
return $default_subject;
}
+ /**
+ * Get the forms processed count.
+ *
+ * @return int
+ */
+ public static function get_forms_count() {
+ return count( self::$forms );
+ }
/**
* Store shortcode content for recall later
@@ -1415,7 +1423,8 @@ public function get_field_ids() {
);
// Initialize marketing consent
- $field_ids['email_marketing_consent'] = null;
+ $field_ids['email_marketing_consent'] = null;
+ $field_ids['email_marketing_consent_field'] = null;
foreach ( $this->fields as $id => $field ) {
$type = $field->get_attribute( 'type' );
@@ -1448,6 +1457,7 @@ public function get_field_ids() {
case 'consent':
// Set email marketing consent for the first Consent type field
if ( null === $field_ids['email_marketing_consent'] ) {
+ $field_ids['email_marketing_consent_field'] = $id;
if ( $field->value ) {
$field_ids['email_marketing_consent'] = true;
} else {
diff --git a/projects/packages/forms/src/contact-form/class-form-response.php b/projects/packages/forms/src/contact-form/class-form-response.php
new file mode 100644
index 0000000000000..7ececfeb7e4d8
--- /dev/null
+++ b/projects/packages/forms/src/contact-form/class-form-response.php
@@ -0,0 +1,806 @@
+feedback_post = $feedback_post;
+ $this->form = $form;
+
+ if ( $this->feedback_post instanceof WP_Post ) {
+
+ $parsed_content = static::parse_content( $this->feedback_post->post_content, $this->feedback_post->post_mime_type );
+
+ $this->status = $this->feedback_post->post_status;
+ $this->feedback_id = $this->feedback_post->post_name;
+ $this->entry_id = $this->feedback_post->post_parent ? (int) $this->feedback_post->post_parent : 0;
+ $current_post = $this->entry_id ? get_post( $this->entry_id ) : null;
+
+ $this->fields = isset( $parsed_content['fields'] ) ? $parsed_content['fields'] : array();
+ $this->ip_address = isset( $parsed_content['ip'] ) ? $parsed_content['ip'] : null;
+ $this->subject = isset( $parsed_content['subject'] ) ? $parsed_content['subject'] : '';
+ $this->author = isset( $parsed_content['author'] ) ? $parsed_content['author'] : '';
+ $this->author_email = isset( $parsed_content['author_email'] ) ? $parsed_content['author_email'] : '';
+ $this->author_url = isset( $parsed_content['author_url'] ) ? $parsed_content['author_url'] : '';
+ $this->comment_content = isset( $parsed_content['comment_content'] ) ? $parsed_content['comment_content'] : '';
+ $this->has_consent = isset( $parsed_content['has_consent'] ) ? $parsed_content['has_consent'] : false;
+ $this->entry_title = isset( $parsed_content['entry_title'] ) ? $parsed_content['entry_title'] : '';
+ $this->entry_page = isset( $parsed_content['entry_page'] ) ? (int) $parsed_content['entry_page'] : 1;
+
+ } elseif ( is_array( $post_data ) && ! empty( $post_data ) ) {
+
+ // If post_data is provided, use it to populate fields.
+ $this->status = $this->status;
+ $this->feedback_id = $this->get_computed_feedback_id();
+ $this->fields = $this->get_computed_fields( $post_data );
+ $this->ip_address = Contact_Form_Plugin::get_ip_address();
+ $this->subject = $this->get_computed_subject( $post_data );
+ $this->author = $this->get_computed_author( $post_data );
+ $this->author_email = $this->get_computed_author_email( $post_data );
+ $this->author_url = $this->get_computed_author_url( $post_data );
+ $this->comment_content = $this->get_computed_comment_content( $post_data );
+ $this->has_consent = $this->get_computed_consent( $post_data );
+ $this->entry_id = ! empty( $current_post ) ? (int) $current_post->ID : 0;
+ $this->entry_title = ! empty( $current_post ) ? get_the_title( $current_post ) : '';
+ $this->entry_page = $current_page_number;
+ }
+
+ $this->entry_title = ! empty( $current_post ) ? get_the_title( $current_post ) : $this->entry_title;
+ $this->entry_permalink = ! empty( $current_post ) ? $this->get_computed_entry_permalink( $current_post, $this->entry_page ) : '';
+ }
+
+ /**
+ * Get a sanitized value from the post data.
+ *
+ * @param string $key The key to look for in the post data.
+ * @param array $post_data The post data array, typically $_POST.
+ *
+ * @return string|array The sanitized value, or an empty string if the key is not found.
+ */
+ private function get_field_value( $key, $post_data ) {
+ if ( isset( $post_data[ $key ] ) ) {
+ if ( is_array( $post_data[ $key ] ) ) {
+ return array_map( 'sanitize_text_field', wp_unslash( $post_data[ $key ] ) );
+ } else {
+ return sanitize_text_field( wp_unslash( $post_data[ $key ] ) );
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Create a response object from a feedback post ID.
+ *
+ * @param int $feedback_post_id The ID of the feedback post.
+ * @return static|null
+ */
+ public static function get( $feedback_post_id ) {
+
+ $feedback_post = get_post( $feedback_post_id );
+ if ( ! $feedback_post || self::POST_TYPE !== $feedback_post->post_type ) {
+ return null;
+ }
+
+ return new static( $feedback_post );
+ }
+
+ /**
+ * Create a response object from a form submission.
+ *
+ * @param array $post_data Typically $_POST.
+ * @param Contact_Form $form The form object.
+ * @param WP_Post|null $current_post The current post object, if available.
+ * @param int $current_page_number The current page number associated with the current post object entry.
+ *
+ * @return static
+ */
+ public static function from_submission( $post_data, $form, $current_post = null, $current_page_number = 1 ) {
+ return new static( null, $form, $post_data, $current_post, $current_page_number );
+ }
+
+ /**
+ * Get all the fields of the response.
+ */
+ public function get_fields() {
+ return $this->fields;
+ }
+
+ /**
+ * Get all the values of the response.
+ *
+ * This is a convenience method to get all values in a simple array format.
+ *
+ * This is done for backwards compatibility. Use `get_fields()` instead.
+ *
+ * @return array
+ */
+ private function get_all_values() {
+ $values = array();
+ foreach ( $this->fields as $field ) {
+ if ( ! $field instanceof Response_Field ) {
+ continue;
+ }
+ $values[ $field->get_key() ] = $field->get_value();
+ }
+ return $values;
+ }
+
+ /**
+ * Get the feedback ID of the response.
+ * Which is the same as the post name for feedback entries.
+ * Please note that this is not the same as the feedback post ID.
+ *
+ * @return string
+ */
+ public function get_feedback_id() {
+ return $this->feedback_id;
+ }
+
+ /**
+ * Get the author name of the feedback entry.
+ * If the author is not provided we will use the email instead.
+ *
+ * @return string
+ */
+ public function get_author() {
+ if ( ! empty( $this->author ) ) {
+ return $this->author;
+ }
+ if ( ! empty( $this->author_email ) ) {
+ return $this->author_email;
+ }
+ return $this->author;
+ }
+
+ /**
+ * Get the author email of a feedback entry.
+ *
+ * @return string
+ */
+ public function get_author_email() {
+ return $this->author_email;
+ }
+
+ /**
+ * Get the author url of a feedback entry.
+ *
+ * @return string
+ */
+ public function get_author_url() {
+ return $this->author_url;
+ }
+
+ /**
+ * Get the comment content of a feedback entry.
+ *
+ * @return string
+ */
+ public function get_comment_content() {
+ return $this->comment_content;
+ }
+
+ /**
+ * Get the IP address of the submitted feedback request.
+ *
+ * @return string|null
+ */
+ public function get_ip_address() {
+ return $this->ip_address;
+ }
+
+ /**
+ * Get the email subject.
+ *
+ * @return string
+ */
+ public function get_subject() {
+ return $this->subject;
+ }
+
+ /**
+ * Gets the value of the consent field.
+ *
+ * @return bool
+ */
+ public function has_consent() {
+ return $this->has_consent;
+ }
+
+ /**
+ * Get the feedback status. For example 'publish', 'spam' or 'trash'.
+ *
+ * @return string
+ */
+ public function get_status() {
+ return $this->status;
+ }
+
+ /**
+ * Sets the status of the feedback.
+ *
+ * @param string $status The status to set for the feedback entry.
+ * @return void
+ */
+ public function set_status( $status ) {
+ $this->status = $status;
+ }
+
+ /**
+ * Get the entry ID of the post that the feedback was submitted from.
+ *
+ * This is the post ID of the post or page that the feedback was submitted from.
+ *
+ * @return int|null
+ */
+ public function get_entry_id() {
+ return $this->entry_id;
+ }
+
+ /**
+ * Get the entry title of the post that the feedback was submitted from.
+ *
+ * This is the title of the post or page that the feedback was submitted from.
+ *
+ * @return string
+ */
+ public function get_entry_title() {
+ return $this->entry_title;
+ }
+
+ /**
+ * Get the permalink of the post or page that the feedback was submitted from.
+ * This includes the page number if the feedback was submitted from a paginated form.
+ *
+ * @return string
+ */
+ public function get_entry_permalink() {
+ return $this->entry_permalink;
+ }
+ /**
+ * Save the feedback entry to the database.
+ *
+ * @return int
+ */
+ public function save() {
+ $post_id = wp_insert_post(
+ array(
+ 'post_type' => self::POST_TYPE,
+ 'post_status' => $this->status,
+ 'post_name' => $this->feedback_id,
+ 'post_content' => $this->serialize(),
+ 'post_mime_type' => 'v2', // a way to help us identify what version of the data this is.
+ 'post_parent' => $this->entry_id,
+ )
+ );
+
+ $this->feedback_post = get_post( $post_id );
+ return $this->feedback_post ? $this->feedback_post->ID : 0;
+ }
+
+ /**
+ * Serialize the fields to JSON format.
+ *
+ * @return string
+ */
+ public function serialize() {
+
+ $fields_to_serialize = array(
+ 'subject' => $this->subject,
+ 'author' => $this->author,
+ 'author_email' => $this->author_email,
+ 'author_url' => $this->author_url,
+ 'comment_content' => $this->comment_content,
+ 'has_consent' => (bool) $this->has_consent,
+ 'entry_title' => $this->entry_title,
+ 'entry_page' => $this->entry_page,
+ 'ip' => $this->ip_address,
+ );
+
+ $fields_to_serialize['fields'] = array();
+ foreach ( $this->fields as $field ) {
+ if ( ! $field instanceof Response_Field ) {
+ continue;
+ }
+ $fields_to_serialize['fields'][] = $field->serialize();
+ }
+
+ // Check if the IP should be included.
+ if ( apply_filters( 'jetpack_contact_form_forget_ip_address', false, $this->ip_address ) ) {
+ $fields_to_serialize['ip'] = null;
+ }
+
+ return wp_json_encode( $fields_to_serialize );
+ }
+
+ /**
+ * Helper function to parse the post content.
+ *
+ * @param string $post_content The post content to parse.
+ * @param string|null $version The version of the content format.
+ * @return array Parsed fields.
+ */
+ public static function parse_content( $post_content = '', $version = null ) {
+ if ( $version === 'v2' ) {
+ $decoded_content = json_decode( $post_content, true );
+ if ( $decoded_content === null ) {
+ // If JSON decoding fails, try to decode the second try with stripslashes and trim.
+ // This is a workaround for some cases where the JSON data is not properly formatted.
+ $decoded_content = json_decode( stripslashes( trim( $post_content ) ), true );
+ }
+
+ if ( $decoded_content === null ) {
+ return array();
+ }
+ $fields = array();
+ foreach ( $decoded_content['fields'] as $field ) {
+ $fields[ $field['key'] ] = Response_Field::from_serialized( $field );
+ }
+ $decoded_content['fields'] = $fields;
+ return $decoded_content;
+ }
+
+ // parse_feedback_content
+ $all_values = array();
+ $content = explode( '', $post_content );
+ $lines = array();
+ $comment_content = '';
+ if ( count( $content ) > 1 ) {
+ $comment_content = $content[0];
+ $content = str_ireplace( array( '
', ')
' ), '', $content[1] );
+
+ if ( str_contains( $content, 'JSON_DATA' ) ) {
+ $chunks = explode( "\nJSON_DATA", $content );
+
+ $all_values = json_decode( $chunks[1], true );
+
+ if ( $all_values === null ) {
+ // If JSON decoding fails, try to decode the second try with stripslashes and trim.
+ // This is a workaround for some cases where the JSON data is not properly formatted.
+ $all_values = json_decode( stripslashes( trim( $chunks[1] ) ), true );
+ }
+ $lines = array_filter( explode( "\n", $chunks[0] ) );
+ } else {
+ $fields_array = preg_replace( '/.*Array\s\( (.*)\)/msx', '$1', $content );
+
+ // This line of code is used to parse a string containing key-value pairs formatted as [Key] => Value and extract the keys and values into an array.
+ // The regular expression ensures that each key-value pair is correctly identified and captured.
+ // Given an input string
+ // [Key1] => Value1
+ // [Key2] => Value2
+ // it $matches[1]: The keys (e.g., Key1, Key2 ).
+ // and $matches[2]: The values (e.g., Value1, Value2 ).
+ preg_match_all( '/^\s*\[([^\]]+)\] =\>\; (.*)(?=^\s*(\[[^\]]+\] =\>\;)|\z)/msU', $fields_array, $matches );
+
+ if ( count( $matches ) > 1 ) {
+ $all_values = array_combine( array_map( 'trim', $matches[1] ), array_map( 'trim', $matches[2] ) );
+ }
+
+ $lines = array_filter( explode( "\n", $content ) );
+ }
+ }
+
+ $var_map = array(
+ 'AUTHOR' => 'author',
+ 'AUTHOR EMAIL' => 'author_email',
+ 'AUTHOR URL' => 'author_url',
+ 'SUBJECT' => 'subject',
+ 'IP' => 'ip',
+ );
+
+ $decoded_fields = array();
+
+ foreach ( $lines as $line ) {
+ $vars = explode( ': ', $line, 2 );
+ if ( ! empty( $vars ) ) {
+ if ( isset( $var_map[ $vars[0] ] ) ) {
+ $type = $var_map[ $vars[0] ];
+ $decoded_fields[ $type ] = self::strip_tags( trim( $vars[1] ) );
+ }
+ }
+ }
+ // All fields should always be an array, even if empty.
+ if ( ! is_array( $all_values ) ) {
+ $all_values = array();
+ }
+
+ $non_user_fields = array(
+ 'email_marketing_consent',
+ 'entry_title',
+ 'entry_permalink',
+ 'entry_page',
+ 'feedback_id',
+ );
+
+ foreach ( $all_values as $key => $value ) {
+ $key = wp_strip_all_tags( $key );
+ $label = '';
+ if ( in_array( $key, $non_user_fields, true ) ) {
+ $decoded_fields[ $key ] = $value;
+ // Skip fields that are not user-submitted.
+ continue;
+ }
+ $decoded_fields['fields'][ $key ] = new Response_Field( $key, $label, $value );
+ }
+
+ $decoded_fields['comment_content'] = trim( self::strip_tags( $comment_content ) );
+
+ return $decoded_fields;
+ }
+
+ /**
+ * Strips HTML tags from input. Output is NOT HTML safe.
+ *
+ * @param mixed $data_with_tags - data we're stripping HTML tags from.
+ * @return mixed
+ */
+ public static function strip_tags( $data_with_tags ) {
+ $data_without_tags = array();
+ if ( is_array( $data_with_tags ) ) {
+ foreach ( $data_with_tags as $index => $value ) {
+ if ( is_array( $value ) ) {
+ $data_without_tags[ $index ] = self::strip_tags( $value );
+ continue;
+ }
+
+ $index = sanitize_text_field( (string) $index );
+ $value = wp_kses_post( (string) $value );
+ $value = str_replace( '&', '&', $value ); // undo damage done by wp_kses_normalize_entities()
+
+ $data_without_tags[ $index ] = $value;
+ }
+ } else {
+ $data_without_tags = wp_kses_post( (string) $data_with_tags );
+ $data_without_tags = str_replace( '&', '&', $data_without_tags ); // undo damage done by wp_kses_normalize_entities()
+ }
+
+ return $data_without_tags;
+ }
+
+ /**
+ * Get the computed feedback id.
+ *
+ * @return string
+ */
+ private function get_computed_feedback_id() {
+ $comment_author = $this->get_author();
+
+ // Build feedback reference
+ $feedback_time = \current_time( 'mysql' );
+ return md5( "{$comment_author} - {$feedback_time}" );
+ }
+
+ /**
+ * Get all the fields of the response, computed from the post data.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return array An array of Response_Field objects.
+ */
+ private function get_computed_fields( $post_data ) {
+
+ $fields = array();
+
+ $field_ids = $this->form->get_field_ids();
+ // For all fields, grab label and value
+ $i = 1;
+ foreach ( $field_ids['all'] as $field_id ) {
+ $field = $this->form->fields[ $field_id ];
+ $type = $field->get_attribute( 'type' );
+ if ( ! $field->is_field_renderable( $type ) ) {
+ continue;
+ }
+
+ $value = $this->get_field_value( $field_id, $post_data );
+ $label = wp_strip_all_tags( $field->get_attribute( 'label' ) );
+ $key = $i . '_' . $label;
+
+ $fields[ $key ] = new Response_Field( $key, $label, $value, $type );
+ ++$i; // Increment prefix counter for the next field
+ }
+
+ return $fields;
+ }
+
+ /**
+ * Gets the computed subject.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_subject( $post_data ) {
+
+ $contact_form_subject = $this->form->get_attribute( 'subject' );
+ $field_ids = $this->form->get_field_ids();
+
+ if ( isset( $field_ids['subject'] ) ) {
+ $value = $this->get_field_value( $field_ids['subject'], $post_data );
+ if ( ! empty( $value ) ) {
+ $contact_form_subject = $value;
+ }
+ }
+
+ return apply_filters( 'contact_form_subject', $contact_form_subject, $this->get_all_values() );
+ }
+
+ /**
+ * Gets the computed author.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_author( $post_data ) {
+ $field_ids = $this->form->get_field_ids();
+ if ( isset( $field_ids['name'] ) ) {
+ $value = $this->get_field_value( $field_ids['name'], $post_data );
+ if ( is_string( $value ) ) {
+ return self::strip_tags(
+ stripslashes(
+ /** This filter is already documented in core/wp-includes/comment-functions.php */
+ apply_filters( 'pre_comment_author_name', addslashes( $value ) )
+ )
+ );
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Gets the computed author email.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_author_email( $post_data ) {
+ $field_ids = $this->form->get_field_ids();
+ if ( isset( $field_ids['email'] ) ) {
+ $value = $this->get_field_value( $field_ids['email'], $post_data );
+ if ( is_string( $value ) ) {
+ return self::strip_tags(
+ stripslashes(
+ /** This filter is already documented in core/wp-includes/comment-functions.php */
+ apply_filters( 'pre_comment_author_email', addslashes( $value ) )
+ )
+ );
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Gets the computed author url.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_author_url( $post_data ) {
+ $field_ids = $this->form->get_field_ids();
+ if ( isset( $field_ids['url'] ) ) {
+ $value = $this->get_field_value( $field_ids['url'], $post_data );
+ if ( is_string( $value ) ) {
+ return self::strip_tags(
+ stripslashes(
+ /** This filter is already documented in core/wp-includes/comment-functions.php */
+ apply_filters( 'pre_comment_author_url', addslashes( $value ) )
+ )
+ );
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Gets the computed comment content.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_comment_content( $post_data ) {
+ $field_ids = $this->form->get_field_ids();
+ if ( isset( $field_ids['textarea'] ) ) {
+ $value = $this->get_field_value( $field_ids['textarea'], $post_data );
+ if ( is_string( $value ) ) {
+ return trim( self::strip_tags( stripslashes( $value ) ) );
+ }
+ }
+ return '';
+ }
+
+ /**
+ * Gets the computed consent.
+ *
+ * @param array $post_data The post data from the form submission.
+ * @return string
+ */
+ private function get_computed_consent( $post_data ) {
+ $field_ids = $this->form->get_field_ids();
+
+ if ( isset( $field_ids['email_marketing_consent_field'] ) && $field_ids['email_marketing_consent_field'] !== null ) {
+ return (bool) $this->get_field_value( $field_ids['email_marketing_consent_field'], $post_data );
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the permalink of post parent associated with the feedback.
+ *
+ * So that we can link the user to the URL that the feedback was created from.
+ *
+ * @param WP_Post|null $current_post The current post object, if available.
+ * @param int $current_page_number The current page number associated with the current post.
+ *
+ * @return string The permalink of the post or page that the feedback was submitted from.
+ */
+ private function get_computed_entry_permalink( $current_post = null, $current_page_number = 1 ) {
+
+ if ( $current_post instanceof WP_Post ) {
+ $permalink = get_the_permalink( $current_post );
+ if ( $current_page_number > 1 ) {
+ $permalink = add_query_arg( 'page', $current_page_number, $permalink );
+ }
+ return $permalink;
+ }
+ return '';
+ }
+}
diff --git a/projects/packages/forms/src/contact-form/class-response-field.php b/projects/packages/forms/src/contact-form/class-response-field.php
new file mode 100644
index 0000000000000..80c027f2eb10d
--- /dev/null
+++ b/projects/packages/forms/src/contact-form/class-response-field.php
@@ -0,0 +1,157 @@
+key = $key;
+ $this->label = $label;
+ $this->value = $value;
+ $this->type = $type;
+ $this->meta = $meta;
+ }
+
+ /**
+ * Get the value of the field.
+ *
+ * @return string
+ */
+ public function get_key() {
+ return $this->key;
+ }
+
+ /**
+ * Get the label of the field.
+ *
+ * @return string
+ */
+ public function get_label() {
+ return $this->label;
+ }
+
+ /**
+ * Get the value of the field.
+ *
+ * @return mixed
+ */
+ public function get_value() {
+ return $this->value;
+ }
+
+ /**
+ * Get the value of the field.
+ *
+ * @return mixed
+ */
+ public function render_value() {
+ return $this->value;
+ }
+
+ /**
+ * Get the type of the field.
+ *
+ * @return string
+ */
+ public function get_type() {
+ return $this->type;
+ }
+
+ /**
+ * Get the meta of the field.
+ *
+ * @return string
+ */
+ public function get_meta() {
+ return $this->meta;
+ }
+
+ /**
+ * Get the serialized representation of the field.
+ *
+ * @return array
+ */
+ public function serialize() {
+ return array(
+ 'key' => $this->get_key(),
+ 'label' => $this->get_label(),
+ 'value' => $this->get_value(),
+ 'type' => $this->get_type(),
+ 'meta' => $this->get_meta(),
+ );
+ }
+ /**
+ * Create a Response_Field object from serialized data.
+ *
+ * @param array $data The serialized data.
+ *
+ * @return Response_Field|null Returns a Response_Field object or null if the data is invalid.
+ */
+ public static function from_serialized( $data ) {
+ if ( ! is_array( $data ) || ! isset( $data['key'] ) || ! isset( $data['value'] ) || ! isset( $data['label'] ) ) {
+ return null;
+ }
+
+ return new self(
+ $data['key'],
+ $data['label'],
+ $data['value'],
+ isset( $data['type'] ) ? $data['type'] : 'basic',
+ isset( $data['meta'] ) ? $data['meta'] : array()
+ );
+ }
+}
diff --git a/projects/packages/forms/tests/php/contact-form/Contact_Form_Test.php b/projects/packages/forms/tests/php/contact-form/Contact_Form_Test.php
index fe63c94439a10..58c03b2566cb8 100644
--- a/projects/packages/forms/tests/php/contact-form/Contact_Form_Test.php
+++ b/projects/packages/forms/tests/php/contact-form/Contact_Form_Test.php
@@ -131,15 +131,11 @@ public function tear_down() {
* @param string $form_id Optional form ID. If not provided, will use $this->post->ID.
*/
private function add_field_values( $values, $form_id = null ) {
- $prefix = $form_id ? $form_id : 'g' . $this->post->ID;
- $_POST = array();
- foreach ( $values as $key => $val ) {
- if ( strpos( $key, 'contact-form' ) === 0 || strpos( $key, 'action' ) === 0 ) {
- $_POST[ $key ] = $val;
- } else {
- $_POST[ $prefix . '-' . $key ] = $val;
- }
- }
+ Utility::add_post_request(
+ $values,
+ $form_id,
+ $this->post->ID
+ );
}
/**
diff --git a/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php b/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
new file mode 100644
index 0000000000000..7a18da98fd803
--- /dev/null
+++ b/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
@@ -0,0 +1,1130 @@
+assertNull( $response );
+ }
+
+ public function test_from_post_id_returns_instance_for_valid_feedback_post() {
+ $post_id = \wp_insert_post(
+ array(
+ 'post_type' => 'feedback',
+ 'post_status' => 'publish',
+ 'post_title' => 'Test Feedback',
+ 'post_content' => '{}',
+ 'page_template' => 'v2',
+ )
+ );
+ $response = Form_Response::get( $post_id );
+ $this->assertInstanceOf( Form_Response::class, $response );
+ }
+
+ public function test_from_submission_sets_fields_and_post_data() {
+ $form = new Contact_Form( array() );
+ $post_data = array(
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ 'message' => 'Hello!',
+ 'ignore' => 'should not be included',
+ );
+ $response = Form_Response::from_submission( $post_data, $form );
+ $this->assertInstanceOf( Form_Response::class, $response );
+ }
+
+ public function test_form_response_is_matches_empty_data() {
+ $form = new Contact_Form( array() );
+ $post_data = array();
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $response->serialize(), $post_response->serialize() );
+ $this->assertEquals( $response->get_fields(), $post_response->get_fields() );
+ }
+
+ public function test_form_response_is_matches_submission_data() {
+ $name = 'John Doe';
+ $email = 'john@example.com';
+ $message = 'Test message';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => $name,
+ 'email' => $email,
+ 'message' => $message,
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $response->serialize(), $post_response->serialize() );
+ $this->assertEquals( $response->get_fields(), $post_response->get_fields() );
+
+ foreach ( $response->get_fields() as $field ) {
+ $this->assertInstanceOf( Response_Field::class, $field );
+ }
+
+ foreach ( $post_response->get_fields() as $field ) {
+ $this->assertInstanceOf( Response_Field::class, $field );
+ }
+
+ foreach ( $post_response->get_fields() as $field_key => $field ) {
+ $this->assertEquals( $response->get_fields()[ $field_key ]->serialize(), $post_response->get_fields()[ $field_key ]->serialize(), 'Serialized response field should match' );
+ }
+
+ $this->assertEquals( $name, $response->get_fields()['1_Name']->get_value(), 'Response field value should match' );
+ $this->assertEquals( $name, $post_response->get_fields()['1_Name']->get_value(), 'Saved response field value should match' );
+
+ $this->assertEquals( 'Name', $response->get_fields()['1_Name']->get_label(), 'Name response field label should match' );
+ $this->assertEquals( 'Name', $post_response->get_fields()['1_Name']->get_label(), 'Saved response field label should match' );
+ $this->assertEquals( 'name', $response->get_fields()['1_Name']->get_type(), 'Response field type should match' );
+ $this->assertEquals( 'name', $post_response->get_fields()['1_Name']->get_type(), 'Saved response type value should match' );
+
+ $this->assertEquals( $email, $response->get_fields()['2_Email']->get_value(), 'Response field value should match' );
+ $this->assertEquals( $email, $post_response->get_fields()['2_Email']->get_value(), 'Saved response field value should match' );
+
+ $this->assertEquals( $message, $response->get_fields()['3_Message']->get_value(), 'Response field value should match' );
+ $this->assertEquals( $message, $post_response->get_fields()['3_Message']->get_value(), 'Saved Name response field value should match ' );
+ }
+
+ /**
+ * Test that a previously saved response can be handled correctly.
+ *
+ * This test checks if the Form_Response class can retrieve and handle
+ * a response that was saved in the legacy format.
+ */
+ public function test_handle_previously_saved_response() {
+
+ $post_id = Utility::create_legacy_feedback(
+ array(
+ '1_field' => 'value1',
+ '2_field' => 'value2',
+ )
+ );
+
+ $response = Form_Response::get( $post_id );
+
+ $this->assertInstanceOf( Form_Response::class, $response );
+
+ $field = $response->get_fields()['1_field'];
+
+ $this->assertInstanceOf( Response_Field::class, $field );
+ $this->assertEquals( '1_field', $field->get_key() );
+ $this->assertEquals( 'value1', $field->render_value() );
+ $this->assertEquals( 'value1', $field->render_value() );
+ $this->assertEquals( 'basic', $field->get_type() ); // Assuming 'basic' is the default type for a simple text field.
+ }
+ /**
+ * Tests that the feedback ID is computed correctly when saving a from response.
+ *
+ * It should be non empty and match the post slug.
+ */
+ public function test_form_response_computed_feedback_id() {
+ $name = 'John Doe';
+ $email = 'john@example.com';
+ $message = 'Test message';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => $name,
+ 'email' => $email,
+ 'message' => $message,
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post = get_post( $post_id );
+
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $response->get_feedback_id(), $post_response->get_feedback_id(), 'Feedback ID should match' );
+ $this->assertEquals( $post->post_name, $post_response->get_feedback_id(), 'Feedback ID should match post slug' );
+ $this->assertNotEmpty( $post_response->get_feedback_id(), 'Feedback ID should not be empty' );
+ }
+
+ /**
+ * Test the IP address is included in the serialized response.
+ * It should be always available when the reponse is created during the form submission.
+ *
+ * It should only be empty if the response that has the filter applied to it.
+ */
+ public function test_ip_address_included_in_serialized_response() {
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ // The IP address should be present.
+ $this->assertNotEmpty( $response->get_ip_address(), 'IP address should not be empty' );
+ $this->assertNotEmpty( $post_response->get_ip_address(), 'IP address should not be empty' );
+ $this->assertEquals( $response->get_ip_address(), $post_response->get_ip_address(), 'IP address should match' );
+
+ add_filter( 'jetpack_contact_form_forget_ip_address', '__return_true' );
+ $new_post_id = $response->save();
+ remove_filter( 'jetpack_contact_form_forget_ip_address', '__return_true' );
+
+ // The IP address should NOT be present.
+ $post_response = Form_Response::get( $new_post_id );
+ $this->assertEmpty( $post_response->get_ip_address(), 'IP address should BE empty' );
+ }
+
+ /**
+ * Test the IP address is included in the serialized response.
+ * It should be always available when the reponse is created during the form submission.
+ *
+ * It should only be empty if the response that has the filter applied to it.
+ */
+ public function test_ip_address_in_legacy() {
+ $ip = 'http://123.123.123.122';
+
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ null,
+ null,
+ null,
+ $ip
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $ip, $post_response->get_ip_address(), 'IP should match the legacy feedback IP' );
+ }
+
+ /**
+ * Test the subject line is computed for legacy correctly.
+ */
+ public function test_computed_subject_legacy() {
+ $subject = 'Test Subject';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ null,
+ null,
+ null,
+ null,
+ $subject
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $subject, $post_response->get_subject(), 'Subject should match the legacy feedback post subject' );
+ }
+
+ /**
+ * Test the subject line is computed correctly.
+ */
+ public function test_computed_form_subject() {
+ $subject = 'Test Subject';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ 'subject' => $subject,
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $subject, $response->get_subject(), 'Subject should match the form submission' );
+ $this->assertEquals( $subject, $post_response->get_subject(), 'Subject should match the saved form submission' );
+ }
+
+ /**
+ * Test the subject line is computed via field
+ */
+ public function test_computed_form_subject_field() {
+ $subject = 'Test Subject';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'John Doe',
+ 'subject' => $subject,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Subject' type='subject' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $subject, $response->get_subject(), 'Subject should match the form submission' );
+ $this->assertEquals( $subject, $post_response->get_subject(), 'Subject should match the saved form submission' );
+ }
+
+ /**
+ * Test the subject line is computed correctly when both a subject attribute and a field is present.
+ */
+ public function test_computed_form_subject_field_overwrites() {
+ $subject = 'Test Subject';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'John Doe',
+ 'subject' => $subject,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ 'subject' => $subject . ' (from form attributes)',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Subject' type='subject' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $subject, $response->get_subject(), 'Subject should match the form submission' );
+ $this->assertEquals( $subject, $post_response->get_subject(), 'Subject should match the saved form submission' );
+ }
+
+ /**
+ * Test the subject line is computed correctly and the filter is applied correctly.
+ */
+ public function test_computed_form_subject_field_overwrites_filter() {
+ $subject = 'Test Subject';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'John Doe',
+ 'subject' => $subject,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ 'subject' => $subject . ' (from form attributes)',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Subject' type='subject' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ add_filter( 'contact_form_subject', array( $this, 'subject_from_filter' ), 10, 2 );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ remove_filter( 'contact_form_subject', array( $this, 'subject_from_filter' ), 10, 2 );
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( 'Overwritten Subject (from filter)', $response->get_subject(), 'Subject should match the form submission' );
+ $this->assertEquals( 'Overwritten Subject (from filter)', $post_response->get_subject(), 'Subject should match the saved form submission' );
+ }
+ /**
+ * Callback for the contact_form_subject filter.
+ *
+ * This function is used to overwrite the subject line when the filter is applied.
+ *
+ * @return string The overwritten subject line.
+ */
+ public function subject_from_filter() {
+ // Overwrite the subject with a different value.
+ return 'Overwritten Subject (from filter)';
+ }
+
+ public function test_computed_name_for_legacy() {
+ $author = 'Mikey Mouse';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ $author
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $author, $post_response->get_author(), 'Author should match the legacy feedback post author' );
+ }
+
+ public function test_computed_name() {
+ $author = 'Mikey Mouse';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => $author,
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $author, $response->get_author(), 'Author should match the form submission' );
+ $this->assertEquals( $author, $post_response->get_author(), 'Author should match the saved form submission' );
+ }
+
+ public function test_computed_name_as_email() {
+ $author = ''; // author is empty
+ $email = 'email@email.com';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => $author,
+ 'email' => $email,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $email, $response->get_author(), 'Author should match the form submission' );
+ $this->assertEquals( $email, $post_response->get_author(), 'Author should match the saved form submission' );
+ }
+
+ public function test_computed_name_filter() {
+ $author = 'Mikey Mouse';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => $author,
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ add_filter( 'pre_comment_author_name', array( $this, 'set_filter_as_string' ) );
+ $response = Form_Response::from_submission( $post_data, $form );
+ remove_filter( 'pre_comment_author_name', array( $this, 'set_filter_as_string' ) );
+
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( 'STRING', $response->get_author(), 'Author should match the form submission' );
+ $this->assertEquals( 'STRING', $post_response->get_author(), 'Author should match the saved form submission' );
+ }
+ /**
+ * A helper function that sets the filter to return string 'STRING'.
+ *
+ * @return string
+ */
+ public function set_filter_as_string() {
+ return 'STRING';
+ }
+
+ public function test_computed_email_for_legacy() {
+ $email = 'email@email.com';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ null,
+ $email
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $email, $post_response->get_author_email(), 'Author email should match the legacy feedback post author email' );
+ }
+
+ public function test_computed_email() {
+
+ $email = 'email@email.com';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'author ',
+ 'email' => $email,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $email, $response->get_author_email(), 'Author email should match the form submission' );
+ $this->assertEquals( $email, $post_response->get_author_email(), 'Author email should match the saved form submission' );
+ }
+
+ public function test_computed_email_filter() {
+ $email = 'email@email.com';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'name' => 'joe',
+ 'email' => $email,
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Name' type='name' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ add_filter( 'pre_comment_author_email', array( $this, 'set_filter_as_string' ) );
+ $response = Form_Response::from_submission( $post_data, $form );
+ remove_filter( 'pre_comment_author_email', array( $this, 'set_filter_as_string' ) );
+
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( 'STRING', $response->get_author_email(), 'Author email should match the form submission' );
+ $this->assertEquals( 'STRING', $post_response->get_author_email(), 'Author email should match the saved form submission' );
+ }
+
+ public function test_computed_url_for_legacy() {
+ $url = 'https://wordpress.com';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ null,
+ null,
+ $url
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $url, $post_response->get_author_url(), 'Author url should match the legacy feedback post author url' );
+ }
+
+ public function test_computed_url() {
+ $url = 'https://wordpress.com';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'url' => $url,
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Url' type='url' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $url, $response->get_author_url(), 'Author url should match the form submission' );
+ $this->assertEquals( $url, $post_response->get_author_url(), 'Author url should match the saved form submission' );
+ }
+
+ public function test_computed_url_filter() {
+ $url = 'https://wordpress.com';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'url' => $url,
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Url' type='url' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ add_filter( 'pre_comment_author_url', array( $this, 'set_filter_as_string' ) );
+ $response = Form_Response::from_submission( $post_data, $form );
+ remove_filter( 'pre_comment_author_url', array( $this, 'set_filter_as_string' ) );
+
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( 'STRING', $response->get_author_url(), 'Author url should match the form submission' );
+ $this->assertEquals( 'STRING', $post_response->get_author_url(), 'Author url should match the saved form submission' );
+ }
+
+ public function test_computed_comment_content_for_legacy() {
+ $content = 'Some comment content!';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ $content
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $content, $post_response->get_comment_content(), 'Comment content should match the legacy feedback post author url' );
+ }
+
+ public function test_computed_comment_content() {
+ $content = 'Some comment content!';
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'url' => 'https://howdy.com',
+ 'email' => 'email@email.com',
+ 'message' => $content,
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Url' type='url' required='1'/][contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ // Create a contact form
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $content, $response->get_comment_content(), 'Comment content should match the form submission' );
+ $this->assertEquals( $content, $post_response->get_comment_content(), 'Comment content should match the saved form submission' );
+ }
+
+ public function test_status_from_legacy() {
+ $status = 'spam';
+ $post_id = Utility::create_legacy_feedback(
+ array(),
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'spam'
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $status, $post_response->get_status(), 'Status should match the legacy feedback status' );
+ }
+
+ public function test_computed_status() {
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( 'publish', $response->get_status(), 'Status should match the form submission' );
+ $this->assertEquals( 'publish', $post_response->get_status(), 'Status should match the saved form submission' );
+ }
+
+ public function test_set_status() {
+ $status = 'trash';
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ 'message' => 'Test message',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/][contact-field label='Message' type='textarea' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form );
+ $response->set_status( $status );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertEquals( $status, $response->get_status(), 'Status should match the form submission' );
+ $this->assertEquals( $status, $post_response->get_status(), 'Status should match the saved form submission' );
+ }
+
+ public function test_consent() {
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ 'consent' => 'Yes',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/][contact-field label='Consent' type='consent' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+ $this->assertTrue( $response->has_consent(), 'Has consent should match the form submission' );
+ $this->assertTrue( $post_response->has_consent(), 'Has consent should match the saved form submission' );
+ }
+
+ public function test_empty_consent() {
+
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ 'consent' => '',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/][contact-field label='Consent' type='consent' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form );
+ $post_id = $response->save();
+ $post_response = Form_Response::get( $post_id );
+ $this->assertFalse( $response->has_consent(), 'Has consent should match the form submission' );
+ $this->assertFalse( $post_response->has_consent(), 'Has consent should match the saved form submission' );
+ }
+
+ /**
+ * Helper function for creating the post context.
+ * This is helpful for testing the post context.
+ **/
+ private function create_post_context() {
+ $author_id = wp_insert_user(
+ array(
+ 'user_email' => 'john@example.com',
+ 'user_login' => 'test_user',
+ 'user_pass' => 'abc123',
+ )
+ );
+
+ $post_id = wp_insert_post(
+ array(
+ 'post_title' => 'POST TITLE ' . microtime(),
+ 'post_content' => 'POST CONTENT',
+ 'post_status' => 'publish',
+ 'post_author' => $author_id,
+ ),
+ true
+ );
+
+ global $post;
+ $post = get_post( $post_id );
+ return $post;
+ }
+
+ /**
+ * Helper function for destroying the post context.
+ * This is helpful cleaning up the post context after the test.
+ **/
+ private function destroy_post_context() {
+ global $post;
+ if ( $post ) {
+ wp_delete_user( $post->post_author, true );
+ wp_delete_post( $post->ID, true );
+ $post = null;
+ }
+ }
+
+ public function test_compute_entry_ID_legacy() {
+ $current_post = $this->create_post_context();
+ $post_id = Utility::create_legacy_feedback();
+ $this->destroy_post_context();
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $current_post->ID, $post_response->get_entry_id(), 'Entry_ID should match the saved form submission' );
+ }
+
+ public function test_compute_entry_ID() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ 'consent' => '',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/][contact-field label='Consent' type='consent' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+ $this->destroy_post_context();
+
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEquals( $current_post->ID, $response->get_entry_id(), 'Entry_ID should match the form submission' );
+ $this->assertEquals( $current_post->ID, $post_response->get_entry_id(), 'Entry_ID should match the saved form submission' );
+ }
+
+ public function test_compute_entry_title() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+
+ $post_response = Form_Response::get( $post_id );
+ $this->destroy_post_context();
+
+ $this->assertEquals( $current_post->post_title, $response->get_entry_title(), 'Post title should match the form submission' );
+ $this->assertEquals( $current_post->post_title, $post_response->get_entry_title(), 'Post title should match the saved form submission' );
+ }
+
+ public function test_compute_entry_title_updated() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+
+ // Update the post title to simulate an update.
+ $update_title = 'Updated Title';
+ wp_update_post(
+ array(
+ 'ID' => $current_post->ID,
+ 'post_title' => $update_title,
+ )
+ );
+
+ $post_response = Form_Response::get( $post_id );
+ $this->destroy_post_context();
+
+ $this->assertEquals( $update_title, $post_response->get_entry_title(), 'Post Title should match the new updated title saved form submission' );
+ }
+
+ public function test_compute_entry_title_deleted() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+ $this->destroy_post_context();
+
+ $this->assertSame( '', get_the_title( $current_post->ID ), 'Post title should not be available after the post is deleted' );
+ // At this point we should have a deleted post.
+ $post_response = Form_Response::get( $post_id );
+
+ $this->assertNotEmpty( $post_response->get_entry_title(), 'Post Title should NOT be empty after the post is deleted' );
+ $this->assertEquals( $current_post->post_title, $post_response->get_entry_title(), 'Post Title should match the saved form submission Original post title' );
+ }
+
+ public function test_compute_entry_permalink() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+
+ $post_response = Form_Response::get( $post_id );
+ $this->destroy_post_context();
+ $current_permalink = get_the_permalink( $current_post );
+ $this->assertEquals( $current_permalink, $response->get_entry_permalink(), 'Post permalink should match the form submission' );
+
+ $this->assertEquals( $current_permalink, $post_response->get_entry_permalink(), 'Post permalink should match the saved form submission' );
+ }
+
+ public function test_compute_entry_permalink_deleted_post() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post );
+ $post_id = $response->save();
+ $this->destroy_post_context(); // Destroy the post context to simulate a deleted post.
+ $post_response = Form_Response::get( $post_id );
+ $this->assertEmpty( $post_response->get_entry_permalink(), 'Post permalink should match the form submission' );
+ }
+
+ public function test_compute_entry_permalink_with_page_number() {
+ $current_post = $this->create_post_context();
+ $form_id = Utility::get_form_id();
+ // Create a form submission
+ $post_data = Utility::get_post_request(
+ array(
+ 'email' => 'email@email.com',
+ ),
+ 'g' . $form_id
+ );
+
+ $form = new Contact_Form(
+ array(
+ 'title' => 'Test Form',
+ 'description' => 'This is a test form.',
+ ),
+ "[contact-field label='Email' type='email' required='1'/]"
+ );
+
+ $response = Form_Response::from_submission( $post_data, $form, $current_post, 999 );
+ $post_id = $response->save();
+
+ $post_response = Form_Response::get( $post_id );
+ $this->destroy_post_context();
+
+ $this->assertStringContainsString( 'page=999', $response->get_entry_permalink(), 'Post permalink should match the form submission' );
+ $this->assertStringContainsString( 'page=999', $post_response->get_entry_permalink(), 'Post permalink should match the saved form submission' );
+ }
+}
diff --git a/projects/packages/forms/tests/php/contact-form/Response_Field_Test.php b/projects/packages/forms/tests/php/contact-form/Response_Field_Test.php
new file mode 100644
index 0000000000000..4f54f903f2cc0
--- /dev/null
+++ b/projects/packages/forms/tests/php/contact-form/Response_Field_Test.php
@@ -0,0 +1,110 @@
+assertInstanceOf( Response_Field::class, $field );
+
+ $this->assertEquals( 'test_key', $field->get_key() );
+ $this->assertEquals( 'test_label', $field->get_label() );
+ $this->assertEquals( 'test_value', $field->get_value() );
+ $this->assertEquals( 'basic', $field->get_type() );
+ $this->assertEquals( array(), $field->get_meta() );
+ }
+
+ /**
+ * Test that the Response_Field class can be instantiated with additional parameters.
+ */
+ public function test_response_field_with_additional_parameters() {
+ $field = new Response_Field( 'test_key', 'test_label', 'test_value', 'text', array( 'meta_key' => 'meta_value' ) );
+ $this->assertEquals( 'test_key', $field->get_key() );
+ $this->assertEquals( 'test_label', $field->get_label() );
+ $this->assertEquals( 'test_value', $field->get_value() );
+ $this->assertEquals( 'text', $field->get_type() );
+ $this->assertEquals( array( 'meta_key' => 'meta_value' ), $field->get_meta() );
+ }
+
+ /**
+ * Test that the Response_Field class can handle empty values.
+ */
+ public function test_response_field_with_empty_values() {
+ $field = new Response_Field( 'test_key', '', '' );
+ $this->assertEquals( 'test_key', $field->get_key() );
+ $this->assertSame( '', $field->get_label() );
+ $this->assertSame( '', $field->get_value() );
+ $this->assertEquals( 'basic', $field->get_type() );
+ $this->assertEquals( array(), $field->get_meta() );
+ }
+
+ /**
+ * Test that the Response_Field can serealize and unserialize correctly.
+ */
+ public function test_response_field_serialization() {
+ $field = new Response_Field( 'test_key', 'test_label', 'test_value', 'text', array( 'meta_key' => 'meta_value' ) );
+ $serialized = $field->serialize();
+ $unserialized = Response_Field::from_serialized( $serialized );
+
+ $this->assertInstanceOf( Response_Field::class, $unserialized );
+
+ $this->assertEquals( $serialized, $unserialized->serialize() );
+ $this->assertEquals( 'test_key', $unserialized->get_key() );
+ $this->assertEquals( 'test_label', $unserialized->get_label() );
+ $this->assertEquals( 'test_value', $unserialized->get_value() );
+ $this->assertEquals( 'text', $unserialized->get_type() );
+ $this->assertEquals( array( 'meta_key' => 'meta_value' ), $unserialized->get_meta() );
+ }
+
+ /**
+ * Test that the Response_Field can serealize and unserialize correctly.
+ */
+ public function test_response_from_serialized_is_null() {
+
+ $unserialized = Response_Field::from_serialized( array( 'key' => 'test_key' ) );
+
+ $this->assertNull( $unserialized );
+
+ $unserialized = Response_Field::from_serialized( 'howdy' );
+
+ $this->assertNull( $unserialized );
+
+ $unserialized = Response_Field::from_serialized( array( 'value' => 'test_value' ) );
+
+ $this->assertNull( $unserialized );
+
+ $unserialized = Response_Field::from_serialized( array( 'label' => 'test_value' ) );
+
+ $this->assertNull( $unserialized );
+
+ $unserialized = Response_Field::from_serialized(
+ array(
+ 'label' => 'test_value',
+ 'value' => 'value',
+ )
+ );
+
+ $this->assertNull( $unserialized );
+ }
+}
diff --git a/projects/packages/forms/tests/php/contact-form/class-utility.php b/projects/packages/forms/tests/php/contact-form/class-utility.php
new file mode 100644
index 0000000000000..3f8653696a058
--- /dev/null
+++ b/projects/packages/forms/tests/php/contact-form/class-utility.php
@@ -0,0 +1,137 @@
+ 'value1',
+ 'field2' => 'value2',
+ 'email_marketing_consent' => 'yes',
+ );
+ }
+ // Ensure all_values is an array and has the necessary keys.
+ $entry_values = array(
+ 'entry_title' => 'Cool Post Title',
+ 'entry_permalink' => 'https://example.com/post/123',
+ 'feedback_id' => $feedback_id,
+ );
+
+ if ( isset( $_POST['page'] ) ) {
+ $entry_values['entry_page'] = absint( wp_unslash( $_POST['page'] ) );
+ }
+
+ if ( ! isset( $all_values['email_marketing_consent'] ) ) {
+ $all_values['email_marketing_consent'] = false;
+ }
+
+ $all_values = array_merge(
+ $all_values,
+ $entry_values
+ );
+
+ $content = addslashes( wp_kses( "$comment_content\n\nAUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$subject}\nIP: {$comment_ip_text}\nJSON_DATA\n" . wp_json_encode( $all_values ), array() ) );
+
+ // Create a mock post with JSON_DATA format
+ return wp_insert_post(
+ array(
+ 'post_date' => addslashes( $feedback_time ),
+ 'post_type' => 'feedback',
+ 'post_status' => addslashes( $status ),
+ 'post_parent' => $post ? $post->ID : 0,
+ 'post_title' => addslashes( wp_kses( $feedback_title, array() ) ),
+ 'post_content' => $content, // so that search will pick up this data
+ 'post_name' => $feedback_id,
+ )
+ );
+ }
+
+ /**
+ * Adds the field values to the global $_POST value.
+ *
+ * @param array $values Array of form fields and values.
+ * @param string $form_id Optional form ID. If not provided, will use $post_id.
+ */
+ public static function add_post_request( $values, $form_id = null, $post_id = 0 ) {
+ $post_data = self::get_post_request( $values, $form_id, $post_id );
+ foreach ( $post_data as $key => $value ) {
+ $_POST[ $key ] = $value;
+ }
+ }
+
+ public static function get_form_id( $attributes = array() ) {
+ global $post, $page;
+
+ $count = Contact_Form::get_forms_count();
+
+ if ( ! empty( $attributes['widget'] ) && $attributes['widget'] ) {
+ $attributes['id'] = 'widget-' . $attributes['widget'];
+ } elseif ( ! empty( $attributes['block_template'] ) && $attributes['block_template'] ) {
+
+ $attributes['id'] = 'block-template-' . $attributes['block_template'];
+ } elseif ( ! empty( $attributes['block_template_part'] ) && $attributes['block_template_part'] ) {
+ $attributes['id'] = 'block-template-part-' . $attributes['block_template_part'];
+ } elseif ( $post ) {
+ $attributes['id'] = $post->ID;
+ }
+
+ if ( $count ) {
+ // Ensure 'id' exists in $attributes before trying to modify it
+ if ( ! isset( $attributes['id'] ) ) {
+ $attributes['id'] = '';
+ }
+
+ // When submitting the page number is not always set, so we need to handle that: TODO: This is a hack, we need to find a better way to handle form identification
+ $page_num = max( 1, intval( $page ) );
+
+ $attributes['id'] = $attributes['id'] . '-' . ( $count + 1 ) . '-' . $page_num;
+ }
+ return $attributes['id'];
+ }
+
+ public static function get_post_request( $values, $form_id = null, $post_id = 0 ) {
+ $prefix = $form_id ? $form_id : 'g' . $post_id;
+ $post_data = array();
+ foreach ( $values as $key => $val ) {
+ if ( strpos( $key, 'contact-form' ) === 0 || strpos( $key, 'action' ) === 0 ) {
+ $post_data[ $key ] = $val;
+ } else {
+ $post_data[ $prefix . '-' . $key ] = $val;
+ }
+ }
+ return $post_data;
+ }
+}
From d45083f4a212eb9657ce7d7f921ab36896a51a55 Mon Sep 17 00:00:00 2001
From: Enej Bajgoric
Date: Tue, 8 Jul 2025 15:16:44 -0700
Subject: [PATCH 02/48] Add tests for Form_Response::strip_tags method
---
.../php/contact-form/Form_Response_Test.php | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php b/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
index 7a18da98fd803..51aeffee66d64 100644
--- a/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
+++ b/projects/packages/forms/tests/php/contact-form/Form_Response_Test.php
@@ -1127,4 +1127,156 @@ public function test_compute_entry_permalink_with_page_number() {
$this->assertStringContainsString( 'page=999', $response->get_entry_permalink(), 'Post permalink should match the form submission' );
$this->assertStringContainsString( 'page=999', $post_response->get_entry_permalink(), 'Post permalink should match the saved form submission' );
}
+ /**
+ * Test that strip_tags handles simple string without HTML tags.
+ */
+ public function test_strip_tags_with_plain_string() {
+ $input = 'Hello, this is a plain text string.';
+ $expected = 'Hello, this is a plain text string.';
+ $result = Form_Response::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags removes script tags but keeps the content.
+ */
+ public function test_strip_tags_removes_script_tags() {
+ $input = 'Hello world!';
+ $expected = 'alert("XSS")Hello world!';
+ $result = Form_Response::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags handles HTML entities correctly.
+ */
+ public function test_strip_tags_handles_html_entities() {
+ $input = 'Hello & goodbye';
+ $expected = 'Hello & goodbye';
+ $result = Form_Response::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags handles arrays recursively.
+ */
+ public function test_strip_tags_handles_arrays() {
+ $input = array(
+ 'field1' => 'Hello world',
+ 'field2' => array(
+ 'nested' => 'Test bold text',
+ 'deep' => array(
+ 'deeper' => 'More & testing',
+ ),
+ ),
+ );
+
+ $expected = array(
+ 'field1' => 'Hello alert("XSS")world',
+ 'field2' => array(
+ 'nested' => 'Test bold text',
+ 'deep' => array(
+ 'deeper' => 'More & testing',
+ ),
+ ),
+ );
+
+ $result = Form_Response::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags sanitizes array keys.
+ */
+ public function test_strip_tags_sanitizes_array_keys() {
+ $input = array(
+ '' => 'value1',
+ 'normal_key' => 'value2',
+ );
+
+ $result = Form_Response::strip_tags( $input );
+
+ // Check that the results exist and are correct
+ // We need to check what key actually gets created after sanitization
+ $keys = array_keys( $result );
+ $this->assertCount( 2, $keys );
+ $this->assertEquals( 'value2', $result['normal_key'] );
+
+ // Find the sanitized key (should be the one that's not 'normal_key')
+ $sanitized_key = null;
+ foreach ( $keys as $key ) {
+ if ( $key !== 'normal_key' ) {
+ $sanitized_key = $key;
+ break;
+ }
+ }
+ $this->assertEquals( 'value1', $result[ $sanitized_key ] );
+ }
+
+ /**
+ * Test that strip_tags handles empty values.
+ */
+ public function test_strip_tags_handles_empty_values() {
+ $this->assertSame( '', Form_Response::strip_tags( '' ) );
+ $this->assertEquals( array(), Form_Response::strip_tags( array() ) );
+ $this->assertSame( '0', Form_Response::strip_tags( 0 ) );
+ $this->assertSame( '', Form_Response::strip_tags( null ) );
+ }
+
+ /**
+ * Test that strip_tags handles numeric values.
+ */
+ public function test_strip_tags_handles_numeric_values() {
+ $this->assertSame( '123', Form_Response::strip_tags( 123 ) );
+ $this->assertSame( '123.45', Form_Response::strip_tags( 123.45 ) );
+ }
+
+ /**
+ * Test that strip_tags preserves allowed HTML tags as per wp_kses_post.
+ */
+ public function test_strip_tags_preserves_allowed_html() {
+ $input = 'This is a test with emphasis and links.
';
+ $result = Form_Response::strip_tags( $input );
+
+ // wp_kses_post should preserve these tags - let's just verify we get a non-empty string with HTML
+ $this->assertNotEquals( '', $result );
+ $this->assertStringContainsString( 'This is a', $result );
+ $this->assertStringContainsString( 'test', $result );
+ }
+
+ /**
+ * Test that strip_tags removes dangerous HTML tags.
+ */
+ public function test_strip_tags_removes_dangerous_html() {
+ $input = 'Hello world';
+ $result = Form_Response::strip_tags( $input );
+
+ // These dangerous tags should be removed, but content might remain
+ $this->assertStringNotContainsString( 'Hello world!';
$expected = 'alert("XSS")Hello world!';
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
$this->assertEquals( $expected, $result );
}
@@ -1221,7 +1221,7 @@ public function test_strip_tags_removes_script_tags() {
public function test_strip_tags_handles_html_entities() {
$input = 'Hello & goodbye';
$expected = 'Hello & goodbye';
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
$this->assertEquals( $expected, $result );
}
@@ -1249,7 +1249,7 @@ public function test_strip_tags_handles_arrays() {
),
);
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
$this->assertEquals( $expected, $result );
}
@@ -1262,7 +1262,7 @@ public function test_strip_tags_sanitizes_array_keys() {
'normal_key' => 'value2',
);
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
// Check that the results exist and are correct
// We need to check what key actually gets created after sanitization
@@ -1285,18 +1285,18 @@ public function test_strip_tags_sanitizes_array_keys() {
* Test that strip_tags handles empty values.
*/
public function test_strip_tags_handles_empty_values() {
- $this->assertSame( '', Form_Response::strip_tags( '' ) );
- $this->assertEquals( array(), Form_Response::strip_tags( array() ) );
- $this->assertSame( '0', Form_Response::strip_tags( 0 ) );
- $this->assertSame( '', Form_Response::strip_tags( null ) );
+ $this->assertSame( '', Feedback::strip_tags( '' ) );
+ $this->assertEquals( array(), Feedback::strip_tags( array() ) );
+ $this->assertSame( '0', Feedback::strip_tags( 0 ) );
+ $this->assertSame( '', Feedback::strip_tags( null ) );
}
/**
* Test that strip_tags handles numeric values.
*/
public function test_strip_tags_handles_numeric_values() {
- $this->assertSame( '123', Form_Response::strip_tags( 123 ) );
- $this->assertSame( '123.45', Form_Response::strip_tags( 123.45 ) );
+ $this->assertSame( '123', Feedback::strip_tags( 123 ) );
+ $this->assertSame( '123.45', Feedback::strip_tags( 123.45 ) );
}
/**
@@ -1304,7 +1304,7 @@ public function test_strip_tags_handles_numeric_values() {
*/
public function test_strip_tags_preserves_allowed_html() {
$input = 'This is a test with emphasis and links.
';
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
// wp_kses_post should preserve these tags - let's just verify we get a non-empty string with HTML
$this->assertNotEquals( '', $result );
@@ -1317,7 +1317,7 @@ public function test_strip_tags_preserves_allowed_html() {
*/
public function test_strip_tags_removes_dangerous_html() {
$input = 'Hello world';
- $result = Form_Response::strip_tags( $input );
+ $result = Feedback::strip_tags( $input );
// These dangerous tags should be removed, but content might remain
$this->assertStringNotContainsString( 'Hello world!';
+ $expected = 'alert("XSS")Hello world!';
+ $result = Contact_Form_Plugin::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags handles HTML entities correctly.
+ */
+ public function test_strip_tags_handles_html_entities() {
+ $input = 'Hello & goodbye';
+ $expected = 'Hello & goodbye';
+ $result = Contact_Form_Plugin::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags handles arrays recursively.
+ */
+ public function test_strip_tags_handles_arrays() {
+ $input = array(
+ 'field1' => 'Hello world',
+ 'field2' => array(
+ 'nested' => 'Test bold text',
+ 'deep' => array(
+ 'deeper' => 'More & testing',
+ ),
+ ),
+ );
+
+ $expected = array(
+ 'field1' => 'Hello alert("XSS")world',
+ 'field2' => array(
+ 'nested' => 'Test bold text',
+ 'deep' => array(
+ 'deeper' => 'More & testing',
+ ),
+ ),
+ );
+
+ $result = Contact_Form_Plugin::strip_tags( $input );
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * Test that strip_tags sanitizes array keys.
+ */
+ public function test_strip_tags_sanitizes_array_keys() {
+ $input = array(
+ '' => 'value1',
+ 'normal_key' => 'value2',
+ );
+
+ $result = Contact_Form_Plugin::strip_tags( $input );
+
+ // Check that the results exist and are correct
+ // We need to check what key actually gets created after sanitization
+ $keys = array_keys( $result );
+ $this->assertCount( 2, $keys );
+ $this->assertEquals( 'value2', $result['normal_key'] );
+
+ // Find the sanitized key (should be the one that's not 'normal_key')
+ $sanitized_key = null;
+ foreach ( $keys as $key ) {
+ if ( $key !== 'normal_key' ) {
+ $sanitized_key = $key;
+ break;
+ }
+ }
+ $this->assertEquals( 'value1', $result[ $sanitized_key ] );
+ }
+
+ /**
+ * Test that strip_tags handles empty values.
+ */
+ public function test_strip_tags_handles_empty_values() {
+ $this->assertSame( '', Contact_Form_Plugin::strip_tags( '' ) );
+ $this->assertEquals( array(), Contact_Form_Plugin::strip_tags( array() ) );
+ $this->assertSame( '0', Contact_Form_Plugin::strip_tags( 0 ) );
+ $this->assertSame( '', Contact_Form_Plugin::strip_tags( null ) );
+ }
+
+ /**
+ * Test that strip_tags handles numeric values.
+ */
+ public function test_strip_tags_handles_numeric_values() {
+ $this->assertSame( '123', Contact_Form_Plugin::strip_tags( 123 ) );
+ $this->assertSame( '123.45', Contact_Form_Plugin::strip_tags( 123.45 ) );
+ }
+
+ /**
+ * Test that strip_tags preserves allowed HTML tags as per wp_kses_post.
+ */
+ public function test_strip_tags_preserves_allowed_html() {
+ $input = 'This is a test with emphasis and links.
';
+ $result = Contact_Form_Plugin::strip_tags( $input );
+
+ // wp_kses_post should preserve these tags - let's just verify we get a non-empty string with HTML
+ $this->assertNotEquals( '', $result );
+ $this->assertStringContainsString( 'This is a', $result );
+ $this->assertStringContainsString( 'test', $result );
+ }
+
+ /**
+ * Test that strip_tags removes dangerous HTML tags.
+ */
+ public function test_strip_tags_removes_dangerous_html() {
+ $input = 'Hello world';
+ $result = Contact_Form_Plugin::strip_tags( $input );
+
+ // These dangerous tags should be removed, but content might remain
+ $this->assertStringNotContainsString( 'Hello world!';
- $expected = 'alert("XSS")Hello world!';
- $result = Feedback::strip_tags( $input );
- $this->assertEquals( $expected, $result );
- }
-
- /**
- * Test that strip_tags handles HTML entities correctly.
- */
- public function test_strip_tags_handles_html_entities() {
- $input = 'Hello & goodbye';
- $expected = 'Hello & goodbye';
- $result = Feedback::strip_tags( $input );
- $this->assertEquals( $expected, $result );
- }
-
- /**
- * Test that strip_tags handles arrays recursively.
- */
- public function test_strip_tags_handles_arrays() {
- $input = array(
- 'field1' => 'Hello world',
- 'field2' => array(
- 'nested' => 'Test bold text',
- 'deep' => array(
- 'deeper' => 'More & testing',
- ),
- ),
- );
-
- $expected = array(
- 'field1' => 'Hello alert("XSS")world',
- 'field2' => array(
- 'nested' => 'Test bold text',
- 'deep' => array(
- 'deeper' => 'More & testing',
- ),
- ),
- );
-
- $result = Feedback::strip_tags( $input );
- $this->assertEquals( $expected, $result );
- }
-
- /**
- * Test that strip_tags sanitizes array keys.
- */
- public function test_strip_tags_sanitizes_array_keys() {
- $input = array(
- '' => 'value1',
- 'normal_key' => 'value2',
- );
-
- $result = Feedback::strip_tags( $input );
-
- // Check that the results exist and are correct
- // We need to check what key actually gets created after sanitization
- $keys = array_keys( $result );
- $this->assertCount( 2, $keys );
- $this->assertEquals( 'value2', $result['normal_key'] );
-
- // Find the sanitized key (should be the one that's not 'normal_key')
- $sanitized_key = null;
- foreach ( $keys as $key ) {
- if ( $key !== 'normal_key' ) {
- $sanitized_key = $key;
- break;
- }
- }
- $this->assertEquals( 'value1', $result[ $sanitized_key ] );
- }
-
- /**
- * Test that strip_tags handles empty values.
- */
- public function test_strip_tags_handles_empty_values() {
- $this->assertSame( '', Feedback::strip_tags( '' ) );
- $this->assertEquals( array(), Feedback::strip_tags( array() ) );
- $this->assertSame( '0', Feedback::strip_tags( 0 ) );
- $this->assertSame( '', Feedback::strip_tags( null ) );
- }
-
- /**
- * Test that strip_tags handles numeric values.
- */
- public function test_strip_tags_handles_numeric_values() {
- $this->assertSame( '123', Feedback::strip_tags( 123 ) );
- $this->assertSame( '123.45', Feedback::strip_tags( 123.45 ) );
- }
-
- /**
- * Test that strip_tags preserves allowed HTML tags as per wp_kses_post.
- */
- public function test_strip_tags_preserves_allowed_html() {
- $input = 'This is a test with emphasis and links.
';
- $result = Feedback::strip_tags( $input );
-
- // wp_kses_post should preserve these tags - let's just verify we get a non-empty string with HTML
- $this->assertNotEquals( '', $result );
- $this->assertStringContainsString( 'This is a', $result );
- $this->assertStringContainsString( 'test', $result );
- }
-
- /**
- * Test that strip_tags removes dangerous HTML tags.
- */
- public function test_strip_tags_removes_dangerous_html() {
- $input = 'Hello world';
- $result = Feedback::strip_tags( $input );
-
- // These dangerous tags should be removed, but content might remain
- $this->assertStringNotContainsString( '