Skip to content

[Block] Add Form templates #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions includes/blocks/class-mailchimp-list-subscribe-form-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand Down Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion includes/blocks/mailchimp-form-field/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
6 changes: 4 additions & 2 deletions includes/blocks/mailchimp-form-field/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div {...blockProps} style={{ color: 'inherit' }}>
Expand Down
6 changes: 6 additions & 0 deletions includes/blocks/mailchimp-form-field/field-markup.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

?>
<div <?php echo get_block_wrapper_attributes(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<?php
Expand Down
9 changes: 7 additions & 2 deletions includes/blocks/mailchimp/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
"required_indicator_text": {
"type": "string",
"default": "* = required field"
},
"template": {
"type": "string",
"default": "default"
}
},
"supports": {
Expand Down Expand Up @@ -76,10 +80,11 @@
],
"providesContext": {
"mailchimp/list_id": "list_id",
"mailchimp/show_required_indicator": "show_required_indicator"
"mailchimp/show_required_indicator": "show_required_indicator",
"mailchimp/template": "template"
},
"textdomain": "mailchimp",
"editorScript": "file:./index.js",
"render": "file:./markup.php",
"editorStyle": "file:./editor.css"
"editorStyle": ["file:./editor.css", "dashicons"]
}
44 changes: 28 additions & 16 deletions includes/blocks/mailchimp/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import apiFetch from '@wordpress/api-fetch';
import { useDispatch, useSelect } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
import Icon from './icon';
import { VariationPicker } from './variation-picker';

const SelectListPlaceholder = () => {
return (
Expand Down Expand Up @@ -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({});
Expand All @@ -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.
Expand Down Expand Up @@ -116,20 +121,27 @@ 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 =
data?.interest_groups?.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 the groups hidden by default.
}),
) || [];
replaceInnerBlocks(clientId, [...listFieldsBlocks, ...listGroupsBlocks], false);
Expand Down Expand Up @@ -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.
}),
);

Expand Down Expand Up @@ -279,6 +286,11 @@ export const BlockEdit = (props) => {
);
}

// Display the variation picker if there are no innerBlocks.
if (innerBlocks.length === 0) {
return <VariationPicker {...props} />;
}

// Create a template for innerBlocks based on list data and visibility settings.
const templateFields =
listData?.merge_fields?.map((field) => [
Expand All @@ -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 (
<>
Expand Down Expand Up @@ -343,7 +355,7 @@ export const BlockEdit = (props) => {
<InnerBlocks
allowedBlocks={['mailchimp/mailchimp-form-field']}
orientation="vertical"
template={template}
template={templateBlocks}
templateLock="insert"
/>
{show_required_indicator && (
Expand Down
35 changes: 35 additions & 0 deletions includes/blocks/mailchimp/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 2 additions & 0 deletions includes/blocks/mailchimp/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down
12 changes: 8 additions & 4 deletions includes/blocks/mailchimp/markup.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
)
);
Expand Down Expand Up @@ -108,6 +111,7 @@ function ( $single_list ) {
<input type="hidden" name="mailchimp_sf_list_id" value="<?php echo esc_attr( $list_id ); ?>" />
<input type="hidden" name="mailchimp_sf_update_existing_subscribers" value="<?php echo esc_attr( $update_existing_subscribers ); ?>" />
<input type="hidden" name="mailchimp_sf_double_opt_in" value="<?php echo esc_attr( $double_opt_in ); ?>" />
<input type="hidden" name="mailchimp_sf_skip_merge_validation" value="<?php echo esc_attr( $skip_merge_validation ); ?>" />
<input type="hidden" name="mailchimp_sf_hash" value="<?php echo esc_attr( $hash ); ?>" />
<?php
wp_nonce_field( 'mc_submit_signup_form', '_mc_submit_signup_form_nonce', false );
Expand Down
Loading
Loading