'background' => esc_html__( 'Background', 'wpforms-lite' ),
'border' => esc_html__( 'Border', 'wpforms-lite' ),
'text' => esc_html__( 'Text', 'wpforms-lite' ),
'menu' => esc_html__( 'Menu', 'wpforms-lite' ),
'image' => esc_html__( 'Image', 'wpforms-lite' ),
'media_library' => esc_html__( 'Media Library', 'wpforms-lite' ),
'choose_image' => esc_html__( 'Choose Image', 'wpforms-lite' ),
'stock_photo' => esc_html__( 'Stock Photo', 'wpforms-lite' ),
'border_radius' => esc_html__( 'Border Radius', 'wpforms-lite' ),
'border_size' => esc_html__( 'Border Size', 'wpforms-lite' ),
'border_style' => esc_html__( 'Border Style', 'wpforms-lite' ),
'none' => esc_html__( 'None', 'wpforms-lite' ),
'solid' => esc_html__( 'Solid', 'wpforms-lite' ),
'dashed' => esc_html__( 'Dashed', 'wpforms-lite' ),
'dotted' => esc_html__( 'Dotted', 'wpforms-lite' ),
'double' => esc_html__( 'Double', 'wpforms-lite' ),
'shadow_size' => esc_html__( 'Shadow', 'wpforms-lite' ),
'border_width' => esc_html__( 'Border Size', 'wpforms-lite' ),
'border_color' => esc_html__( 'Border', 'wpforms-lite' ),
'colors' => esc_html__( 'Colors', 'wpforms-lite' ),
'label' => esc_html__( 'Label', 'wpforms-lite' ),
'sublabel_hints' => esc_html__( 'Sublabel & Hint', 'wpforms-lite' ),
'error_message' => esc_html__( 'Error Message', 'wpforms-lite' ),
'small' => esc_html__( 'Small', 'wpforms-lite' ),
'medium' => esc_html__( 'Medium', 'wpforms-lite' ),
'large' => esc_html__( 'Large', 'wpforms-lite' ),
'btn_yes' => esc_html__( 'Yes', 'wpforms-lite' ),
'btn_no' => esc_html__( 'No', 'wpforms-lite' ),
'copy_paste_settings' => esc_html__( 'Copy / Paste Style Settings', 'wpforms-lite' ),
'copy_paste_error' => esc_html__( 'There was an error parsing your JSON code. Please check your code and try again.', 'wpforms-lite' ),
'copy_paste_notice' => esc_html__( 'If you\'ve copied style settings from another form, you can paste them here to add the same styling to this form. Any current style settings will be overwritten.', 'wpforms-lite' ),
'custom_css' => esc_html__( 'Custom CSS', 'wpforms-lite' ),
'custom_css_notice' => esc_html__( 'Further customize the look of this form without having to edit theme files.', 'wpforms-lite' ),
// Translators: %1$s: Opening strong tag, %2$s: Closing strong tag.
'wpforms_empty_info' => sprintf( esc_html__( 'You can use %1$sWPForms%2$s to build contact forms, surveys, payment forms, and more with just a few clicks.', 'wpforms-lite' ), '<strong>','</strong>' ),
// Translators: %1$s: Opening anchor tag, %2$s: Closing anchor tag.
'wpforms_empty_help' => sprintf( esc_html__( 'Need some help? Check out our %1$scomprehensive guide.%2$s', 'wpforms-lite' ), '<a target="_blank" href="' . esc_url( wpforms_utm_link( 'https://wpforms.com/docs/creating-first-form/', 'gutenberg', 'Create Your First Form Documentation' ) ) . '">','</a>' ),
'other_styles' => esc_html__( 'Other Styles', 'wpforms-lite' ),
'page_break' => esc_html__( 'Page Break', 'wpforms-lite' ),
'rating' => esc_html__( 'Rating', 'wpforms-lite' ),
'heads_up' => esc_html__( 'Heads Up!', 'wpforms-lite' ),
'form_not_available_message' => esc_html__( 'It looks like the form you had selected is in the Trash or has been permanently deleted.', 'wpforms-lite' ),
'logo_url' => WPFORMS_PLUGIN_URL . 'assets/images/wpforms-logo.svg',
'block_preview_url' => WPFORMS_PLUGIN_URL . 'assets/images/integrations/gutenberg/block-preview.png',
'block_empty_url' => WPFORMS_PLUGIN_URL . 'assets/images/empty-states/no-forms.svg',
'route_namespace' => RestApi::ROUTE_NAMESPACE,
'wpnonce' => wp_create_nonce( 'wpforms-gutenberg-form-selector' ),
'form_url' => admin_url( 'admin.php?page=wpforms-builder&view=fields&form_id={ID}' ),
'entries_url' => admin_url( 'admin.php?view=list&page=wpforms-entries&form_id={ID}' ),
'forms' => $this->get_form_list(),
'isAdmin' => current_user_can( 'manage_options' ),
'isPro' => wpforms()->is_pro(),
'defaults' => self::DEFAULT_ATTRIBUTES,
'is_modern_markup' => $this->render_engine === 'modern',
'is_full_styling' => $this->disable_css_setting === 1,
'wpforms_guide' => esc_url( wpforms_utm_link( 'https://wpforms.com/docs/creating-first-form/', 'gutenberg', 'Create Your First Form Documentation' ) ),
'get_started_url' => esc_url( admin_url( 'admin.php?page=wpforms-builder' ) ),
'field-size' => CSSVars::FIELD_SIZE,
'label-size' => CSSVars::LABEL_SIZE,
'button-size' => CSSVars::BUTTON_SIZE,
'container-shadow-size' => CSSVars::CONTAINER_SHADOW_SIZE,
* @noinspection NullPointerExceptionInspection
public function get_form_list(): array {
$forms = wpforms()->obj( 'form' )->get( '', [ 'order' => 'DESC' ] );
static function ( $form ) {
$form->post_title = htmlspecialchars_decode( $form->post_title, ENT_QUOTES );
$form->post_title = trim( mb_substr( trim( $form->post_title ), 0, $max_length ) );
$form->post_title = mb_strlen( $form->post_title ) === $max_length ? $form->post_title . '…' : $form->post_title;
* @param string|mixed $action Form action.
* @param array|mixed $form_data Form data.
* @noinspection PhpUnusedParameterInspection
public function form_action_filter( $action, $form_data ): string {
if ( $this->is_gb_editor() ) {
// Remove inappropriate form action URL that contains all the block attributes.
* Get form HTML to display in a WPForms Gutenberg block.
* @param array|mixed $attr Attributes passed by WPForms Gutenberg block.
public function get_form_html( $attr ): string {
$id = ! empty( $attr['formId'] ) ? absint( $attr['formId'] ) : 0;
$this->current_form_id = $id;
if ( $this->is_gb_editor() ) {
$this->disable_fields_in_gb_editor();
$title = ! empty( $attr['displayTitle'] );
$desc = ! empty( $attr['displayDesc'] );
$this->add_class_callback( $id, $attr );
// Maybe override block attributes with the theme settings.
$attr = $this->maybe_override_block_attributes( $attr );
$content = $this->get_content( $id, $title, $desc, $attr );
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
* Filter Gutenberg block content.
* @param string $content Block content.
* @param int $id Form id.
return (string) apply_filters( 'wpforms_gutenberg_block_form_content', $content, $id );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
* Maybe override block attributes.
* This method is used to override block attributes with the theme settings.
* @param array $attr Attributes passed by WPForms Gutenberg block.
private function maybe_override_block_attributes( array $attr ): array {
$theme_slug = (string) ( $attr['theme'] ?? '' );
// Previously added blocks (FS 1.0) don't have the themeName attribute.
// To preserve existing styling of such old blocks, we shouldn't override attributes.
if ( ! isset( $attr['themeName'] ) || ( empty( $attr['themeName'] ) && $theme_slug === 'default' ) ) {
if ( $theme_slug === '' ) {
$theme_slug = $this->get_theme_slug( $attr );
$theme_data = $this->themes_data_obj->get_theme( $theme_slug );
// Theme doesn't exist, let's return.
// Override block attributes with the theme settings.
return array_merge( $attr, $theme_data['settings'] );
* @param array $attr Attributes passed by WPForms Gutenberg block.
private function get_theme_slug( array $attr ): string {
$form_handler = wpforms()->obj( 'form' );
$form_id = (int) $attr['formId'];
$form_data = $form_handler->get( $form_id, [ 'content_only' => true ] );
if ( empty( $form_data['settings']['themes']['wpformsTheme'] ) ) {
return $form_data['settings']['themes']['wpformsTheme'];
* @param int $id Form id.
* @param array $attr Form attributes.
private function add_class_callback( int $id, array $attr ): void { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
$class_callback = static function ( $classes, $form_data ) use ( $id, $attr ) {
if ( (int) $form_data['id'] !== $id ) {
// Add custom class to form container.
if ( ! empty( $attr['className'] ) ) {
$cls = array_map( 'esc_attr', explode( ' ', $attr['className'] ) );
// Add classes to identify that the form displays inside the block.
$cls[] = 'wpforms-block';
if ( ! empty( $attr['clientId'] ) ) {
$cls[] = 'wpforms-block-' . $attr['clientId'];
return array_unique( array_merge( $classes, $cls ) );
if ( empty( $this->callbacks[ $id ] ) ) {
add_filter( 'wpforms_frontend_container_class', $class_callback, 10, 2 );
$this->callbacks[ $id ][] = $class_callback;
* @param int $id Form id.
* @param bool $title Form title is not empty.
* @param bool $desc Form desc is not empty.
* @param array $attr Form attributes.
* @noinspection JSUnresolvedReference
private function get_content( int $id, bool $title, bool $desc, array $attr ): string {
* Filter allow render block content flag.
* @param bool $allow_render Allow render flag. Defaults to `true`.
$allow_render = (bool) apply_filters( 'wpforms_integrations_gutenberg_form_selector_allow_render', true );
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
* Fires before Gutenberg block output.
do_action( 'wpforms_gutenberg_block_before' );
* Filter block title display flag.
* @param bool $title Title display flag.
* @param int $id Form id.
$title = (bool) apply_filters( 'wpforms_gutenberg_block_form_title', $title, $id );
* Filter block description display flag.
* @param bool $desc Description display flag.
* @param int $id Form id.
$desc = (bool) apply_filters( 'wpforms_gutenberg_block_form_desc', $desc, $id );
$this->output_css_vars( $attr );
$this->output_custom_css( $attr );
wpforms_display( $id, $title, $desc );
* Fires after Gutenberg block output.
do_action( 'wpforms_gutenberg_block_after' );
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName
$content = (string) ob_get_clean();
if ( ! $this->is_gb_editor() ) {
if ( empty( $content ) ) {
return '<div class="components-placeholder"><div class="components-placeholder__label"></div>' .
'<div class="components-placeholder__fieldset">' .
esc_html__( 'The form cannot be displayed.', 'wpforms-lite' ) .
* Unfortunately, the inline 'script' tag cannot be executed in the GB editor.
* This is the hacky way to trigger custom event on form loaded in the Block Editor / GB / FSE.
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_var_export
'<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" onLoad="
window.top.dispatchEvent(
\'wpformsFormSelectorFormLoaded\',
block: this.closest( \'.wp-block\' )
" class="wpforms-pix-trigger" alt="">',
var_export( $title, true ),
var_export( $desc, true )
// phpcs:enable WordPress.PHP.DevelopmentFunctions.error_log_var_export
* Checking if is Gutenberg REST API call.
* @return bool True if is Gutenberg REST API call.
public function is_gb_editor(): bool {
// TODO: Find a better way to check if is GB editor API call.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_REQUEST['context'] ) && $_REQUEST['context'] === 'edit';
* Disable form fields if called from the Gutenberg editor.
private function disable_fields_in_gb_editor(): void { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
'wpforms_frontend_container_class',
static function ( $classes ) {
$classes[] = 'wpforms-gutenberg-form-selector';
'wpforms_frontend_output',
echo '<fieldset disabled>';
'wpforms_frontend_output',
* Output CSS variables for the particular form.
* @param array $attr Attributes passed by WPForms Gutenberg block.
private function output_css_vars( array $attr ): void {
if ( empty( $this->css_vars_obj ) || ! method_exists( $this->css_vars_obj, 'get_vars' ) ) {
if ( $this->render_engine === 'classic' || $this->disable_css_setting !== 1 ) {
$css_vars = $this->css_vars_obj->get_customized_css_vars( $attr );
if ( empty( $css_vars ) ) {
$style_id = "#wpforms-css-vars-{$attr['formId']}-block-{$attr['clientId']}";
* Filter the CSS selector for output CSS variables for styling the GB block form.
* @param string $selector The CSS selector for output CSS variables for styling the GB block form.
* @param array $attr Attributes passed by WPForms Gutenberg block.
* @param array $css_vars CSS variables data.
$vars_selector = apply_filters(
'wpforms_integrations_gutenberg_form_selector_output_css_vars_selector',
"#wpforms-{$attr['formId']}.wpforms-block-{$attr['clientId']}",
$style_id = rtrim( $style_id, '-' );
$vars_selector = rtrim( $vars_selector, '-' );
$this->css_vars_obj->output_selector_vars( $vars_selector, $css_vars, $style_id, $this->current_form_id );
* Output custom CSS styles.