From d073ee95c47a1e82e04bc7e089b4effe14a8544e Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Mon, 25 Nov 2024 11:58:23 -0300 Subject: [PATCH] add hidden fields PoC/draft --- .../forms/changelog/add-forms-hidden-fields | 4 + .../src/blocks/contact-form/attributes.js | 4 + .../forms/src/blocks/contact-form/edit.js | 84 ++++++++++++++++++- .../forms/src/blocks/contact-form/editor.scss | 8 +- .../class-contact-form-plugin.php | 24 ++++-- .../src/contact-form/class-contact-form.php | 63 ++++++++++++++ .../jetpack/changelog/add-forms-hidden-fields | 4 + 7 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 projects/packages/forms/changelog/add-forms-hidden-fields create mode 100644 projects/plugins/jetpack/changelog/add-forms-hidden-fields 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.