Skip to content

Commit

Permalink
add hidden fields PoC/draft
Browse files Browse the repository at this point in the history
  • Loading branch information
CGastrell committed Nov 25, 2024
1 parent 1352759 commit d073ee9
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 7 deletions.
4 changes: 4 additions & 0 deletions projects/packages/forms/changelog/add-forms-hidden-fields
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Forms: add handling for hidden fields
4 changes: 4 additions & 0 deletions projects/packages/forms/src/blocks/contact-form/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ export default {
sendToSalesforce: false,
},
},
hiddenFields: {
type: 'array',
default: [],
},
};
84 changes: 83 additions & 1 deletion projects/packages/forms/src/blocks/contact-form/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -101,6 +101,7 @@ export const JetpackContactFormEdit = forwardRef(
customThankyouRedirect,
jetpackCRM,
salesforceData,
hiddenFields,
} = attributes;
const [ isPatternsModalOpen, setIsPatternsModalOpen ] = useState( false );

Expand Down Expand Up @@ -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' } ],

Check failure

Code scanning / CodeQL

Insecure randomness High

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
} );
}
} );

const renderSubmissionSettings = () => {
return (
<>
Expand Down Expand Up @@ -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,

Check failure

Code scanning / CodeQL

Insecure randomness High

This uses a cryptographically insecure random number generated at
Math.random()
in a security context.
name: '',
value: '',
edit: 'both',
} );

setAttributes( {
hiddenFields: newHiddenFields,
} );
};

const HiddenFieldInspector = ( props, setter ) => {
const { uuid, name, value, edit = 'both' } = props;
return (
<div
key={ uuid }
style={ { display: 'flex' } }
className="jetpack-contact-form__hidden-fields-panel"
>
{ ( edit === 'both' || edit === 'name' ) && (
<TextControl
value={ name }
placeholder={ __( 'Field name', 'jetpack-forms' ) }
onChange={ fieldName => setter( uuid, fieldName, value, edit ) }
/>
) }
{ ( ! edit || edit === 'value' || edit === 'none' ) && <span>{ name }</span> }
{ ( edit === 'both' || edit === 'value' ) && (
<TextControl
value={ value }
placeholder={ __( 'Field value', 'jetpack-forms' ) }
onChange={ fieldValue => setter( uuid, name, fieldValue, edit ) }
/>
) }
{ ( ! edit || edit === 'value' || edit === 'none' ) && <span>{ value }</span> }
</div>
);
};

// eslint-disable-next-line no-console
console.log( hiddenFields );

let elt;

if ( ! isModuleActive ) {
Expand Down Expand Up @@ -344,6 +415,17 @@ export const JetpackContactFormEdit = forwardRef(
</PanelBody>
</Fragment>
) }
<PanelBody title={ __( 'Hidden Fields', 'jetpack-forms' ) }>
<InspectorHint>
{ __(
"Use hidden fields to get fixed data alongside visitor's submissions.",
'jetpack-forms'
) }
</InspectorHint>
{ map( hiddenFields, ( { uuid, name, value, edit } ) => {
return HiddenFieldInspector( { uuid, name, value, edit }, setHiddenField );
} ) }
</PanelBody>
</InspectorControls>

<div className={ formClassnames } style={ style } ref={ ref }>
Expand Down
8 changes: 7 additions & 1 deletion projects/packages/forms/src/blocks/contact-form/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 ) );

Check warning on line 1556 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

error_log() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_error_log)

Check warning on line 1556 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

print_r() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_print_r)
/**
* Whether the feedback post has JSON data or not.
* This is used as optional parameter on legacy functions.
Expand Down Expand Up @@ -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 ) );

Check warning on line 1584 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

error_log() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_error_log)

Check warning on line 1584 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

print_r() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_print_r)
/**
* 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 ) );

Check warning on line 1589 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

error_log() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_error_log)

Check warning on line 1589 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

print_r() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_print_r)
/**
* 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.
Expand All @@ -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 ) );

Check warning on line 1616 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

error_log() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_error_log)

Check warning on line 1616 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

print_r() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_print_r)
/**
* Make sure the field names are unique, because we don't want duplicate data.
*/
Expand All @@ -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 ) );

Check warning on line 1626 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

error_log() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_error_log)

Check warning on line 1626 in projects/packages/forms/src/contact-form/class-contact-form-plugin.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

print_r() found. Debug code should not normally be used in production. (WordPress.PHP.DevelopmentFunctions.error_log_print_r)
$well_known_column_names = $this->get_well_known_column_names();
$result = array();

Expand Down
63 changes: 63 additions & 0 deletions projects/packages/forms/src/contact-form/class-contact-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) );
}

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1803,4 +1812,58 @@ public static function get_block_alignment_class( $attributes = array() ) {
}
return $align_to_class_map[ $attributes['align'] ];
}

/**

Check failure on line 1816 in projects/packages/forms/src/contact-form/class-contact-form.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

Doc comment for parameter "$fields" missing (Squiz.Commenting.FunctionComment.MissingParamTag)
* 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;
}

/**

Check failure on line 1849 in projects/packages/forms/src/contact-form/class-contact-form.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

Doc comment for parameter "$block_array" missing (Squiz.Commenting.FunctionComment.MissingParamTag)
* 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;
}
}
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/add-forms-hidden-fields
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit d073ee9

Please sign in to comment.