diff --git a/projects/packages/forms/changelog/add-forms-hidden-fields b/projects/packages/forms/changelog/add-forms-hidden-fields
new file mode 100644
index 0000000000000..669bfc8adcbe5
--- /dev/null
+++ b/projects/packages/forms/changelog/add-forms-hidden-fields
@@ -0,0 +1,4 @@
+Significance: minor
+Type: added
+
+Forms: add handling for hidden fields
diff --git a/projects/packages/forms/src/blocks/contact-form/attributes.js b/projects/packages/forms/src/blocks/contact-form/attributes.js
index d7290106be40e..3ad1fe5a51c9e 100644
--- a/projects/packages/forms/src/blocks/contact-form/attributes.js
+++ b/projects/packages/forms/src/blocks/contact-form/attributes.js
@@ -42,4 +42,8 @@ export default {
sendToSalesforce: false,
},
},
+ hiddenFields: {
+ type: 'array',
+ default: [],
+ },
};
diff --git a/projects/packages/forms/src/blocks/contact-form/edit.js b/projects/packages/forms/src/blocks/contact-form/edit.js
index 1dc222dacf1da..626962b1b838b 100644
--- a/projects/packages/forms/src/blocks/contact-form/edit.js
+++ b/projects/packages/forms/src/blocks/contact-form/edit.js
@@ -29,7 +29,7 @@ import { withDispatch, withSelect } from '@wordpress/data';
import { forwardRef, Fragment, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import clsx from 'clsx';
-import { filter, get, isArray, map } from 'lodash';
+import { filter, every, get, isArray, map, remove } from 'lodash';
import { childBlocks } from './child-blocks';
import InspectorHint from './components/inspector-hint';
import { ContactFormPlaceholder } from './components/jetpack-contact-form-placeholder';
@@ -101,6 +101,7 @@ export const JetpackContactFormEdit = forwardRef(
customThankyouRedirect,
jetpackCRM,
salesforceData,
+ hiddenFields,
} = attributes;
const [ isPatternsModalOpen, setIsPatternsModalOpen ] = useState( false );
@@ -159,6 +160,14 @@ export const JetpackContactFormEdit = forwardRef(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );
+ useEffect( () => {
+ if ( ! hiddenFields.length ) {
+ setAttributes( {
+ hiddenFields: [ { uuid: Math.random() * 1000000, name: '', value: '', edit: 'both' } ],
+ } );
+ }
+ } );
+
const renderSubmissionSettings = () => {
return (
<>
@@ -273,6 +282,68 @@ export const JetpackContactFormEdit = forwardRef(
);
};
+ const setHiddenField = ( key, newName, newValue, editMode ) => {
+ const newHiddenFields = map( hiddenFields, ( { uuid, name, value, edit } ) => {
+ const hiddenField = {
+ uuid,
+ name,
+ value,
+ edit,
+ };
+ if ( key === uuid ) {
+ hiddenField.name = editMode === 'both' || editMode === 'name' ? newName : name;
+ hiddenField.value = editMode === 'both' || editMode === 'name' ? newValue : value;
+ }
+ return hiddenField;
+ } );
+
+ remove( newHiddenFields, hf => ! hf.name.trim() && ! hf.value.trim() );
+
+ // if all hidden fields have some value, add an empty one at the end
+ every( newHiddenFields, 'value' ) &&
+ newHiddenFields.push( {
+ uuid: Math.random() * 1000000,
+ name: '',
+ value: '',
+ edit: 'both',
+ } );
+
+ setAttributes( {
+ hiddenFields: newHiddenFields,
+ } );
+ };
+
+ const HiddenFieldInspector = ( props, setter ) => {
+ const { uuid, name, value, edit = 'both' } = props;
+ return (
+
+ { ( edit === 'both' || edit === 'name' ) && (
+ setter( uuid, fieldName, value, edit ) }
+ />
+ ) }
+ { ( ! edit || edit === 'value' || edit === 'none' ) && { name } }
+ { ( edit === 'both' || edit === 'value' ) && (
+ setter( uuid, name, fieldValue, edit ) }
+ />
+ ) }
+ { ( ! edit || edit === 'value' || edit === 'none' ) && { value } }
+
+ );
+ };
+
+ // eslint-disable-next-line no-console
+ console.log( hiddenFields );
+
let elt;
if ( ! isModuleActive ) {
@@ -344,6 +415,17 @@ export const JetpackContactFormEdit = forwardRef(
) }
+
+
+ { __(
+ "Use hidden fields to get fixed data alongside visitor's submissions.",
+ 'jetpack-forms'
+ ) }
+
+ { map( hiddenFields, ( { uuid, name, value, edit } ) => {
+ return HiddenFieldInspector( { uuid, name, value, edit }, setHiddenField );
+ } ) }
+
diff --git a/projects/packages/forms/src/blocks/contact-form/editor.scss b/projects/packages/forms/src/blocks/contact-form/editor.scss
index c1704fffc8281..6df94a7841563 100644
--- a/projects/packages/forms/src/blocks/contact-form/editor.scss
+++ b/projects/packages/forms/src/blocks/contact-form/editor.scss
@@ -458,7 +458,7 @@
margin: 0 5px 0 0;
}
}
-
+
.jetpack-field-option.field-option-checkbox,
.jetpack-field-option.field-option-radio,
.wp-block-jetpack-field-option-checkbox,
@@ -970,3 +970,9 @@
.block-editor-block-inspector .components-base-control .components-base-control:last-child {
margin-bottom: 0;
}
+
+.jetpack-contact-form__hidden-fields-panel {
+ & .components-base-control {
+ margin-bottom: 0;
+ }
+}
diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php
index 837f89c7a7bfd..0d864012a8dac 100644
--- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php
+++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php
@@ -1165,6 +1165,20 @@ public function get_post_meta_for_csv_export( $post_id, $has_json_data = false )
$md['-6_source'] .= '?' . $parsed['query'];
}
}
+
+ // add any hidden (not private) field to the array
+ $non_exportable_fields = array( 'email_marketing_consent', 'entry_title', 'entry_permalink', 'feedback_id' );
+ foreach ( $all_fields as $field_name => $field_value ) {
+ if ( in_array( $field_name, $non_exportable_fields, true ) ) {
+ continue;
+ }
+
+ if ( ! preg_match( '/^(\d{1,2}_)/', $field_name ) ) {
+ $md[ '85_' . $field_name ] = $field_value;
+ } else {
+ $md[ $field_name ] = $field_value;
+ }
+ }
}
// flatten and decode all values.
@@ -1539,7 +1553,7 @@ public function get_export_data_for_posts( $post_ids ) {
* Fetch post main data, because we need the subject and author data for the feedback form.
*/
$post_real_data = $this->get_parsed_field_contents_of_post( $post_id );
-
+ error_log( 'post_real_data ' . print_r( $post_real_data, true ) );
/**
* Whether the feedback post has JSON data or not.
* This is used as optional parameter on legacy functions.
@@ -1567,12 +1581,12 @@ public function get_export_data_for_posts( $post_ids ) {
* Map parsed fields to proper field names
*/
$mapped_fields = $this->map_parsed_field_contents_of_post_to_field_names( $post_real_data, ! $post_has_json_data );
-
+ error_log( 'mapped_fields ' . print_r( $mapped_fields, true ) );
/**
* Fetch post meta data.
*/
$post_meta_data = $this->get_post_meta_for_csv_export( $post_id, $post_has_json_data );
-
+ error_log( 'post_meta_data ' . print_r( $post_meta_data, true ) );
/**
* If `$post_meta_data` is not an array or if it is empty, then there is no
* extra feedback to work with. Create an empty array.
@@ -1599,7 +1613,7 @@ public function get_export_data_for_posts( $post_ids ) {
*/
$field_names = array_merge( $field_names, array_keys( $post_meta_data ) );
}
-
+ error_log( 'field_names ' . print_r( $field_names, true ) );
/**
* Make sure the field names are unique, because we don't want duplicate data.
*/
@@ -1609,7 +1623,7 @@ public function get_export_data_for_posts( $post_ids ) {
* Sort the field names by the field id number
*/
sort( $field_names, SORT_NUMERIC );
-
+ error_log( 'field_names AFTER UNIQUE AND SORT ' . print_r( $field_names, true ) );
$well_known_column_names = $this->get_well_known_column_names();
$result = array();
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 5c54ec765b4bf..6cda886e1529b 100644
--- a/projects/packages/forms/src/contact-form/class-contact-form.php
+++ b/projects/packages/forms/src/contact-form/class-contact-form.php
@@ -179,6 +179,9 @@ public function __construct( $attributes, $content = null ) {
// $this->body and $this->fields have been setup. We no longer need the contact-field shortcode.
Contact_Form_Plugin::$using_contact_form_field = false;
+
+ // this filter is only to be used on submit, so the values can be extracted and saved on the feedback
+ add_filter( 'jetpack_contact_form_hidden_fields', array( $this, 'hidden_fields_filter' ) );
}
/**
@@ -1351,6 +1354,12 @@ public function process_submission() {
$extra_values[ $ev_key ] = Contact_Form_Plugin::strip_tags( $ev_value );
}
+ /**
+ * HIDDEN FIELDS
+ * Use jetpack_contact_form_hidden_fields filter
+ */
+ $all_values = apply_filters( 'jetpack_contact_form_hidden_fields', $all_values );
+
/*
* We need to make sure that the post author is always zero for contact
* form submissions. This prevents export/import from trying to create
@@ -1803,4 +1812,58 @@ public static function get_block_alignment_class( $attributes = array() ) {
}
return $align_to_class_map[ $attributes['align'] ];
}
+
+ /**
+ * Extract hidden fields from contact-form blocks
+ */
+ public function hidden_fields_filter( $fields ) {
+ $content = get_the_content();
+ if ( ! has_block( 'jetpack/contact-form', $content ) ) {
+ return $fields;
+ }
+
+ $form_blocks = $this->get_jetpack_form_blocks_with_hidden_fields( parse_blocks( $content ) );
+
+ // If there's more than one form block on the page we can't tell
+ // which one is being submitted, abort.
+ // TODO: solve it or inform the user about this limitation!
+ if ( count( $form_blocks ) !== 1 ) {
+ return $fields;
+ }
+
+ $hidden_fields = array();
+
+ foreach ( $form_blocks[0]['attrs']['hiddenFields'] as $hidden_field ) {
+ if ( empty( $hidden_field['name'] ) ) {
+ continue;
+ }
+
+ // we could even be more strict here, like a preg_replace for non [a-zA-Z0-9]_- characters on field names
+ $hidden_fields[ Contact_Form_Plugin::strip_tags( $hidden_field['name'] ) ] = Contact_Form_Plugin::strip_tags( $hidden_field['value'] );
+ }
+
+ // Original/non-hidden fields have priority, shouldn't overwrite with hidden fields.
+ return $fields + $hidden_fields;
+ }
+
+ /**
+ * Filter a blocks array in search of jetpack/contact-form blocks.
+ */
+ public function get_jetpack_form_blocks_with_hidden_fields( $block_array ) {
+ $form_blocks = array();
+
+ foreach ( $block_array as $block ) {
+ if (
+ $block['blockName'] === 'jetpack/contact-form' &&
+ isset( $block['attrs']['hiddenFields'] ) &&
+ ! empty( $block['attrs']['hiddenFields'] )
+ ) {
+ $form_blocks[] = $block;
+ } elseif ( isset( $block['innerBlocks'] ) ) {
+ $form_blocks = array_merge( $form_blocks, $this->get_jetpack_form_blocks_with_hidden_fields( $block['innerBlocks'] ) );
+ }
+ }
+
+ return $form_blocks;
+ }
}
diff --git a/projects/plugins/jetpack/changelog/add-forms-hidden-fields b/projects/plugins/jetpack/changelog/add-forms-hidden-fields
new file mode 100644
index 0000000000000..bdfb66c817e25
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/add-forms-hidden-fields
@@ -0,0 +1,4 @@
+Significance: major
+Type: major
+
+Use fields as stored on entry (post type feedback). Breaks normalization. Allows named fields. Honors field order as in editor/frontend.