register_block_from_metadata( 'submit' ); } public function render( $atts, $content, $block = null ) { $form_id = $this->resolve_form_scope_id(); $dynamic_target_post_id = 0; if ( $form_id && function_exists( 'stacklar_multilingual' ) ) { $atts = stacklar_multilingual()->translate_block_attributes( $form_id, 'stacklar/submit', is_array( $atts ) ? $atts : array() ); } $submit_post_settings = $form_id ? stacklar_get_action_settings_for_action( $form_id, 'stacklar_submit_post' ) : array(); $edit_link_enabled = ! empty( $submit_post_settings ) && isset( $submit_post_settings['editlinkenabled'] ) ? stacklar_is_truthy( $submit_post_settings['editlinkenabled'] ) : false; if ( $edit_link_enabled ) { if ( ! function_exists( 'is_user_logged_in' ) || ! is_user_logged_in() ) { return $this->render_access_message( __( 'Please log in to edit this content.', 'stacklar-forms' ) ); } $roles = isset( $submit_post_settings['editlinkroles'] ) ? stacklar_parse_role_list( $submit_post_settings['editlinkroles'] ) : array(); if ( ! stacklar_current_user_has_role( $roles ) ) { return $this->render_access_message( __( 'You do not have permission to edit this content.', 'stacklar-forms' ) ); } // The post to edit comes from the edit link; edit_post is checked below. $target_post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( $target_post_id ) { if ( ! current_user_can( 'edit_post', $target_post_id ) ) { return $this->render_access_message( __( 'You do not have permission to edit this content.', 'stacklar-forms' ) ); } $dynamic_target_post_id = $target_post_id; } else { if ( ! current_user_can( 'edit_posts' ) ) { return $this->render_access_message( __( 'You do not have permission to edit this content.', 'stacklar-forms' ) ); } } } if ( ! $dynamic_target_post_id && function_exists( 'get_queried_object' ) ) { $queried_object = get_queried_object(); if ( $queried_object instanceof WP_Post ) { $dynamic_target_post_id = absint( $queried_object->ID ); } } if ( ! $dynamic_target_post_id && function_exists( 'get_the_ID' ) ) { $dynamic_target_post_id = absint( get_the_ID() ); } $scope_class = $form_id ? ' stacklar--' . $form_id : ''; $submit_label = isset( $atts['submitLabel'] ) ? $atts['submitLabel'] : 'Submit'; $test_mode = $form_id && function_exists( 'stacklar_is_form_test_mode' ) ? stacklar_is_form_test_mode( $form_id ) : ! empty( $atts['testMode'] ); $actions = $this->prepare_actions( $atts ); $action_settings = $this->prepare_action_settings( $atts ); if ( $this->should_store_entries( $atts ) && ! in_array( 'stacklar_save_entry', $actions, true ) ) { array_unshift( $actions, 'stacklar_save_entry' ); } $actions = array_values( array_unique( $actions ) ); $paypal_enabled = in_array( 'stacklar_paypal', $actions, true ); $stripe_enabled = in_array( 'stacklar_stripe', $actions, true ); $woocommerce_enabled = in_array( 'stacklar_woocommerce', $actions, true ); $protection_config = function_exists( 'stacklar_submission_protection_service' ) ? stacklar_submission_protection_service()->get_render_config( $form_id ) : array( 'enabled' => false ); $restrictions_config = function_exists( 'stacklar_restrictions_service' ) ? stacklar_restrictions_service()->get_render_config( $form_id ) : array( 'enabled' => false ); $survey_results_config = function_exists( 'stacklar_survey_results_service' ) ? stacklar_survey_results_service()->get_render_config( $form_id ) : array( 'enabled' => false ); $partial_config = class_exists( 'Stacklar_Abandonments', false ) ? Stacklar_Abandonments::get_render_config( $form_id ) : array( 'enabled' => false ); $autosave_nonce = $form_id ? wp_create_nonce( 'stacklar_autosave_' . $form_id ) : ''; $config_nonce = $form_id ? wp_create_nonce( 'stacklar_config_' . $form_id ) : ''; $dynamic_post_context = $form_id && $dynamic_target_post_id && function_exists( 'stacklar_create_dynamic_post_context' ) ? stacklar_create_dynamic_post_context( $form_id, $dynamic_target_post_id ) : array(); $form_typography = ''; if ( ! empty( $atts['style'] ) && ! empty( $atts['style']['typography'] ) && function_exists( 'wp_style_engine_get_styles' ) ) { $typo_result = wp_style_engine_get_styles( array( 'typography' => $atts['style']['typography'] ) ); if ( ! empty( $typo_result['css'] ) ) { $form_typography = implode( ' ', stacklar_sanitize_css_declarations( array( $typo_result['css'] ) ) ); } } $button_styles = $this->build_button_style( $atts ); $wrap_styles = $this->build_wrap_style( $this->resolve_responsive_scalar_style_value( $atts, 'submitAlign', 'desktop', 'string' ), isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null ); $has_steps = $this->form_has_step_blocks( $form_id ); $is_multistep = $has_steps; if ( ! $has_steps && ! $form_id ) { $is_multistep = ! empty( $atts['isMultiStep'] ); } $show_progress = ! empty( $atts['showProgressBar'] ); $progress_color = $atts['progressBarColor'] ?? '#111111'; $progress_bg = $atts['progressBarBg'] ?? '#e2e8f0'; $progress_height = isset( $atts['progressBarHeight'] ) ? intval( $atts['progressBarHeight'] ) : 6; $progress_percent = isset( $atts['progressShowPercent'] ) ? (bool) $atts['progressShowPercent'] : true; $progress_aria = $atts['progressAriaLabel'] ?? 'Form progress'; $form_classes = trim( 'stacklar' . $scope_class . ( $is_multistep ? ' is-multistep' : '' ) ); $form_instance_id = wp_unique_id( 'stacklar-' ); $instance_selector = '.stacklar-submit-root[data-stacklar-instance="' . esc_attr( $form_instance_id ) . '"]'; $partial_active = ! empty( $partial_config['enabled'] ) && ! empty( $partial_config['licenseValid'] ); $partial_autosave = $partial_active && ! empty( $partial_config['autosaveEnabled'] ); $partial_client = $partial_active && ( $partial_autosave || ! empty( $partial_config['showSaveButton'] ) || ! empty( $partial_config['resumeEnabled'] ) ); $current_locale = $form_id && function_exists( 'stacklar_get_current_locale' ) ? stacklar_get_current_locale( $form_id ) : ''; $form_attrs = 'id="' . esc_attr( $form_instance_id ) . '" data-stacklar-instance="' . esc_attr( $form_instance_id ) . '" class="' . esc_attr( $form_classes ) . '" method="post" data-stacklar-rest="1" data-stacklar-abandonment="' . ( $partial_client ? '1' : '0' ) . '"'; if ( '' !== $current_locale ) { $form_attrs .= ' data-stacklar-locale="' . esc_attr( $current_locale ) . '"'; } if ( $test_mode ) { $form_attrs .= ' data-stacklar-test-mode="1"'; } if ( $form_id ) { $form_attrs .= ' data-stacklar-id="' . esc_attr( $form_id ) . '"'; } if ( $paypal_enabled ) { $form_attrs .= ' data-stacklar-paypal="1"'; } if ( $stripe_enabled ) { $form_attrs .= ' data-stacklar-stripe="1"'; } if ( $woocommerce_enabled ) { $form_attrs .= ' data-stacklar-woocommerce="1"'; } if ( $form_typography !== '' ) { $form_attrs .= ' style="' . esc_attr( $form_typography ) . '"'; } if ( ! empty( $atts['useLabelKeys'] ) ) { $form_attrs .= ' data-stacklar-label-keys="1"'; } else { $form_attrs .= ' data-stacklar-label-keys="0"'; } $form_attrs .= ' data-stacklar-autosave-token=""'; $form_attrs .= ' data-stacklar-autosave-nonce="' . esc_attr( $autosave_nonce ) . '"'; if ( ! empty( $partial_config['enabled'] ) ) { $form_attrs .= ' data-stacklar-partial="1"'; $form_attrs .= ' data-stacklar-partial-autosave="' . ( $partial_autosave ? '1' : '0' ) . '"'; $form_attrs .= ' data-stacklar-autosave-interval="' . esc_attr( isset( $partial_config['autosaveIntervalSeconds'] ) ? absint( $partial_config['autosaveIntervalSeconds'] ) : 15 ) . '"'; $form_attrs .= ' data-stacklar-partial-resume="' . ( ! empty( $partial_config['resumeEnabled'] ) ? '1' : '0' ) . '"'; $form_attrs .= ' data-stacklar-partial-save-message="' . esc_attr( isset( $partial_config['savedMessage'] ) ? $partial_config['savedMessage'] : '' ) . '"'; $form_attrs .= ' data-stacklar-partial-resume-message="' . esc_attr( isset( $partial_config['resumeMessage'] ) ? $partial_config['resumeMessage'] : '' ) . '"'; $form_attrs .= ' data-stacklar-partial-expired-message="' . esc_attr( isset( $partial_config['expiredMessage'] ) ? $partial_config['expiredMessage'] : '' ) . '"'; } $hidden_inputs = ''; if ( $form_id ) { $hidden_inputs .= ''; if ( '' !== $current_locale ) { $hidden_inputs .= ''; } if ( $config_nonce ) { $hidden_inputs .= ''; } if ( ! empty( $dynamic_post_context['post_id'] ) && ! empty( $dynamic_post_context['expires'] ) && ! empty( $dynamic_post_context['signature'] ) ) { $hidden_inputs .= ''; $hidden_inputs .= ''; $hidden_inputs .= ''; } $hidden_inputs .= ''; $hidden_inputs .= ''; if ( ! empty( $protection_config['enabled'] ) || ! empty( $restrictions_config['enabled'] ) || ! empty( $survey_results_config['enabled'] ) || ! empty( $partial_config['enabled'] ) ) { $hidden_inputs .= ''; } if ( ! empty( $protection_config['enabled'] ) ) { if ( ! empty( $protection_config['issued_at'] ) && ! empty( $protection_config['signature'] ) ) { $hidden_inputs .= ''; $hidden_inputs .= ''; } } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- carries the edit link's post ID into the form. if ( $edit_link_enabled && isset( $_GET['post_id'] ) ) { $edit_post_id = absint( $_GET['post_id'] ); // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( $edit_post_id ) { $hidden_inputs .= ''; } } } $html = '
'; $html .= '
'; if ( $test_mode ) { $html .= '
' . esc_html__( 'Test mode is active. Submissions are marked as test data and excluded from production counters.', 'stacklar-forms' ) . '
'; } if ( $hidden_inputs !== '' ) { $html .= $hidden_inputs; } if ( ! empty( $protection_config['enabled'] ) && ! empty( $protection_config['honeypot'] ) ) { $html .= ''; } if ( $is_multistep && $show_progress ) { $progress_style = implode( '', stacklar_sanitize_css_declarations( array( '--stacklar-progress-bg:' . stacklar_sanitize_css_value( $progress_bg ) . ';', '--stacklar-progress-color:' . stacklar_sanitize_css_value( $progress_color ) . ';', '--stacklar-progress-height:' . max( 1, $progress_height ) . 'px;', ) ) ); $html .= '
'; $html .= '
'; if ( $progress_percent ) { $html .= '0%'; } $html .= '
'; } $html .= '
'; $wrap_styles = stacklar_sanitize_css_declarations( $wrap_styles ); $button_styles = stacklar_sanitize_css_declarations( $button_styles ); $wrap_attr = $wrap_styles ? ' style="' . esc_attr( implode( ' ', $wrap_styles ) ) . '"' : ''; $button_attr = $button_styles ? ' style="' . esc_attr( implode( ' ', $button_styles ) ) . '"' : ''; $html .= '
'; if ( $paypal_enabled ) { $html .= '
'; } if ( $stripe_enabled ) { $html .= '
'; } if ( ! empty( $partial_config['enabled'] ) && ! empty( $partial_config['showSaveButton'] ) && ! empty( $partial_config['licenseValid'] ) ) { $save_label = ! empty( $partial_config['saveButtonLabel'] ) ? $partial_config['saveButtonLabel'] : __( 'Save progress', 'stacklar-forms' ); $html .= ''; } $html .= ''; $html .= '
'; $html .= '
'; $html .= '
'; $html .= '
'; $prepended_css = ''; if ( $is_multistep ) { $multistep_css = $this->build_multistep_css( $atts, $form_id ); if ( $multistep_css ) { $prepended_css .= $multistep_css; } } else { $hover_css = $this->build_hover_css( $atts, $form_id ); $focus_css = $this->build_focus_css( $atts, $form_id ); $style = ''; if ( $hover_css ) $style .= $hover_css; if ( $focus_css ) $style .= $focus_css; if ( $style !== '' ) { $prepended_css .= $style; } } $message_css = $this->build_message_css( $atts, $form_id ); if ( $message_css ) { $prepended_css .= $message_css; } $responsive_submit_css = $this->build_submit_responsive_css( $atts, $instance_selector ); if ( $responsive_submit_css ) { $prepended_css .= $responsive_submit_css; } stacklar_add_inline_css( $prepended_css ); return $html; } protected function prepare_actions( $atts ) { $actions = array(); if ( ! empty( $atts['actions'] ) && is_array( $atts['actions'] ) ) { foreach ( $atts['actions'] as $act ) { $act = sanitize_key( $act ); if ( $act ) { $actions[] = $act; } } } return array_values( array_unique( $actions ) ); } protected function prepare_action_settings( $atts ) { $out = array(); if ( empty( $atts['actionSettings'] ) || ! is_array( $atts['actionSettings'] ) ) { return $out; } foreach ( $atts['actionSettings'] as $slug => $settings ) { $slug_key = sanitize_key( $slug ); if ( ! $slug_key || ! is_array( $settings ) ) { continue; } foreach ( $settings as $key => $value ) { $setting_key = sanitize_key( $key ); if ( '' === $setting_key ) { continue; } if ( is_scalar( $value ) ) { $out[ $slug_key ][ $setting_key ] = sanitize_text_field( $value ); } } } return $out; } protected function should_track_abandonment( $atts, $form_id = 0 ) { if ( $form_id && class_exists( 'Stacklar_Abandonments', false ) ) { return Stacklar_Abandonments::is_autosave_enabled( $form_id ); } if ( array_key_exists( 'trackAbandonment', $atts ) ) { return (bool) $atts['trackAbandonment']; } return false; } protected function render_access_message( $message ) { $message = is_string( $message ) ? $message : ''; if ( '' === $message ) { return ''; } return '
' . esc_html( $message ) . '
'; } protected function should_store_entries( $atts ) { if ( array_key_exists( 'storeEntries', $atts ) ) { return (bool) $atts['storeEntries']; } return false; } protected function build_wrap_style( $align, $button_margin = null, $device = 'desktop', $force_explicit_margin_top = false ) { $align = $align ? sanitize_key( $align ) : 'left'; $styles = array(); if ( 'center' === $align ) { $styles[] = 'display:flex;'; $styles[] = 'justify-content:center;'; } elseif ( 'right' === $align ) { $styles[] = 'display:flex;'; $styles[] = 'justify-content:flex-end;'; } elseif ( 'full' === $align ) { $styles[] = 'display:block;'; } else { $styles[] = 'display:flex;'; $styles[] = 'justify-content:flex-start;'; } if ( $force_explicit_margin_top ) { $styles[] = $this->has_box_side_value( $button_margin, 'top', $device ) ? 'margin-top:0;' : 'margin-top:8px;'; } elseif ( ! $this->has_box_side_value( $button_margin, 'top', $device ) ) { $styles[] = 'margin-top:8px;'; } return $styles; } protected function resolve_padding_css( $atts, $device = 'desktop' ) { $pad = isset( $atts['submitPadding'] ) ? $this->resolve_responsive_style_value( $atts['submitPadding'], $device, 'box' ) : null; $pad_y = isset( $atts['submitPadY'] ) ? floatval( $atts['submitPadY'] ) : 8; $pad_x = isset( $atts['submitPadX'] ) ? floatval( $atts['submitPadX'] ) : 12; if ( is_string( $pad ) ) { $pad = trim( $pad ); if ( '' !== $pad ) { return $pad; } } if ( is_array( $pad ) && ! empty( $pad ) ) { $unit = ! empty( $pad['unit'] ) ? preg_replace( '/[^a-z%]/i', '', $pad['unit'] ) : 'px'; $format = function( $value, $fallback ) use ( $unit ) { if ( $value === null || $value === '' ) { return $fallback === 0 ? '0' : ( preg_match( '/[a-z%]+$/i', (string) $fallback ) ? $fallback : $fallback . $unit ); } $str = is_string( $value ) ? trim( $value ) : ( is_numeric( $value ) ? (string) $value : '' ); if ( '' === $str ) { return '0'; } return preg_match( '/[a-z%]+$/i', $str ) ? $str : $str . $unit; }; return $format( $pad['top'] ?? '', $pad_y ) . ' ' . $format( $pad['right'] ?? '', $pad_x ) . ' ' . $format( $pad['bottom'] ?? '', $pad_y ) . ' ' . $format( $pad['left'] ?? '', $pad_x ); } return $pad_y . 'px ' . $pad_x . 'px'; } protected function resolve_box_css( $box, $device = 'desktop' ) { $box = $this->resolve_responsive_style_value( $box, $device, 'box' ); if ( is_string( $box ) ) { return trim( $box ); } if ( empty( $box ) || ! is_array( $box ) ) { return ''; } $has_value = false; foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { if ( isset( $box[ $side ] ) && trim( (string) $box[ $side ] ) !== '' ) { $has_value = true; break; } } if ( ! $has_value ) { return ''; } $unit = ! empty( $box['unit'] ) ? preg_replace( '/[^a-z%]/i', '', $box['unit'] ) : 'px'; $format = function( $value ) use ( $unit ) { if ( $value === null || $value === '' ) { return '0'; } $str = is_string( $value ) ? trim( $value ) : ( is_numeric( $value ) ? (string) $value : '' ); if ( '' === $str ) { return '0'; } return preg_match( '/[a-z%]+$/i', $str ) ? $str : $str . $unit; }; return $format( $box['top'] ?? '' ) . ' ' . $format( $box['right'] ?? '' ) . ' ' . $format( $box['bottom'] ?? '' ) . ' ' . $format( $box['left'] ?? '' ); } protected function has_box_side_value( $box, $side, $device = 'desktop' ) { $box = $this->resolve_responsive_style_value( $box, $device, 'box' ); if ( empty( $box ) || ! is_array( $box ) || ! isset( $box[ $side ] ) ) { return false; } $value = is_scalar( $box[ $side ] ) ? trim( (string) $box[ $side ] ) : ''; return '' !== $value; } protected function build_typography_declarations( $value, $context = 'attr', $device = 'desktop' ) { $value = $this->resolve_responsive_style_value( $value, $device, 'typography' ); if ( empty( $value ) || ! is_array( $value ) ) { return array(); } if ( isset( $value['typography'] ) && is_array( $value['typography'] ) ) { $value = $value['typography']; } $map = array( 'fontFamily' => 'font-family', 'fontSize' => 'font-size', 'fontStyle' => 'font-style', 'fontWeight' => 'font-weight', 'lineHeight' => 'line-height', 'letterSpacing' => 'letter-spacing', 'textTransform' => 'text-transform', 'textDecoration'=> 'text-decoration', 'textAlign' => 'text-align', ); $styles = array(); foreach ( $map as $key => $property ) { if ( ! isset( $value[ $key ] ) ) { continue; } $raw = $value[ $key ]; if ( $raw === '' || $raw === null ) { continue; } $sanitized = is_scalar( $raw ) ? trim( wp_strip_all_tags( (string) $raw ) ) : ''; if ( '' === $sanitized ) { continue; } $styles[] = $property . ':' . stacklar_sanitize_css_value( $sanitized ) . ';'; } return $styles; } protected function sanitize_css_keyword( $value ) { return preg_replace( '/[^a-z-]/i', '', (string) $value ); } protected function clamp_css_number( $value, $min = 0, $max = null ) { $number = floatval( $value ); if ( null !== $min ) { $number = max( floatval( $min ), $number ); } if ( null !== $max ) { $number = min( floatval( $max ), $number ); } return $number; } protected function append_css_rule_if_string( array &$rules, $property, $value, $important = false ) { $value = trim( (string) $value ); if ( '' === $value ) { return; } $rules[] = $property . ':' . stacklar_sanitize_css_value( $value ) . ( $important ? ' !important' : '' ) . ';'; } protected function append_css_rule_if_number( array &$rules, $property, $value, $unit = 'px', $min = 0, $max = null, $important = false ) { if ( null === $value ) { return; } $rules[] = $property . ':' . $this->clamp_css_number( $value, $min, $max ) . $unit . ( $important ? ' !important' : '' ) . ';'; } protected function append_resolved_string_rule( array &$rules, $atts, $key, $property, $device = 'desktop', $important = false, $keyword = false, $responsive_only = false ) { if ( $responsive_only && ! $this->has_responsive_scalar_style_value( $atts, $key, 'string' ) ) { return; } $value = $this->resolve_responsive_scalar_style_value( $atts, $key, $device, 'string' ); $value = $keyword ? $this->sanitize_css_keyword( $value ) : trim( (string) $value ); $this->append_css_rule_if_string( $rules, $property, $value, $important ); } protected function append_resolved_number_rule( array &$rules, $atts, $key, $property, $device = 'desktop', $unit = 'px', $min = 0, $max = null, $important = false, $responsive_only = false ) { if ( $responsive_only && ! $this->has_responsive_scalar_style_value( $atts, $key, 'number' ) ) { return; } $value = $this->resolve_responsive_scalar_style_value( $atts, $key, $device, 'number' ); $this->append_css_rule_if_number( $rules, $property, $value, $unit, $min, $max, $important ); } protected function build_button_style( $atts, $device = 'desktop' ) { $styles = array(); $styles[] = 'box-sizing:border-box;'; $styles[] = 'box-shadow:none;'; $styles[] = 'font-family:inherit;'; $styles[] = 'font-size:16px;'; $styles[] = 'font-weight:400;'; $styles[] = 'line-height:1.2;'; $styles[] = 'text-align:center;'; $styles[] = 'text-decoration:none;'; $styles[] = 'appearance:none;'; $submit_bg = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBg', $device, 'string' ) ); if ( $submit_bg !== '' ) { $styles[] = 'background:' . stacklar_sanitize_css_value( $submit_bg ) . ';'; } $submit_color = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitColor', $device, 'string' ) ); if ( $submit_color !== '' ) { $styles[] = 'color:' . stacklar_sanitize_css_value( $submit_color ) . ';'; } $border_width_value = $this->resolve_responsive_scalar_style_value( $atts, 'submitBorderWidth', $device, 'number' ); $border_width = $border_width_value !== null ? max( 0, floatval( $border_width_value ) ) : 1; $border_style = preg_replace( '/[^a-z-]/i', '', (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBorderStyle', $device, 'string' ) ); $border_style = $border_style !== '' ? $border_style : 'solid'; $border_color = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBorder', $device, 'string' ) ); $border_color = stacklar_sanitize_css_value( $border_color ); $border_color = $border_color !== '' ? $border_color : 'transparent'; $styles[] = 'border:' . $border_width . 'px ' . ( $border_style ?: 'solid' ) . ' ' . $border_color . ';'; $radius_value = $this->resolve_responsive_scalar_style_value( $atts, 'submitRadius', $device, 'number' ); $radius = $radius_value !== null ? floatval( $radius_value ) : 6; $styles[] = 'border-radius:' . max( 0, $radius ) . 'px;'; $padding_css = $this->resolve_padding_css( $atts, $device ); if ( $padding_css ) { $styles[] = 'padding:' . $padding_css . ';'; } $align = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitAlign', $device, 'string' ) ); if ( $align === 'full' ) { $styles[] = 'width:100%;'; $styles[] = 'display:inline-block;'; $styles[] = 'text-align:center;'; } $margin_css = $this->resolve_box_css( isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null, $device ); if ( $margin_css ) { $styles[] = 'margin:' . $margin_css . ';'; } $typography_styles = $this->build_typography_declarations( isset( $atts['submitTypography'] ) ? $atts['submitTypography'] : null, 'attr', $device ); if ( ! empty( $typography_styles ) ) { $styles = array_merge( $styles, $typography_styles ); } return $styles; } protected function build_submit_button_responsive_rules( $atts, $device = 'desktop' ) { $rules = array(); $this->append_resolved_string_rule( $rules, $atts, 'submitBg', 'background', $device, false, false, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitColor', 'color', $device, false, false, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitBorder', 'border-color', $device, false, false, true ); $this->append_resolved_number_rule( $rules, $atts, 'submitBorderWidth', 'border-width', $device, 'px', 0, null, false, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitBorderStyle', 'border-style', $device, false, true, true ); $this->append_resolved_number_rule( $rules, $atts, 'submitRadius', 'border-radius', $device, 'px', 0, null, false, true ); if ( $this->is_responsive_style_value( isset( $atts['submitPadding'] ) ? $atts['submitPadding'] : null ) ) { $padding_css = $this->resolve_padding_css( $atts, $device ); if ( $padding_css ) { $this->append_css_rule_if_string( $rules, 'padding', $padding_css ); } } if ( $this->is_responsive_style_value( isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null ) ) { $margin_css = $this->resolve_box_css( isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null, $device ); if ( $margin_css ) { $this->append_css_rule_if_string( $rules, 'margin', $margin_css ); } } if ( $this->is_responsive_style_value( isset( $atts['submitTypography'] ) ? $atts['submitTypography'] : null ) ) { $rules = array_merge( $rules, $this->build_typography_declarations( isset( $atts['submitTypography'] ) ? $atts['submitTypography'] : null, 'css', $device ) ); } if ( $this->has_responsive_scalar_style_value( $atts, 'submitAlign', 'string' ) ) { $align = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitAlign', $device, 'string' ) ); if ( $align === 'full' ) { $rules[] = 'width:100%;'; } else { $rules[] = 'width:auto;'; } $rules[] = 'display:inline-block;'; $rules[] = 'text-align:center;'; } return $this->append_important_rules( $rules ); } protected function build_submit_wrap_responsive_rules( $atts, $device = 'desktop' ) { if ( ! $this->is_responsive_style_value( isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null ) && ! $this->has_responsive_scalar_style_value( $atts, 'submitAlign', 'string' ) ) { return array(); } $styles = $this->build_wrap_style( $this->resolve_responsive_scalar_style_value( $atts, 'submitAlign', $device, 'string' ), isset( $atts['submitMargin'] ) ? $atts['submitMargin'] : null, $device, true ); return $this->append_important_rules( $styles ); } protected function build_hover_rules( $atts, $device = 'desktop' ) { $rules = array(); $this->append_resolved_string_rule( $rules, $atts, 'submitHoverBg', 'background', $device, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitHoverColor', 'color', $device, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitHoverBorder', 'border-color', $device, true ); $this->append_resolved_number_rule( $rules, $atts, 'submitHoverBorderWidth', 'border-width', $device, 'px', 0, null, true ); $this->append_resolved_string_rule( $rules, $atts, 'submitHoverBorderStyle', 'border-style', $device, true, true ); $this->append_resolved_number_rule( $rules, $atts, 'submitHoverOpacity', 'opacity', $device, '', 0, 1, true ); return $rules; } protected function build_hover_css( $atts, $form_id ) { $rules = stacklar_sanitize_css_declarations( $this->build_hover_rules( $atts, 'desktop' ) ); if ( empty( $rules ) ) { return ''; } $scope = $form_id ? '.stacklar--' . $form_id . ' .stacklar-submit' : '.stacklar-submit'; return $scope . ':hover{' . implode( '', $rules ) . '}'; } protected function build_focus_rules( $atts, $device = 'desktop' ) { $color = stacklar_sanitize_css_value( $this->resolve_responsive_scalar_style_value( $atts, 'submitFocusColor', $device, 'string' ) ); if ( $color === '' ) { return array(); } $width_value = $this->resolve_responsive_scalar_style_value( $atts, 'submitFocusWidth', $device, 'number' ); $width = $width_value !== null ? max( 0, min( 8, floatval( $width_value ) ) ) : 2; return array( 'outline:' . $width . 'px solid ' . $color . ' !important;', 'outline-offset:2px;' ); } protected function build_focus_css( $atts, $form_id ) { $rules = stacklar_sanitize_css_declarations( $this->build_focus_rules( $atts, 'desktop' ) ); if ( empty( $rules ) ) { return ''; } $scope = $form_id ? '.stacklar--' . $form_id . ' .stacklar-submit' : '.stacklar-submit'; return $scope . ':focus,' . $scope . ':focus-visible{' . implode( '', $rules ) . '}'; } protected function build_multistep_css( $atts, $form_id ) { if ( ! $form_id ) { return ''; } $bg = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBg', 'desktop', 'string' ) ); $bg = $bg !== '' ? $bg : '#111111'; $color = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitColor', 'desktop', 'string' ) ); $color = $color !== '' ? $color : '#ffffff'; $border = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBorder', 'desktop', 'string' ) ); $border = $border !== '' ? $border : $bg; $border_width_value = $this->resolve_responsive_scalar_style_value( $atts, 'submitBorderWidth', 'desktop', 'number' ); $border_width = $border_width_value !== null ? max( 0, floatval( $border_width_value ) ) : 1; $border_style = preg_replace( '/[^a-z-]/i', '', (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitBorderStyle', 'desktop', 'string' ) ); $border_style = $border_style !== '' ? $border_style : 'solid'; $hover_bg = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitHoverBg', 'desktop', 'string' ) ); $hover_bg = $hover_bg !== '' ? $hover_bg : $bg; $hover_color = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitHoverColor', 'desktop', 'string' ) ); $hover_color = $hover_color !== '' ? $hover_color : $color; $hover_border = trim( (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitHoverBorder', 'desktop', 'string' ) ); $hover_border = $hover_border !== '' ? $hover_border : $border; $hover_bw_attr = $this->resolve_responsive_scalar_style_value( $atts, 'submitHoverBorderWidth', 'desktop', 'number' ); $hover_bw = $hover_bw_attr !== null && $hover_bw_attr !== '' ? max( 0, floatval( $hover_bw_attr ) ) : $border_width; $hover_bs = preg_replace( '/[^a-z-]/i', '', (string) $this->resolve_responsive_scalar_style_value( $atts, 'submitHoverBorderStyle', 'desktop', 'string' ) ); $hover_bs = $hover_bs !== '' ? $hover_bs : $border_style; return $this->build_selector_rule_block( '.stacklar--' . $form_id, array( '--stacklar-btn-bg:' . $bg . ';', '--stacklar-btn-color:' . $color . ';', '--stacklar-btn-border:' . $border . ';', '--stacklar-btn-border-width:' . $border_width . 'px;', '--stacklar-btn-border-style:' . ( $border_style ?: 'solid' ) . ';', '--stacklar-btn-bg-hover:' . $hover_bg . ';', '--stacklar-btn-color-hover:' . $hover_color . ';', '--stacklar-btn-border-hover:' . $hover_border . ';', '--stacklar-btn-border-width-hover:' . $hover_bw . 'px;', '--stacklar-btn-border-style-hover:' . ( $hover_bs ?: $border_style ?: 'solid' ) . ';', ) ); } protected function build_message_css( $atts, $form_id ) { if ( ! $form_id ) { return ''; } $scope = '.stacklar--' . $form_id . ' .stacklar-message'; $css = ''; $success = stacklar_sanitize_css_declarations( $this->build_message_rules( $atts, 'success' ) ); if ( ! empty( $success ) ) { $css .= $scope . '.is-success{' . implode( '', $success ) . '}'; } $error = stacklar_sanitize_css_declarations( $this->build_message_rules( $atts, 'error' ) ); if ( ! empty( $error ) ) { $css .= $scope . '.is-error{' . implode( '', $error ) . '}'; } return $css; } protected function form_has_step_blocks( $post_id ) { static $cache = array(); $post_id = absint( $post_id ); if ( ! $post_id ) { return false; } if ( array_key_exists( $post_id, $cache ) ) { return (bool) $cache[ $post_id ]; } $post = get_post( $post_id ); if ( ! $post || ! is_string( $post->post_content ) || '' === $post->post_content ) { $cache[ $post_id ] = false; return false; } if ( false === strpos( $post->post_content, 'stacklar/form-step' ) ) { $cache[ $post_id ] = false; return false; } $blocks = parse_blocks( $post->post_content ); $cache[ $post_id ] = $this->blocks_contain_step_block( $blocks ); return (bool) $cache[ $post_id ]; } protected function blocks_contain_step_block( $blocks ) { if ( empty( $blocks ) || ! is_array( $blocks ) ) { return false; } foreach ( $blocks as $block ) { if ( empty( $block ) || ! is_array( $block ) ) { continue; } if ( ! empty( $block['blockName'] ) && 'stacklar/form-step' === $block['blockName'] ) { return true; } if ( ! empty( $block['innerBlocks'] ) && $this->blocks_contain_step_block( $block['innerBlocks'] ) ) { return true; } } return false; } protected function build_message_rules( $atts, $type = 'success', $device = 'desktop' ) { $prefix = $type === 'error' ? 'messageError' : 'messageSuccess'; $rules = array(); $padding_key = $prefix . 'Padding'; $typo_key = $prefix . 'Typography'; $this->append_resolved_string_rule( $rules, $atts, $prefix . 'Bg', 'background', $device ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'Color', 'color', $device ); $this->append_resolved_number_rule( $rules, $atts, $prefix . 'BorderWidth', 'border-width', $device ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'BorderStyle', 'border-style', $device, false, true ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'BorderColor', 'border-color', $device ); $this->append_resolved_number_rule( $rules, $atts, $prefix . 'BorderRadius', 'border-radius', $device ); if ( ! empty( $atts[ $padding_key ] ) && is_array( $atts[ $padding_key ] ) ) { $padding = $this->resolve_box_css( $atts[ $padding_key ], $device ); if ( $padding !== '' ) { $this->append_css_rule_if_string( $rules, 'padding', $padding ); } } if ( ! empty( $atts[ $typo_key ] ) ) { $rules = array_merge( $rules, $this->build_typography_declarations( $atts[ $typo_key ], 'css', $device ) ); } return $rules; } protected function build_message_responsive_rules( $atts, $type = 'success', $device = 'desktop' ) { $prefix = $type === 'error' ? 'messageError' : 'messageSuccess'; $rules = array(); $padding_key = $prefix . 'Padding'; $typo_key = $prefix . 'Typography'; $this->append_resolved_string_rule( $rules, $atts, $prefix . 'Bg', 'background', $device, false, false, true ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'Color', 'color', $device, false, false, true ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'BorderColor', 'border-color', $device, false, false, true ); $this->append_resolved_number_rule( $rules, $atts, $prefix . 'BorderWidth', 'border-width', $device, 'px', 0, null, false, true ); $this->append_resolved_string_rule( $rules, $atts, $prefix . 'BorderStyle', 'border-style', $device, false, true, true ); $this->append_resolved_number_rule( $rules, $atts, $prefix . 'BorderRadius', 'border-radius', $device, 'px', 0, null, false, true ); if ( $this->is_responsive_style_value( isset( $atts[ $padding_key ] ) ? $atts[ $padding_key ] : null ) ) { $padding = $this->resolve_box_css( isset( $atts[ $padding_key ] ) ? $atts[ $padding_key ] : null, $device ); if ( $padding !== '' ) { $this->append_css_rule_if_string( $rules, 'padding', $padding ); } } if ( $this->is_responsive_style_value( isset( $atts[ $typo_key ] ) ? $atts[ $typo_key ] : null ) ) { $rules = array_merge( $rules, $this->build_typography_declarations( isset( $atts[ $typo_key ] ) ? $atts[ $typo_key ] : null, 'css', $device ) ); } return $this->append_important_rules( $rules ); } protected function build_submit_responsive_css( $atts, $instance_selector ) { $instance_selector = trim( (string) $instance_selector ); if ( '' === $instance_selector ) { return ''; } $css = ''; $button_selector = $instance_selector . ' .stacklar-submit'; $button_css = $this->build_responsive_selector_css( $button_selector, array( 'tablet' => $this->build_submit_button_responsive_rules( $atts, 'tablet' ), 'mobile' => $this->build_submit_button_responsive_rules( $atts, 'mobile' ), ) ); if ( $button_css ) { $css .= $button_css; } $wrap_selector = $instance_selector . ' .stacklar-submit-wrap'; $wrap_css = $this->build_responsive_selector_css( $wrap_selector, array( 'tablet' => $this->build_submit_wrap_responsive_rules( $atts, 'tablet' ), 'mobile' => $this->build_submit_wrap_responsive_rules( $atts, 'mobile' ), ) ); if ( $wrap_css ) { $css .= $wrap_css; } $has_responsive_hover = $this->has_responsive_scalar_style_value( $atts, 'submitHoverBg', 'string' ) || $this->has_responsive_scalar_style_value( $atts, 'submitHoverColor', 'string' ) || $this->has_responsive_scalar_style_value( $atts, 'submitHoverBorder', 'string' ) || $this->has_responsive_scalar_style_value( $atts, 'submitHoverBorderWidth', 'number' ) || $this->has_responsive_scalar_style_value( $atts, 'submitHoverBorderStyle', 'string' ) || $this->has_responsive_scalar_style_value( $atts, 'submitHoverOpacity', 'number' ); if ( $has_responsive_hover ) { $hover_css = $this->build_responsive_selector_css( $button_selector . ':hover', array( 'tablet' => $this->build_hover_rules( $atts, 'tablet' ), 'mobile' => $this->build_hover_rules( $atts, 'mobile' ), ) ); if ( $hover_css ) { $css .= $hover_css; } } $has_responsive_focus = $this->has_responsive_scalar_style_value( $atts, 'submitFocusColor', 'string' ) || $this->has_responsive_scalar_style_value( $atts, 'submitFocusWidth', 'number' ); if ( $has_responsive_focus ) { $focus_selector = $button_selector . ':focus, ' . $button_selector . ':focus-visible'; $focus_css = $this->build_responsive_selector_css( $focus_selector, array( 'tablet' => $this->build_focus_rules( $atts, 'tablet' ), 'mobile' => $this->build_focus_rules( $atts, 'mobile' ), ) ); if ( $focus_css ) { $css .= $focus_css; } } $success_selector = $instance_selector . ' .stacklar-message.is-success'; $success_css = $this->build_responsive_selector_css( $success_selector, array( 'tablet' => $this->build_message_responsive_rules( $atts, 'success', 'tablet' ), 'mobile' => $this->build_message_responsive_rules( $atts, 'success', 'mobile' ), ) ); if ( $success_css ) { $css .= $success_css; } $error_selector = $instance_selector . ' .stacklar-message.is-error'; $error_css = $this->build_responsive_selector_css( $error_selector, array( 'tablet' => $this->build_message_responsive_rules( $atts, 'error', 'tablet' ), 'mobile' => $this->build_message_responsive_rules( $atts, 'error', 'mobile' ), ) ); if ( $error_css ) { $css .= $error_css; } return $css; } } }