diff --git a/includes/blocks/class-mailchimp-list-subscribe-form-blocks.php b/includes/blocks/class-mailchimp-list-subscribe-form-blocks.php index 45496dca..945863fd 100644 --- a/includes/blocks/class-mailchimp-list-subscribe-form-blocks.php +++ b/includes/blocks/class-mailchimp-list-subscribe-form-blocks.php @@ -78,6 +78,8 @@ public function register_blocks() { 'double_opt_in' => (bool) get_option( 'mc_double_optin', true ), 'merge_fields_visibility' => $merge_fields_visibility, 'interest_groups_visibility' => $interest_groups_visibility, + 'merge_fields' => $merge_fields, + 'interest_groups' => $interest_groups, ); $data = 'window.mailchimp_sf_block_data = ' . wp_json_encode( $data ); wp_add_inline_script( 'mailchimp-mailchimp-editor-script', $data, 'before' ); @@ -209,4 +211,51 @@ public function get_list_data( $request ) { public function get_list_data_permissions_check() { return current_user_can( 'edit_posts' ); } + + /** + * Check if the merge validation should be skipped. + * + * @param array $inner_blocks The inner blocks of the block. + * @param array $merge_fields The merge fields. + * @param string $template The template of the block. + * @return bool True if the merge validation should be skipped, false otherwise. + */ + public function should_skip_merge_validation( $inner_blocks = array(), $merge_fields = array(), $template = 'default' ) { + if ( 'default' === $template ) { + return false; + } + + // Get the tags of the visible inner blocks. + $visible_inner_blocks = array_map( + function ( $block ) { + return $block['attrs']['tag'] ?? ''; + }, + array_filter( + $inner_blocks, + function ( $block ) { + return 'mailchimp/mailchimp-form-field' === $block['blockName'] && isset( $block['attrs']['visible'] ) && $block['attrs']['visible']; + } + ) + ); + + // Get the tags of the required merge fields. + $required_merge_fields = array_map( + function ( $field ) { + return $field['tag'] ?? ''; + }, + array_filter( + $merge_fields, + function ( $field ) { + return $field['required']; + } + ) + ); + + $missing_required_fields = array_diff( $required_merge_fields, $visible_inner_blocks ); + if ( ! empty( $missing_required_fields ) ) { + return true; + } + + return false; + } } diff --git a/includes/blocks/mailchimp-form-field/block.json b/includes/blocks/mailchimp-form-field/block.json index 7707dd8c..7f5e29ff 100644 --- a/includes/blocks/mailchimp-form-field/block.json +++ b/includes/blocks/mailchimp-form-field/block.json @@ -30,7 +30,7 @@ "parent": [ "mailchimp/mailchimp" ], - "usesContext": ["mailchimp/list_id","mailchimp/show_required_indicator"], + "usesContext": ["mailchimp/list_id","mailchimp/show_required_indicator","mailchimp/template"], "editorScript": "file:./index.js", "render": "file:./field-markup.php" } diff --git a/includes/blocks/mailchimp-form-field/edit.js b/includes/blocks/mailchimp-form-field/edit.js index 115eb274..b1dcd9d4 100644 --- a/includes/blocks/mailchimp-form-field/edit.js +++ b/includes/blocks/mailchimp-form-field/edit.js @@ -312,11 +312,13 @@ export const BlockEdit = (props) => { const { attributes, setAttributes, - context: { 'mailchimp/list_id': listId }, + context: { 'mailchimp/list_id': listId, 'mailchimp/template': template }, } = props; const { visible, tag } = attributes; const { mailchimpListData } = window; - const isRequired = mailchimpListData?.[listId]?.mergeFields?.[tag]?.required || false; + const isRequired = + (template === 'default' && mailchimpListData?.[listId]?.mergeFields?.[tag]?.required) || + tag === 'EMAIL'; return (
diff --git a/includes/blocks/mailchimp-form-field/field-markup.php b/includes/blocks/mailchimp-form-field/field-markup.php index 83e589ae..d622ee69 100644 --- a/includes/blocks/mailchimp-form-field/field-markup.php +++ b/includes/blocks/mailchimp-form-field/field-markup.php @@ -7,6 +7,7 @@ $list_id = $block->context['mailchimp/list_id'] ?? ''; $show_required_indicator = $block->context['mailchimp/show_required_indicator'] ?? true; +$template = $block->context['mailchimp/template'] ?? 'default'; $field_tag = $attributes['tag'] ?? ''; $label = $attributes['label'] ?? ''; $is_visible = $attributes['visible'] ?? false; @@ -32,6 +33,11 @@ function ( $field ) use ( $field_tag ) { return; } +// If the template is not default and the field is marked as hidden, don't render the field. +if ( 'default' !== $template && ! $is_visible && 'EMAIL' !== $field_tag ) { + return; +} + ?>
> { return ( @@ -59,6 +60,7 @@ export const BlockEdit = (props) => { unsubscribe_link_text, show_required_indicator = true, required_indicator_text, + template = 'default', } = attributes; const [listData, setListData] = useState({}); @@ -74,7 +76,10 @@ export const BlockEdit = (props) => { ); const exisingTags = innerBlocks.map((block) => block?.attributes?.tag).filter(Boolean); const exisingGroups = innerBlocks.map((block) => block?.attributes?.id).filter(Boolean); - const visibleFieldsCount = innerBlocks.filter((block) => block?.attributes?.visible).length; + const visibleFields = innerBlocks + .filter((block) => block?.attributes?.visible) + .map((block) => block?.attributes?.tag); + const visibleFieldsCount = visibleFields.length; const listOptions = []; // Check if selected list is not in the list of available lists. @@ -116,10 +121,19 @@ export const BlockEdit = (props) => { tag: field.tag, label: field.name, type: field.type, + /** + * Visibility logic: + * 1. If there are visible fields from the previous list, make the field visible if it's visible in the previous list form (Try to keep the same visibility as the previous list form) for the default template also make the field visible if it's required. + * 2. If there are no visible fields from the previous list, make the field visible if it's required or it's public and the visibility setting is on in the global settings. + */ visible: - (field.required || - merge_fields_visibility?.[field.tag] === 'on') && - field.public, + (template === 'default' && field.required) || + (visibleFields.length > 0 && + visibleFields.includes(field.tag)) || + (visibleFields.length === 0 && + (field.required || + (merge_fields_visibility?.[field.tag] === 'on' && + field.public))), }), ) || []; const listGroupsBlocks = @@ -127,9 +141,7 @@ export const BlockEdit = (props) => { createBlock('mailchimp/mailchimp-audience-group', { id: group.id, label: group.title, - visible: - interest_groups_visibility?.[group.id] === 'on' && - group.type !== 'hidden', + visible: false, // Keep the groups hidden by default. }), ) || []; replaceInnerBlocks(clientId, [...listFieldsBlocks, ...listGroupsBlocks], false); @@ -161,19 +173,14 @@ export const BlockEdit = (props) => { tag: field.tag, label: field.name, type: field.type, - visible: - (field.required || - merge_fields_visibility?.[field.tag] === 'on') && - field.public, + visible: template === 'default' && field.required, // Keep newly added fields hidden by default, except for required fields. }), ); const newGroupBlocks = newFormGroups.map((group) => createBlock('mailchimp/mailchimp-audience-group', { id: group.id, label: group.title, - visible: - interest_groups_visibility?.[group.id] === 'on' && - group.type !== 'hidden', + visible: false, // Keep newly added groups hidden by default. }), ); @@ -279,6 +286,11 @@ export const BlockEdit = (props) => { ); } + // Display the variation picker if there are no innerBlocks. + if (innerBlocks.length === 0) { + return ; + } + // Create a template for innerBlocks based on list data and visibility settings. const templateFields = listData?.merge_fields?.map((field) => [ @@ -301,7 +313,7 @@ export const BlockEdit = (props) => { visible: interest_groups_visibility?.[group.id] === 'on' && group.type !== 'hidden', }, ]) || []; - const template = [...templateFields, ...templateGroups]; + const templateBlocks = [...templateFields, ...templateGroups]; return ( <> @@ -343,7 +355,7 @@ export const BlockEdit = (props) => { {show_required_indicator && ( diff --git a/includes/blocks/mailchimp/editor.css b/includes/blocks/mailchimp/editor.css index 31229236..d37c65c8 100644 --- a/includes/blocks/mailchimp/editor.css +++ b/includes/blocks/mailchimp/editor.css @@ -151,3 +151,38 @@ select.mc_select { width:100%; margin-top: 1em; } + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations { + gap: 20px 0; + justify-content: center; + margin: 16px auto; + max-width: 560px; +} + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations>li { + flex: 0; + margin: 0; + max-width: none; + padding: 0 4px; + text-align: center; + width: unset +} + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations .block-editor-block-variation-picker__variation { + margin-left: 4px; + margin-right: 4px; + padding: 22px +} + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations .block-editor-block-variation-picker__variation span.dashicon::before { + font-size: 24px; +} + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations .block-editor-block-variation-picker__variation svg { + height: auto; + width: 24px +} + +.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker .components-placeholder__fieldset .block-editor-block-variation-picker__variations .block-editor-block-variation-picker__variation-label { + margin-right: 0 +} diff --git a/includes/blocks/mailchimp/index.js b/includes/blocks/mailchimp/index.js index 134b4879..bbbdec13 100644 --- a/includes/blocks/mailchimp/index.js +++ b/includes/blocks/mailchimp/index.js @@ -4,9 +4,11 @@ import { InnerBlocks } from '@wordpress/block-editor'; import { BlockEdit } from './edit'; import metadata from './block.json'; import Icon from './icon'; +import { variations } from './variations'; registerBlockType(metadata, { icon: Icon, + variations, transforms: { from: [ { diff --git a/includes/blocks/mailchimp/markup.php b/includes/blocks/mailchimp/markup.php index 843e5da1..79bd4fcc 100644 --- a/includes/blocks/mailchimp/markup.php +++ b/includes/blocks/mailchimp/markup.php @@ -42,19 +42,22 @@ function ( $single_list ) { $header = $attributes['header'] ?? ''; $sub_heading = $attributes['sub_header'] ?? ''; $submit_text = $attributes['submit_text'] ?? __( 'Subscribe', 'mailchimp' ); - $merge_fields = get_option( 'mailchimp_sf_merge_fields_' . $list_id ); + $merge_fields = get_option( 'mailchimp_sf_merge_fields_' . $list_id, array() ); $show_unsubscribe_link = $attributes['show_unsubscribe_link'] ?? get_option( 'mc_use_unsub_link' ) === 'on'; $unsubscribe_link_text = $attributes['unsubscribe_link_text'] ?? __( 'unsubscribe from list', 'mailchimp' ); $update_existing_subscribers = ( $attributes['update_existing_subscribers'] ?? get_option( 'mc_update_existing' ) === 'on' ) ? 'yes' : 'no'; $double_opt_in = ( $attributes['double_opt_in'] ?? get_option( 'mc_double_optin' ) === 'on' ) ? 'yes' : 'no'; $show_required_indicator = $attributes['show_required_indicator'] ?? true; $required_indicator_text = $attributes['required_indicator_text'] ?? __( '* = required field', 'mailchimp' ); + $template = $attributes['template'] ?? 'default'; + $skip_merge_validation = ( new Mailchimp_List_Subscribe_Form_Blocks() )->should_skip_merge_validation( $inner_blocks, $merge_fields, $template ) ? 'yes' : 'no'; $hash = wp_hash( serialize( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize array( - 'list_id' => $list_id, - 'update_existing' => $update_existing_subscribers, - 'double_opt_in' => $double_opt_in, + 'list_id' => $list_id, + 'update_existing' => $update_existing_subscribers, + 'double_opt_in' => $double_opt_in, + 'skip_merge_validation' => $skip_merge_validation, ) ) ); @@ -108,6 +111,7 @@ function ( $single_list ) { + { + const variationName = variation.name; + const variationFields = formFields[variationName] || []; + const formFieldsTags = variation.innerBlocks + .filter((block) => block[0] === 'mailchimp/mailchimp-form-field') + .map((block) => { + const [, attributes] = block; + const { tag } = attributes; + return tag; + }); + + return variationFields.filter((field) => !formFieldsTags.includes(field)); +}; + +const getHiddenRequiredFields = (variation) => { + const variationName = variation.name; + if (variationName === 'default') { + return []; + } + + const requiredFields = merge_fields.filter((field) => field.required).map((field) => field.tag); + const variationFields = formFields[variationName] || []; + return requiredFields + .filter((field) => !variationFields.includes(field)) + .map((field) => formFieldTitles[field] || field); +}; + +export const VariationPicker = ({ name, setAttributes, clientId }) => { + const { blockType, defaultVariation, variations } = useSelect( + (select) => { + const { getBlockVariations, getBlockType, getDefaultBlockVariation } = + select(blocksStore); + + return { + blockType: getBlockType(name), + defaultVariation: getDefaultBlockVariation(name, 'block'), + variations: getBlockVariations(name, 'block'), + }; + }, + [name], + ); + const { replaceInnerBlocks } = useDispatch(blockEditorStore); + const { createNotice, removeNotice } = useDispatch('core/notices'); + const blockProps = useBlockProps(); + + return ( +
+ { + if (nextVariation.attributes) { + setAttributes(nextVariation.attributes); + } + + if (nextVariation.innerBlocks) { + const missingFields = getMissingFields(nextVariation); + const hiddenRequiredFields = getHiddenRequiredFields(nextVariation); + + // Remove any existing notices. + removeNotice('mailchimp-form-template-required-field-notice'); + removeNotice('mailchimp-form-template-field-notice'); + + replaceInnerBlocks( + clientId, + createBlocksFromInnerBlocksTemplate(nextVariation.innerBlocks), + ); + + // Add a notice if there are required fields missing from the selected form template. + if (hiddenRequiredFields.length > 0) { + createNotice( + 'warning', + sprintf( + __( + 'The selected form template is missing some required fields (%s) for Mailchimp, so merge validation will be turned off for this form.', + 'mailchimp', + ), + hiddenRequiredFields.join(', '), + ), + { + id: 'mailchimp-form-template-required-field-notice', + isDismissible: true, + }, + ); + } + + // Add a notice if there are missing fields from the selected form template. + if (missingFields.length > 0) { + createNotice( + 'warning', + sprintf( + _n( + "%s form field is missing from the selected form template. Please create this field in the Mailchimp dashboard, then click the 'Fetch list settings' button on the plugin settings page to update the list and include the missing field.", + "Some form fields are missing from the selected form template: %s. Please create these fields in the Mailchimp dashboard, then click the 'Fetch list settings' button on the plugin settings page to update the list and include the missing fields.", + missingFields.length, + 'mailchimp', + ), + missingFields + .map( + (field) => + `${formFieldTitles[field] || field} (${field})`, + ) + .join(', '), + ), + { + id: 'mailchimp-form-template-field-notice', + isDismissible: true, + }, + ); + } + } + }} + allowSkip + /> +
+ ); +}; diff --git a/includes/blocks/mailchimp/variations.js b/includes/blocks/mailchimp/variations.js new file mode 100644 index 00000000..e9132ea0 --- /dev/null +++ b/includes/blocks/mailchimp/variations.js @@ -0,0 +1,125 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +const { mailchimp_sf_block_data } = window; +const { + merge_fields = [], + interest_groups = [], + merge_fields_visibility = {}, + interest_groups_visibility = {}, +} = mailchimp_sf_block_data; + +/** @typedef {import('@wordpress/blocks').WPBlockVariation} WPBlockVariation */ + +export const formFields = { + 'email-only-form': ['EMAIL'], + 'name-and-email-form': ['FNAME', 'LNAME', 'EMAIL'], + 'contact-form': ['FNAME', 'LNAME', 'EMAIL', 'PHONE', 'ADDRESS'], + default: [], +}; + +export const formFieldTitles = { + FNAME: __('First Name', 'mailchimp'), + LNAME: __('Last Name', 'mailchimp'), + EMAIL: __('Email', 'mailchimp'), + PHONE: __('Phone', 'mailchimp'), + ADDRESS: __('Address', 'mailchimp'), +}; + +const prepareInnerBlocks = (merge_fields = [], template = 'default') => { + const fields = formFields[template] || []; + const fieldInnerBlocks = [...merge_fields] + .sort((a, b) => { + const aIndex = fields.indexOf(a.tag); + const bIndex = fields.indexOf(b.tag); + return (aIndex === -1 ? Infinity : aIndex) - (bIndex === -1 ? Infinity : bIndex); + }) + .map((field) => { + let visible = + (template === 'default' && field.required) || + fields.includes(field.tag) || + fields.includes(field.type?.toUpperCase()); + if (fields.length === 0) { + visible = field.required || merge_fields_visibility?.[field.tag] === 'on'; + } + return [ + 'mailchimp/mailchimp-form-field', + { + tag: field.tag, + label: field.name, + type: field.type, + visible, + }, + ]; + }); + const groupInnerBlocks = interest_groups.map((group) => [ + 'mailchimp/mailchimp-audience-group', + { + id: group.id, + label: group.title, + visible: + template !== 'default' + ? false + : interest_groups_visibility?.[group.id] === 'on' && group.type !== 'hidden', + }, + ]); + return [...fieldInnerBlocks, ...groupInnerBlocks]; +}; + +/** + * Template option choices for predefined columns layouts. + * + * @type {WPBlockVariation[]} + */ +export const variations = [ + { + name: 'email-only-form', + title: __('Quick Signup (Email Only)', 'mailchimp'), + description: __('A quick signup form with only an email field.', 'mailchimp'), + icon: 'email', + attributes: { + template: 'email-only-form', + }, + innerBlocks: prepareInnerBlocks(merge_fields, 'email-only-form'), + scope: ['block'], + }, + { + name: 'name-and-email-form', + title: __('Personal Signup (Name and Email)', 'mailchimp'), + description: __('A personal signup form with only a name and email fields', 'mailchimp'), + icon: 'admin-users', + attributes: { + template: 'name-and-email-form', + }, + innerBlocks: prepareInnerBlocks(merge_fields, 'name-and-email-form'), + scope: ['block'], + }, + { + name: 'contact-form', + title: __('Contact Form (Contact Details)', 'mailchimp'), + description: __( + 'A full contact details form with name, email, phone and address fields', + 'mailchimp', + ), + icon: 'id', + attributes: { + template: 'contact-form', + }, + innerBlocks: prepareInnerBlocks(merge_fields, 'contact-form'), + scope: ['block'], + }, + { + name: 'default', + title: __('Default Form (All Fields)', 'mailchimp'), + description: __('A default form, Fields based on settings.', 'mailchimp'), + icon: 'admin-settings', + attributes: { + template: 'default', + }, + isDefault: true, + innerBlocks: prepareInnerBlocks(merge_fields, 'default'), + scope: ['block'], + }, +]; diff --git a/includes/class-mailchimp-form-submission.php b/includes/class-mailchimp-form-submission.php index 96fd7078..f111c8b6 100644 --- a/includes/class-mailchimp-form-submission.php +++ b/includes/class-mailchimp-form-submission.php @@ -92,24 +92,27 @@ public function handle_form_submission() { return new WP_Error( 'mailchimp-invalid-form', esc_html__( 'Invalid form submission.', 'mailchimp' ) ); } - $list_id = get_option( 'mc_list_id' ); - $update_existing = get_option( 'mc_update_existing' ); - $double_opt_in = get_option( 'mc_double_optin' ); - $merge_fields = get_option( 'mc_merge_vars', array() ); - $interest_groups = get_option( 'mc_interest_groups', array() ); + $list_id = get_option( 'mc_list_id' ); + $update_existing = get_option( 'mc_update_existing' ); + $double_opt_in = get_option( 'mc_double_optin' ); + $skip_merge_validation = false; + $merge_fields = get_option( 'mc_merge_vars', array() ); + $interest_groups = get_option( 'mc_interest_groups', array() ); // Check if request from latest block. if ( isset( $_POST['mailchimp_sf_list_id'] ) ) { - $list_id = isset( $_POST['mailchimp_sf_list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_list_id'] ) ) : ''; - $update_existing = isset( $_POST['mailchimp_sf_update_existing_subscribers'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_update_existing_subscribers'] ) ) : ''; - $double_opt_in = isset( $_POST['mailchimp_sf_double_opt_in'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_double_opt_in'] ) ) : ''; - $hash = isset( $_POST['mailchimp_sf_hash'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_hash'] ) ) : ''; - $expected = wp_hash( + $list_id = isset( $_POST['mailchimp_sf_list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_list_id'] ) ) : ''; + $update_existing = isset( $_POST['mailchimp_sf_update_existing_subscribers'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_update_existing_subscribers'] ) ) : ''; + $double_opt_in = isset( $_POST['mailchimp_sf_double_opt_in'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_double_opt_in'] ) ) : ''; + $skip_merge_validation = isset( $_POST['mailchimp_sf_skip_merge_validation'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_skip_merge_validation'] ) ) : ''; + $hash = isset( $_POST['mailchimp_sf_hash'] ) ? sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_hash'] ) ) : ''; + $expected = wp_hash( serialize( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize array( - 'list_id' => $list_id, - 'update_existing' => $update_existing, - 'double_opt_in' => $double_opt_in, + 'list_id' => $list_id, + 'update_existing' => $update_existing, + 'double_opt_in' => $double_opt_in, + 'skip_merge_validation' => $skip_merge_validation, ) ) ); @@ -119,15 +122,16 @@ public function handle_form_submission() { return new WP_Error( 'mailchimp-invalid-hash', esc_html__( 'Invalid form submission.', 'mailchimp' ) ); } - $update_existing = 'yes' === $update_existing; - $double_opt_in = 'yes' === $double_opt_in; - $merge_fields = get_option( 'mailchimp_sf_merge_fields_' . $list_id, array() ); - $interest_groups = get_option( 'mailchimp_sf_interest_groups_' . $list_id, array() ); + $update_existing = 'yes' === $update_existing; + $double_opt_in = 'yes' === $double_opt_in; + $skip_merge_validation = 'yes' === $skip_merge_validation; + $merge_fields = get_option( 'mailchimp_sf_merge_fields_' . $list_id, array() ); + $interest_groups = get_option( 'mailchimp_sf_interest_groups_' . $list_id, array() ); } // Prepare request body $email = isset( $_POST['mc_mv_EMAIL'] ) ? wp_strip_all_tags( wp_unslash( $_POST['mc_mv_EMAIL'] ) ) : ''; - $merge_fields_body = $this->prepare_merge_fields_body( $merge_fields ); + $merge_fields_body = $this->prepare_merge_fields_body( $merge_fields, $skip_merge_validation ); // Catch errors and fail early. if ( is_wp_error( $merge_fields_body ) ) { @@ -149,11 +153,12 @@ public function handle_form_submission() { $list_id, $email, array( - 'email_type' => $email_type, - 'merge_fields' => $merge_fields_body, - 'interests' => $groups, - 'update_existing' => $update_existing, - 'double_opt_in' => $double_opt_in, + 'email_type' => $email_type, + 'merge_fields' => $merge_fields_body, + 'interests' => $groups, + 'update_existing' => $update_existing, + 'double_opt_in' => $double_opt_in, + 'skip_merge_validation' => $skip_merge_validation, ) ); @@ -176,10 +181,11 @@ public function handle_form_submission() { /** * Prepare the merge fields body for the API request. * - * @param array $merge_fields Merge fields. + * @param array $merge_fields Merge fields. + * @param bool $skip_merge_validation Skip merge validation. * @return stdClass|WP_Error */ - public function prepare_merge_fields_body( $merge_fields ) { + public function prepare_merge_fields_body( $merge_fields, $skip_merge_validation = false ) { // Loop through our merge fields, and if they're empty, but required, then print an error, and mark as failed $merge = new stdClass(); foreach ( $merge_fields as $merge_field ) { @@ -187,7 +193,7 @@ public function prepare_merge_fields_body( $merge_fields ) { $opt = 'mc_mv_' . $tag; // Skip if the field is not required and not submitted. - if ( 'Y' !== $merge_field['required'] && ! isset( $_POST[ $opt ] ) ) { + if ( ( 'Y' !== $merge_field['required'] && ! isset( $_POST[ $opt ] ) ) || $skip_merge_validation ) { continue; } @@ -414,6 +420,11 @@ protected function subscribe_to_list( $list_id, $email, $args ) { return new WP_Error( 'mailchimp-update-existing', $msg ); } + // Add skip merge validation for handle hidden required fields for the form template. + if ( isset( $args['skip_merge_validation'] ) && $args['skip_merge_validation'] ) { + $url .= '?skip_merge_validation=true'; + } + // Prepare request body $request_body = $this->prepare_subscribe_request_body( $email, $status, $args ); $response = $api->post( $url, $request_body, 'PUT', $list_id ); diff --git a/tests/cypress/config.js b/tests/cypress/config.js index ffcfddcb..bc4b5d0e 100644 --- a/tests/cypress/config.js +++ b/tests/cypress/config.js @@ -31,6 +31,7 @@ module.exports = defineConfig({ 'tests/cypress/e2e/submission/**.test.js', 'tests/cypress/e2e/validation/**.test.js', 'tests/cypress/e2e/block.test.js', + 'tests/cypress/e2e/form-templates.test.js', 'tests/cypress/e2e/user-sync.test.js', 'tests/cypress/e2e/logout.test.js', ], diff --git a/tests/cypress/e2e/block.test.js b/tests/cypress/e2e/block.test.js index a492046b..f4d3857d 100644 --- a/tests/cypress/e2e/block.test.js +++ b/tests/cypress/e2e/block.test.js @@ -16,7 +16,13 @@ describe('Block Tests', () => { it('Admin can create a Signup form using Mailchimp block', () => { const postTitle = 'Mailchimp signup form - Block'; const beforeSave = () => { - cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form'); + cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form').then( + (blockId) => { + cy.getBlockEditor() + .find(`#${blockId} .block-editor-block-variation-picker__skip button`) + .click(); + }, + ); cy.wait(500); }; cy.createPost({ title: postTitle, content: '', beforeSave }).then((postBlock) => { @@ -239,6 +245,11 @@ describe('Block Tests', () => { cy.get('#mc_signup_submit').should('exist'); cy.visit(`/wp-admin/post.php?post=${oldBlockPostId}&action=edit`); + cy.getBlockEditor() + .find( + '.wp-block-mailchimp-mailchimp .block-editor-block-variation-picker__skip button', + ) + .click(); const header = '[NEW BLOCK] Subscribe to our newsletter'; cy.getBlockEditor() .find('h2[aria-label="Enter a header (optional)"]') diff --git a/tests/cypress/e2e/form-templates.test.js b/tests/cypress/e2e/form-templates.test.js new file mode 100644 index 00000000..589d7ac9 --- /dev/null +++ b/tests/cypress/e2e/form-templates.test.js @@ -0,0 +1,133 @@ +/* eslint-disable no-undef */ +describe('Form Templates Tests', () => { + before(() => { + cy.login(); + cy.mailchimpLoginIfNotAlreadyLoggedIn(); + cy.toggleMergeFields('check'); + + // Hide all interest groups + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('input[id^="mc_show_interest_groups_"]').uncheck(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + }); + + it('Admin should see the form templates in the block', () => { + const postTitle = 'Mailchimp signup form - Form Templates'; + const beforeSave = () => { + cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form').then( + (blockId) => { + cy.getBlockEditor() + .find(`#${blockId} ul.block-editor-block-variation-picker__variations li`) + .should('have.length', 4); + cy.getBlockEditor() + .find( + `#${blockId} ul li:nth-child(1) span.block-editor-block-variation-picker__variation-label`, + ) + .should('have.text', 'Quick Signup (Email Only)'); + cy.getBlockEditor() + .find( + `#${blockId} ul li:nth-child(2) span.block-editor-block-variation-picker__variation-label`, + ) + .should('have.text', 'Personal Signup (Name and Email)'); + cy.getBlockEditor() + .find( + `#${blockId} ul li:nth-child(3) span.block-editor-block-variation-picker__variation-label`, + ) + .should('have.text', 'Contact Form (Contact Details)'); + cy.getBlockEditor() + .find( + `#${blockId} ul li:nth-child(4) span.block-editor-block-variation-picker__variation-label`, + ) + .should('have.text', 'Default Form (All Fields)'); + }, + ); + cy.wait(500); + }; + cy.createPost({ title: postTitle, content: '', beforeSave }); + }); + + const beforeSave = (variation) => { + cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form').then((blockId) => { + cy.getBlockEditor() + .find( + `#${blockId} ul.block-editor-block-variation-picker__variations li:nth-child(${variation})`, + ) + .click(); + cy.getBlockEditor() + .find('h2[aria-label="Enter a header (optional)"]') + .should('be.visible'); + }); + cy.wait(500); + }; + + it('Admin can select a form template (Quick Signup)', () => { + const postTitle = 'Mailchimp signup form - Form Template 1'; + + cy.createPost({ title: postTitle, content: '', beforeSave: () => beforeSave(1) }).then( + (postBlock) => { + if (postBlock) { + cy.visit(`/?p=${postBlock.id}`); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_mv_FNAME').should('not.exist'); + cy.get('#mc_mv_LNAME').should('not.exist'); + cy.get('#mc_mv_PHONE').should('not.exist'); + } + }, + ); + }); + + it('Admin can select a form template (Personal Signup)', () => { + const postTitle = 'Mailchimp signup form - Form Template 2'; + cy.createPost({ title: postTitle, content: '', beforeSave: () => beforeSave(2) }).then( + (postBlock2) => { + if (postBlock2) { + cy.visit(`/?p=${postBlock2.id}`); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_mv_FNAME').should('exist'); + cy.get('#mc_mv_LNAME').should('exist'); + cy.get('input[id^="mc_mv_PHONE"]').should('not.exist'); + } + }, + ); + }); + + it('Admin can select a form template (Contact Form)', () => { + const postTitle = 'Mailchimp signup form - Form Template 3'; + cy.createPost({ title: postTitle, content: '', beforeSave: () => beforeSave(3) }).then( + (postBlock3) => { + if (postBlock3) { + cy.visit(`/?p=${postBlock3.id}`); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_mv_FNAME').should('exist'); + cy.get('#mc_mv_LNAME').should('exist'); + cy.get('input[id^="mc_mv_PHONE"]').should('exist'); + cy.get('#mc_mv_ADDRESS-addr1').should('exist'); + cy.get('#mc_mv_ADDRESS-addr2').should('exist'); + cy.get('#mc_mv_ADDRESS-city').should('exist'); + } + }, + ); + }); + + it('Admin can select a form template (Default Form)', () => { + const postTitle = 'Mailchimp signup form - Form Template 4'; + cy.createPost({ title: postTitle, content: '', beforeSave: () => beforeSave(4) }).then( + (postBlock4) => { + if (postBlock4) { + cy.visit(`/?p=${postBlock4.id}`); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_mv_FNAME').should('exist'); + cy.get('#mc_mv_LNAME').should('exist'); + cy.get('input[id^="mc_mv_PHONE"]').should('exist'); + cy.get('#mc_mv_COMPANY').should('exist'); + cy.get('#mc_mv_ADDRESS-addr1').should('exist'); + cy.get('#mc_mv_ADDRESS-addr2').should('exist'); + } + }, + ); + }); +});