PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 All 98 releases
← All changes | inc/page-builders/elementor/form-widget.php +767 -12 0.0.13 → 2.12.8 View file →
@@ -7,10 +7,12 @@
7 7 */
8 8
9 9 namespace SRFM\Inc\Page_Builders\Elementor;
10 10
11 +use Elementor\Plugin;
11 12 use Elementor\Widget_Base;
12 -use Elementor\Plugin;
13 +use Spec_Gb_Helper;
14 +use SRFM\Inc\Generate_Form_Markup;
13 15 use SRFM\Inc\Helper;
14 16 use SRFM\Inc\Page_Builders\Page_Builders;
15 17
16 18 if ( ! defined( 'ABSPATH' ) ) {
@@ -17,13 +19,11 @@
17 19 exit; // Exit if accessed directly.
18 20 }
19 21
20 22 /**
21 - *
22 23 * SureForms widget that displays a form.
23 24 */
24 25 class Form_Widget extends Widget_Base {
25 -
26 26 /**
27 27 * Whether we are in the preview mode.
28 28 *
29 29 * @var bool
@@ -52,12 +52,34 @@
52 52 wp_localize_script(
53 53 'srfm-elementor-preview',
54 54 'srfmElementorData',
55 55 [
56 - 'isProActive' => defined( 'SRFM_PRO_VER' ),
56 + 'isProActive' => Helper::has_pro(),
57 57 ]
58 58 );
59 59
60 + // Register styling preview script for live preview.
61 + wp_register_script(
62 + 'srfm-elementor-preview-styling',
63 + SRFM_URL . 'assets/build/elementorPreviewStyling.js',
64 + [ 'elementor-frontend' ],
65 + SRFM_VER,
66 + true
67 + );
68 + wp_localize_script(
69 + 'srfm-elementor-preview-styling',
70 + 'srfmElementorStyling',
71 + [
72 + 'fieldSpacingVars' => Helper::get_css_vars(),
73 + ]
74 + );
75 +
76 + /**
77 + * Hook for Pro to register additional preview assets (scripts and styles).
78 + *
79 + * @since 2.7.0
80 + */
81 + do_action( 'srfm_elementor_register_preview_assets' );
60 82 }
61 83 }
62 84
63 85 /**
@@ -67,9 +89,18 @@
67 89 * @return array<string> Script dependencies.
68 90 */
69 91 public function get_script_depends() {
70 92 if ( $this->is_preview_mode ) {
71 - return [ 'srfm-elementor-preview' ];
93 + $scripts = [ 'srfm-elementor-preview', 'srfm-elementor-preview-styling' ];
94 +
95 + /**
96 + * Filter the widget's script dependencies.
97 + * Pro uses this to add its preview scripts.
98 + *
99 + * @param array<string> $scripts Script handles.
100 + * @since 2.7.0
101 + */
102 + return apply_filters( 'srfm_elementor_widget_script_depends', $scripts );
72 103 }
73 104
74 105 return [];
75 106 }
@@ -74,8 +105,27 @@
74 105 return [];
75 106 }
76 107
77 108 /**
109 + * Get style depends.
110 + *
111 + * @since 2.7.0
112 + * @return array<string> Style dependencies.
113 + */
114 + public function get_style_depends() {
115 + $styles = [];
116 +
117 + /**
118 + * Filter the widget's style dependencies.
119 + * Pro uses this to add its custom-styles CSS.
120 + *
121 + * @param array<string> $styles Style handles.
122 + * @since 2.7.0
123 + */
124 + return apply_filters( 'srfm_elementor_widget_style_depends', $styles );
125 + }
126 +
127 + /**
78 128 * Get widget name.
79 129 *
80 130 * @since 0.0.5
81 131 * @return string Widget name.
@@ -129,8 +179,115 @@
129 179 ];
130 180 }
131 181
132 182 /**
183 + * Static method to get color value from settings.
184 + * Can be used by Pro plugin to resolve global colors.
185 + *
186 + * @param array<string, mixed> $settings Widget settings.
187 + * @param string $control_key The control key.
188 + * @return string|null The color value or null if not set.
189 + * @since 2.7.0
190 + */
191 + public static function get_resolved_color( $settings, $control_key ) {
192 + // First check for a global color reference.
193 + $globals = isset( $settings['__globals__'] ) && is_array( $settings['__globals__'] ) ? $settings['__globals__'] : [];
194 + if ( ! empty( $globals[ $control_key ] ) ) {
195 + $global_key = is_string( $globals[ $control_key ] ) ? $globals[ $control_key ] : '';
196 +
197 + // Use Elementor's data manager to resolve the global value.
198 + if ( $global_key && class_exists( '\Elementor\Plugin' ) && isset( Plugin::$instance->data_manager_v2 ) ) {
199 + $data = Plugin::$instance->data_manager_v2->run( $global_key );
200 +
201 + if ( is_array( $data ) && ! empty( $data['value'] ) && is_string( $data['value'] ) ) {
202 + return $data['value'];
203 + }
204 + }
205 + }
206 +
207 + // Fall back to direct value.
208 + if ( isset( $settings[ $control_key ] ) && '' !== $settings[ $control_key ] && 'default' !== $settings[ $control_key ] ) {
209 + $value = $settings[ $control_key ];
210 + return is_string( $value ) ? $value : null;
211 + }
212 +
213 + return null;
214 + }
215 +
216 + /**
217 + * Build CSS gradient string from Group_Control_Background settings.
218 + *
219 + * Constructs a CSS gradient value from Elementor's gradient control settings.
220 + * Supports both linear and radial gradients. Radial gradients use 'center center' position.
221 + *
222 + * @param array<string, mixed> $settings Widget settings containing gradient values.
223 + * @return string|null CSS gradient string or null if colors not set.
224 + * @since 2.7.0
225 + */
226 + public static function build_gradient_css( $settings ) {
227 + // Get gradient colors - resolve global colors if used.
228 + $color_1 = self::get_resolved_color( $settings, 'bgGradient_color' );
229 + $color_2 = self::get_resolved_color( $settings, 'bgGradient_color_b' );
230 +
231 + // Both colors are required.
232 + if ( ! $color_1 || ! $color_2 ) {
233 + return null;
234 + }
235 +
236 + // Get gradient settings with defaults - use Helper::get_string_value for type safety.
237 + $type = isset( $settings['bgGradient_gradient_type'] ) && '' !== $settings['bgGradient_gradient_type']
238 + ? Helper::get_string_value( $settings['bgGradient_gradient_type'] )
239 + : 'linear';
240 +
241 + // Get angle settings.
242 + $angle_settings = isset( $settings['bgGradient_gradient_angle'] ) && is_array( $settings['bgGradient_gradient_angle'] )
243 + ? $settings['bgGradient_gradient_angle']
244 + : [];
245 + $angle = isset( $angle_settings['size'] ) ? Helper::get_string_value( $angle_settings['size'] ) : '180';
246 + $angle_unit = isset( $angle_settings['unit'] ) ? Helper::get_string_value( $angle_settings['unit'] ) : 'deg';
247 +
248 + // Get color 1 stop settings.
249 + $color_1_settings = isset( $settings['bgGradient_color_stop'] ) && is_array( $settings['bgGradient_color_stop'] )
250 + ? $settings['bgGradient_color_stop']
251 + : [];
252 + $color_1_stop = isset( $color_1_settings['size'] ) ? Helper::get_string_value( $color_1_settings['size'] ) : '0';
253 + $color_1_unit = isset( $color_1_settings['unit'] ) ? Helper::get_string_value( $color_1_settings['unit'] ) : '%';
254 +
255 + // Get color 2 stop settings.
256 + $color_2_settings = isset( $settings['bgGradient_color_b_stop'] ) && is_array( $settings['bgGradient_color_b_stop'] )
257 + ? $settings['bgGradient_color_b_stop']
258 + : [];
259 + $color_2_stop = isset( $color_2_settings['size'] ) ? Helper::get_string_value( $color_2_settings['size'] ) : '100';
260 + $color_2_unit = isset( $color_2_settings['unit'] ) ? Helper::get_string_value( $color_2_settings['unit'] ) : '%';
261 +
262 + // Build radial gradient: radial-gradient(at center center, color1 stop1, color2 stop2).
263 + if ( 'radial' === $type ) {
264 + return sprintf(
265 + 'radial-gradient(at center center, %s %s%s, %s %s%s)',
266 + esc_attr( $color_1 ),
267 + esc_attr( $color_1_stop ),
268 + esc_attr( $color_1_unit ),
269 + esc_attr( $color_2 ),
270 + esc_attr( $color_2_stop ),
271 + esc_attr( $color_2_unit )
272 + );
273 + }
274 +
275 + // Build linear gradient: linear-gradient(angle, color1 stop1, color2 stop2).
276 + return sprintf(
277 + 'linear-gradient(%s%s, %s %s%s, %s %s%s)',
278 + esc_attr( $angle ),
279 + esc_attr( $angle_unit ),
280 + esc_attr( $color_1 ),
281 + esc_attr( $color_1_stop ),
282 + esc_attr( $color_1_unit ),
283 + esc_attr( $color_2 ),
284 + esc_attr( $color_2_stop ),
285 + esc_attr( $color_2_unit )
286 + );
287 + }
288 +
289 + /**
133 290 * Register form widget controls.
134 291 * Adds different input fields to allow the user to change and customize the widget settings.
135 292 *
136 293 * @since 0.0.5
@@ -204,8 +361,439 @@
204 361 ]
205 362 );
206 363
207 364 $this->end_controls_section();
365 +
366 + // Style Tab - Form Styling Section.
367 + $this->start_controls_section(
368 + 'srfm_style_section',
369 + [
370 + 'label' => __( 'Form Styling', 'sureforms' ),
371 + 'tab' => \Elementor\Controls_Manager::TAB_STYLE,
372 + 'condition' => [
373 + 'srfm_form_block!' => '',
374 + ],
375 + ]
376 + );
377 +
378 + // Form Theme Select.
379 + $this->add_control(
380 + 'formTheme',
381 + [
382 + 'label' => __( 'Form Theme', 'sureforms' ),
383 + 'type' => \Elementor\Controls_Manager::SELECT,
384 + 'default' => 'inherit',
385 + 'options' => [
386 + 'inherit' => __( "Inherit Form's Original Style", 'sureforms' ),
387 + 'default' => __( 'Default', 'sureforms' ),
388 + ],
389 + 'separator' => 'after',
390 + ]
391 + );
392 +
393 + // Primary Color.
394 + $this->add_control(
395 + 'primaryColor',
396 + [
397 + 'label' => __( 'Primary Color', 'sureforms' ),
398 + 'type' => \Elementor\Controls_Manager::COLOR,
399 + 'condition' => [
400 + 'formTheme!' => 'inherit',
401 + ],
402 + ]
403 + );
404 +
405 + // Text Color.
406 + $this->add_control(
407 + 'textColor',
408 + [
409 + 'label' => __( 'Text Color', 'sureforms' ),
410 + 'type' => \Elementor\Controls_Manager::COLOR,
411 + 'condition' => [
412 + 'formTheme!' => 'inherit',
413 + ],
414 + ]
415 + );
416 +
417 + // Text on Primary (button text color).
418 + $this->add_control(
419 + 'textOnPrimaryColor',
420 + [
421 + 'label' => __( 'Text on Primary', 'sureforms' ),
422 + 'type' => \Elementor\Controls_Manager::COLOR,
423 + 'condition' => [
424 + 'formTheme!' => 'inherit',
425 + ],
426 + ]
427 + );
428 +
429 + // Background Type Heading.
430 + $this->add_control(
431 + 'bgHeading',
432 + [
433 + 'label' => __( 'Background', 'sureforms' ),
434 + 'type' => \Elementor\Controls_Manager::HEADING,
435 + 'condition' => [
436 + 'formTheme!' => 'inherit',
437 + ],
438 + ]
439 + );
440 +
441 + // Background Type.
442 + $this->add_control(
443 + 'bgType',
444 + [
445 + 'label' => __( 'Type', 'sureforms' ),
446 + 'type' => \Elementor\Controls_Manager::SELECT,
447 + 'default' => 'color',
448 + 'options' => [
449 + 'color' => __( 'Color', 'sureforms' ),
450 + 'gradient' => __( 'Gradient', 'sureforms' ),
451 + 'image' => __( 'Image', 'sureforms' ),
452 + ],
453 + 'condition' => [
454 + 'formTheme!' => 'inherit',
455 + ],
456 + ]
457 + );
458 +
459 + // Background Color.
460 + $this->add_control(
461 + 'bgColor',
462 + [
463 + 'label' => __( 'Color', 'sureforms' ),
464 + 'type' => \Elementor\Controls_Manager::COLOR,
465 + 'default' => '#FFFFFF',
466 + 'condition' => [
467 + 'formTheme!' => 'inherit',
468 + 'bgType' => 'color',
469 + ],
470 + ]
471 + );
472 +
473 + // Background Gradient - using Group_Control_Background for visual gradient picker.
474 + // Position control is hidden - radial gradients use 'center center' position by default.
475 + $this->add_group_control(
476 + \Elementor\Group_Control_Background::get_type(),
477 + [
478 + 'name' => 'bgGradient',
479 + 'types' => [ 'gradient' ],
480 + 'exclude' => [ 'image' ], // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- Elementor Group_Control_Background option, not a query argument.
481 + 'fields_options' => [
482 + 'background' => [
483 + 'label' => __( 'Gradient', 'sureforms' ),
484 + 'default' => 'gradient',
485 + ],
486 + 'color' => [
487 + 'label' => __( 'Color 1', 'sureforms' ),
488 + 'default' => '#FFC9B2',
489 + ],
490 + 'color_stop' => [
491 + 'label' => __( 'Location 1', 'sureforms' ),
492 + 'size_units' => [ '%' ],
493 + 'responsive' => false,
494 + 'description' => '',
495 + ],
496 + 'color_b' => [
497 + 'label' => __( 'Color 2', 'sureforms' ),
498 + 'default' => '#C7CBFF',
499 + ],
500 + 'color_b_stop' => [
501 + 'label' => __( 'Location 2', 'sureforms' ),
502 + 'size_units' => [ '%' ],
503 + 'responsive' => false,
504 + 'description' => '',
505 + ],
506 + 'gradient_type' => [],
507 + 'gradient_angle' => [
508 + 'label' => __( 'Angle', 'sureforms' ),
509 + 'size_units' => [ 'deg' ],
510 + 'responsive' => false,
511 + 'description' => '',
512 + ],
513 + // Hide position control - use 'center center' as default for radial gradients.
514 + 'gradient_position' => [
515 + 'default' => 'center center',
516 + 'type' => \Elementor\Controls_Manager::HIDDEN,
517 + ],
518 + ],
519 + 'selector' => '{{WRAPPER}} .srfm-gradient-dummy-selector', // Dummy selector - we handle styling via CSS variables.
520 + 'condition' => [
521 + 'formTheme!' => 'inherit',
522 + 'bgType' => 'gradient',
523 + ],
524 + ]
525 + );
526 +
527 + // Background Image.
528 + $this->add_control(
529 + 'bgImage',
530 + [
531 + 'label' => __( 'Image', 'sureforms' ),
532 + 'type' => \Elementor\Controls_Manager::MEDIA,
533 + 'default' => [
534 + 'url' => '',
535 + ],
536 + 'condition' => [
537 + 'formTheme!' => 'inherit',
538 + 'bgType' => 'image',
539 + ],
540 + ]
541 + );
542 +
543 + // Background Image Size.
544 + $this->add_control(
545 + 'bgImageSize',
546 + [
547 + 'label' => __( 'Size', 'sureforms' ),
548 + 'type' => \Elementor\Controls_Manager::SELECT,
549 + 'default' => 'cover',
550 + 'options' => [
551 + 'cover' => __( 'Cover', 'sureforms' ),
552 + 'contain' => __( 'Contain', 'sureforms' ),
553 + 'auto' => __( 'Auto', 'sureforms' ),
554 + ],
555 + 'condition' => [
556 + 'formTheme!' => 'inherit',
557 + 'bgType' => 'image',
558 + ],
559 + ]
560 + );
561 +
562 + // Background Image Position.
563 + $this->add_control(
564 + 'bgImagePosition',
565 + [
566 + 'label' => __( 'Position', 'sureforms' ),
567 + 'type' => \Elementor\Controls_Manager::SELECT,
568 + 'default' => 'center center',
569 + 'options' => [
570 + 'left top' => __( 'Left Top', 'sureforms' ),
571 + 'left center' => __( 'Left Center', 'sureforms' ),
572 + 'left bottom' => __( 'Left Bottom', 'sureforms' ),
573 + 'center top' => __( 'Center Top', 'sureforms' ),
574 + 'center center' => __( 'Center Center', 'sureforms' ),
575 + 'center bottom' => __( 'Center Bottom', 'sureforms' ),
576 + 'right top' => __( 'Right Top', 'sureforms' ),
577 + 'right center' => __( 'Right Center', 'sureforms' ),
578 + 'right bottom' => __( 'Right Bottom', 'sureforms' ),
579 + ],
580 + 'condition' => [
581 + 'formTheme!' => 'inherit',
582 + 'bgType' => 'image',
583 + ],
584 + ]
585 + );
586 +
587 + // Background Image Repeat.
588 + $this->add_control(
589 + 'bgImageRepeat',
590 + [
591 + 'label' => __( 'Repeat', 'sureforms' ),
592 + 'type' => \Elementor\Controls_Manager::SELECT,
593 + 'default' => 'no-repeat',
594 + 'options' => [
595 + 'no-repeat' => __( 'No Repeat', 'sureforms' ),
596 + 'repeat' => __( 'Repeat', 'sureforms' ),
597 + 'repeat-x' => __( 'Repeat X', 'sureforms' ),
598 + 'repeat-y' => __( 'Repeat Y', 'sureforms' ),
599 + ],
600 + 'condition' => [
601 + 'formTheme!' => 'inherit',
602 + 'bgType' => 'image',
603 + ],
604 + ]
605 + );
606 +
607 + // Background Image Attachment.
608 + $this->add_control(
609 + 'bgImageAttachment',
610 + [
611 + 'label' => __( 'Attachment', 'sureforms' ),
612 + 'type' => \Elementor\Controls_Manager::SELECT,
613 + 'default' => 'scroll',
614 + 'options' => [
615 + 'scroll' => __( 'Scroll', 'sureforms' ),
616 + 'fixed' => __( 'Fixed', 'sureforms' ),
617 + ],
618 + 'condition' => [
619 + 'formTheme!' => 'inherit',
620 + 'bgType' => 'image',
621 + ],
622 + ]
623 + );
624 +
625 + /**
626 + * Hook for Pro to add advanced styling controls.
627 + *
628 + * @param \Elementor\Widget_Base $widget The widget instance.
629 + * @since 2.7.0
630 + */
631 + do_action( 'srfm_elementor_after_basic_styling_controls', $this );
632 +
633 + $this->end_controls_section();
634 +
635 + // Style Tab - Layout Section.
636 + $this->start_controls_section(
637 + 'srfm_layout_section',
638 + [
639 + 'label' => __( 'Layout', 'sureforms' ),
640 + 'tab' => \Elementor\Controls_Manager::TAB_STYLE,
641 + 'condition' => [
642 + 'srfm_form_block!' => '',
643 + 'formTheme!' => 'inherit',
644 + ],
645 + ]
646 + );
647 +
648 + // Form Padding.
649 + $this->add_control(
650 + 'formPadding',
651 + [
652 + 'label' => __( 'Form Padding', 'sureforms' ),
653 + 'type' => \Elementor\Controls_Manager::DIMENSIONS,
654 + 'size_units' => [ 'px', '%', 'em' ],
655 + 'default' => [
656 + 'top' => '0',
657 + 'right' => '0',
658 + 'bottom' => '0',
659 + 'left' => '0',
660 + 'unit' => 'px',
661 + 'isLinked' => true,
662 + ],
663 + 'separator' => 'after',
664 + ]
665 + );
666 +
667 + // Form Border Radius.
668 + $this->add_control(
669 + 'formBorderRadius',
670 + [
671 + 'label' => __( 'Form Border Radius', 'sureforms' ),
672 + 'type' => \Elementor\Controls_Manager::DIMENSIONS,
673 + 'size_units' => [ 'px', '%', 'em' ],
674 + 'default' => [
675 + 'top' => '0',
676 + 'right' => '0',
677 + 'bottom' => '0',
678 + 'left' => '0',
679 + 'unit' => 'px',
680 + 'isLinked' => true,
681 + ],
682 + ]
683 + );
684 +
685 + /**
686 + * Hook for Pro to add additional layout controls.
687 + *
688 + * @param \Elementor\Widget_Base $widget The widget instance.
689 + * @since 2.7.0
690 + */
691 + do_action( 'srfm_elementor_layout_controls', $this );
692 +
693 + $this->end_controls_section();
694 +
695 + // Style Tab - Button Section.
696 + $this->start_controls_section(
697 + 'srfm_button_section',
698 + [
699 + 'label' => __( 'Button', 'sureforms' ),
700 + 'tab' => \Elementor\Controls_Manager::TAB_STYLE,
701 + 'condition' => [
702 + 'srfm_form_block!' => '',
703 + 'formTheme!' => 'inherit',
704 + ],
705 + ]
706 + );
707 +
708 + // Button Alignment.
709 + $this->add_control(
710 + 'buttonAlignment',
711 + [
712 + 'label' => __( 'Alignment', 'sureforms' ),
713 + 'type' => \Elementor\Controls_Manager::CHOOSE,
714 + 'options' => [
715 + 'left' => [
716 + 'title' => __( 'Left', 'sureforms' ),
717 + 'icon' => 'eicon-text-align-left',
718 + ],
719 + 'center' => [
720 + 'title' => __( 'Center', 'sureforms' ),
721 + 'icon' => 'eicon-text-align-center',
722 + ],
723 + 'right' => [
724 + 'title' => __( 'Right', 'sureforms' ),
725 + 'icon' => 'eicon-text-align-right',
726 + ],
727 + 'justify' => [
728 + 'title' => __( 'Full Width', 'sureforms' ),
729 + 'icon' => 'eicon-text-align-justify',
730 + ],
731 + ],
732 + 'default' => '',
733 + 'toggle' => true,
734 + ]
735 + );
736 +
737 + /**
738 + * Hook for Pro to add additional button controls.
739 + *
740 + * @param \Elementor\Widget_Base $widget The widget instance.
741 + * @since 2.7.0
742 + */
743 + do_action( 'srfm_elementor_button_controls', $this );
744 +
745 + $this->end_controls_section();
746 +
747 + // Style Tab - Fields Section.
748 + $this->start_controls_section(
749 + 'srfm_field_section',
750 + [
751 + 'label' => __( 'Fields', 'sureforms' ),
752 + 'tab' => \Elementor\Controls_Manager::TAB_STYLE,
753 + 'condition' => [
754 + 'srfm_form_block!' => '',
755 + 'formTheme!' => 'inherit',
756 + ],
757 + ]
758 + );
759 +
760 + // Field Spacing.
761 + // Hidden when custom theme is selected (Pro provides Row Gap/Column Gap controls).
762 + $this->add_control(
763 + 'fieldSpacing',
764 + [
765 + 'label' => __( 'Field Spacing', 'sureforms' ),
766 + 'type' => \Elementor\Controls_Manager::SELECT,
767 + 'default' => 'medium',
768 + 'options' => [
769 + 'small' => __( 'Small', 'sureforms' ),
770 + 'medium' => __( 'Medium', 'sureforms' ),
771 + 'large' => __( 'Large', 'sureforms' ),
772 + ],
773 + 'condition' => [
774 + 'formTheme!' => 'custom',
775 + ],
776 + ]
777 + );
778 +
779 + /**
780 + * Hook for Pro to add additional field controls.
781 + *
782 + * @param \Elementor\Widget_Base $widget The widget instance.
783 + * @since 2.7.0
784 + */
785 + do_action( 'srfm_elementor_field_controls', $this );
786 +
787 + $this->end_controls_section();
788 +
789 + /**
790 + * Hook for Pro to add additional style sections.
791 + *
792 + * @param \Elementor\Widget_Base $widget The widget instance.
793 + * @since 2.7.0
794 + */
795 + do_action( 'srfm_elementor_after_styling_section', $this );
208 796 }
209 797
210 798 /**
211 799 * Render form widget output on the frontend.
@@ -220,21 +808,188 @@
220 808 return;
221 809 }
222 810
223 811 $is_editor = Plugin::instance()->editor->is_edit_mode();
812 + $form_id = intval( $settings['srfm_form_block'] ?? 0 );
224 813
225 - if ( $is_editor && isset( $settings['srfm_form_block'] ) && '' === $settings['srfm_form_block'] ) {
226 - echo '<div style="background: #D9DEE1; color: #9DA5AE; padding: 10px; font-family: Roboto, sans-serif">' .
227 - esc_html__( 'Select the form that you wish to add here.', 'sureforms' ) .
228 - '</div>';
814 + // Show placeholder in editor when no form selected.
815 + if ( $is_editor && empty( $form_id ) ) {
816 + ?>
817 + <div style="background: #D9DEE1; color: #9DA5AE; padding: 10px; font-family: Roboto, sans-serif">
818 + <?php echo esc_html__( 'Select the form that you wish to add here.', 'sureforms' ); ?>
819 + </div>
820 + <?php
229 821 return;
230 822 }
231 823
232 - if ( ! isset( $settings['srfm_show_form_title'] ) || ! isset( $settings['srfm_form_block'] ) ) {
824 + // Validation: Same as shortcode for backward compatibility.
825 + $form = get_post( $form_id );
826 + if ( empty( $form_id ) || ! $form || ! in_array( $form->post_status, [ 'publish', 'protected' ], true ) ) {
827 + echo esc_html__( 'This form has been deleted or is unavailable.', 'sureforms' );
233 828 return;
234 829 }
235 830
236 - $show_form_title = 'true' === $settings['srfm_show_form_title'];
237 - echo do_shortcode( '[sureforms id="' . $settings['srfm_form_block'] . '" show_title="' . ! $show_form_title . '"]' );
831 + $show_title = 'true' === ( $settings['srfm_show_form_title'] ?? '' );
832 +
833 + // Build block_attrs from widget settings.
834 + $block_attrs = $this->get_block_attrs( $settings );
835 +
836 + // Bypass shortcode - call get_form_markup() directly.
837 + // $do_blocks = true to match current shortcode behavior.
838 + $form_markup = Generate_Form_Markup::get_form_markup( $form_id, $show_title, '', 'post', true, $block_attrs );
839 + echo $form_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in Generate_Form_Markup.
840 +
841 + // Get spectra blocks and add css and js.
842 + $blocks = parse_blocks( get_post_field( 'post_content', $form_id ) );
843 + $styles = Spec_Gb_Helper::get_instance()->get_assets( $blocks );
844 + ?>
845 + <style><?php echo $styles['css']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></style>
846 + <script><?php echo $styles['js']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></script>
847 + <?php
848 + }
849 +
850 + /**
851 + * Convert widget settings to block_attrs array.
852 + * Uses same camelCase keys as Gutenberg for code reuse.
853 + *
854 + * @param array<string, mixed> $settings Widget settings.
855 + * @return array<string, mixed> Block attributes.
856 + * @since 2.7.0
857 + */
858 + protected function get_block_attrs( $settings ) {
859 + $block_attrs = [
860 + 'blockId' => 'elementor-' . $this->get_id(),
861 + ];
862 +
863 + // Check form theme — if inheriting, don't pass any custom styling attributes.
864 + $form_theme = $settings['formTheme'] ?? 'inherit';
865 + $block_attrs['formTheme'] = $form_theme;
866 +
867 + if ( 'inherit' === $form_theme ) {
868 + return $block_attrs;
869 + }
870 +
871 + // Color controls that support Elementor Global Colors.
872 + $color_keys = [
873 + 'primaryColor',
874 + 'textColor',
875 + 'textOnPrimaryColor',
876 + 'bgColor',
877 + ];
878 +
879 + foreach ( $color_keys as $key ) {
880 + $color_value = self::get_resolved_color( $settings, $key );
881 + if ( $color_value ) {
882 + $block_attrs[ $key ] = $color_value;
883 + }
884 + }
885 +
886 + // Non-color styling keys to pass through (camelCase).
887 + $styling_keys = [
888 + 'fieldSpacing',
889 + 'buttonAlignment',
890 + // Background (non-color).
891 + 'bgType',
892 + 'bgImageSize',
893 + 'bgImagePosition',
894 + 'bgImageRepeat',
895 + 'bgImageAttachment',
896 + ];
897 +
898 + foreach ( $styling_keys as $key ) {
899 + if ( isset( $settings[ $key ] ) && '' !== $settings[ $key ] && 'default' !== $settings[ $key ] ) {
900 + $block_attrs[ $key ] = $settings[ $key ];
901 + }
902 + }
903 +
904 + // Build gradient CSS string from Group_Control_Background settings.
905 + // Use get_settings() (raw) instead of get_settings_for_display() because Elementor
906 + // nullifies group control child fields when the parent 'background' type field
907 + // is not explicitly saved (it uses our default 'gradient' but doesn't persist it).
908 + if ( 'gradient' === ( $settings['bgType'] ?? '' ) ) {
909 + /** Raw widget settings.
910 + *
911 + * @var array<string, mixed> $raw_settings
912 + */
913 + $raw_settings = $this->get_settings();
914 + $gradient_css = self::build_gradient_css( $raw_settings );
915 + if ( $gradient_css ) {
916 + $block_attrs['bgGradient'] = $gradient_css;
917 + }
918 + }
919 +
920 + // Handle DIMENSIONS controls - map to individual camelCase keys for Gutenberg compatibility.
921 + $block_attrs = self::map_elementor_dimensions( $block_attrs, $settings, 'formPadding' );
922 + $block_attrs = self::map_elementor_dimensions( $block_attrs, $settings, 'formBorderRadius' );
923 +
924 + // Handle bgImage separately as it returns an object from Elementor.
925 + $bg_image = isset( $settings['bgImage'] ) && is_array( $settings['bgImage'] ) ? $settings['bgImage'] : [];
926 + if ( ! empty( $bg_image['url'] ) ) {
927 + $raw_url = esc_url_raw( $bg_image['url'] );
928 + // Encode parentheses to prevent CSS injection in url() context.
929 + $block_attrs['bgImage'] = str_replace( [ '(', ')' ], [ '%28', '%29' ], $raw_url );
930 + }
931 + if ( ! empty( $bg_image['id'] ) ) {
932 + $block_attrs['bgImageId'] = $bg_image['id'];
933 + }
934 +
935 + /**
936 + * Filters the Elementor block attributes after sanitization.
937 + *
938 + * Third-party code hooking this filter is responsible for sanitizing
939 + * any values it adds or modifies. Unsanitized values may be output
940 + * directly into CSS custom properties.
941 + *
942 + * Uses get_settings() (raw) instead of get_settings_for_display() because Elementor
943 + * nullifies Group_Control_Background child fields when the parent 'background' type
944 + * field is not explicitly saved (defaults aren't persisted to the database).
945 + *
946 + * @param array<string, mixed> $block_attrs Block attributes.
947 + * @param array<string, mixed> $settings Raw widget settings.
948 + * @since 2.7.0
949 + */
950 + /** Raw widget settings for filter.
951 + *
952 + * @var array<string, mixed> $raw_settings_for_filter
953 + */
954 + $raw_settings_for_filter = $this->get_settings();
955 + return apply_filters( 'srfm_elementor_block_attrs', $block_attrs, $raw_settings_for_filter );
956 + }
957 +
958 + /**
959 + * Map Elementor dimensions control to individual camelCase block attributes.
960 + *
961 + * Elementor returns dimensions as an array with top/right/bottom/left/unit keys.
962 + * This maps them to individual keys like formPaddingTop, formPaddingRight, etc.
963 + *
964 + * @param array<string, mixed> $block_attrs Block attributes.
965 + * @param array<string, mixed> $settings Widget settings.
966 + * @param string $control_key The Elementor dimensions control key (e.g., 'formPadding').
967 + * @return array<string, mixed> Updated block attributes.
968 + * @since 2.7.0
969 + */
970 + private static function map_elementor_dimensions( $block_attrs, $settings, $control_key ) {
971 + if ( empty( $settings[ $control_key ] ) || ! is_array( $settings[ $control_key ] ) ) {
972 + return $block_attrs;
973 + }
974 +
975 + $dimensions = $settings[ $control_key ];
976 + $allowed_units = [ 'px', '%', 'em' ];
977 + $raw_unit = $dimensions['unit'] ?? 'px';
978 + $unit = in_array( $raw_unit, $allowed_units, true ) ? $raw_unit : 'px';
979 + $sides = [ 'top', 'right', 'bottom', 'left' ];
980 +
981 + foreach ( $sides as $side ) {
982 + if ( ! isset( $dimensions[ $side ] ) ) {
983 + continue;
984 + }
985 + if ( ! empty( $dimensions[ $side ] ) || '0' === $dimensions[ $side ] ) {
986 + $block_attrs[ $control_key . ucfirst( $side ) ] = $dimensions[ $side ];
987 + }
988 + }
989 +
990 + $block_attrs[ $control_key . 'Unit' ] = $unit;
991 +
992 + return $block_attrs;
238 993 }
239 994
240 995 }