PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.7.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.7.0
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 0.0.2 0.0.3 All 96 releases
sureforms / inc / post-types.php
post-types.php
1,386 lines 42.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Post Types Class file.
4 *
5 * @package sureforms.
6 * @since 0.0.1
7 */
8
9 namespace SRFM\Inc;
10
11 use SRFM\Inc\Traits\Get_Instance;
12 use WP_Admin_Bar;
13 use WP_Post;
14 use WP_REST_Response;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * Post Types Main Class.
22 *
23 * @since 0.0.1
24 */
25 class Post_Types {
26 use Get_Instance;
27
28 /**
29 * Constructor
30 *
31 * @since 0.0.1
32 */
33 public function __construct() {
34 $this->restrict_unwanted_insertions();
35 add_action( 'init', [ $this, 'register_post_types' ] );
36 add_action( 'init', [ $this, 'register_post_metas' ] );
37 add_shortcode( 'sureforms', [ $this, 'forms_shortcode' ] );
38 add_action( 'manage_posts_extra_tablenav', [ $this, 'maybe_render_blank_form_state' ] );
39 add_action( 'admin_bar_menu', [ $this, 'remove_admin_bar_menu_item' ], 80, 1 );
40 add_action( 'template_redirect', [ $this, 'srfm_instant_form_redirect' ] );
41 add_action( 'template_redirect', [ $this, 'disable_sureforms_archive_page' ], 9 );
42 add_action( 'load-edit.php', [ $this, 'redirect_forms_listing_page' ] );
43
44 add_filter( 'rest_prepare_sureforms_form', [ $this, 'sureforms_normalize_meta_for_rest' ], 10, 2 );
45 add_action( 'admin_bar_menu', [ $this, 'add_edit_form_to_admin_bar_menu' ], 100 );
46 }
47
48 /**
49 * Redirect the forms listing page to the updated forms page.
50 *
51 * @return void
52 * @since 2.0.0
53 */
54 public function redirect_forms_listing_page() {
55 global $pagenow;
56
57 if ( 'edit.php' === $pagenow && isset( $_GET['post_type'] ) && SRFM_FORMS_POST_TYPE === $_GET['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce is not required for the redirection.
58 wp_safe_redirect( admin_url( 'admin.php?page=sureforms_forms' ) );
59 exit;
60 }
61 }
62
63 /**
64 * Add "Edit Form" link to the admin bar menu.
65 *
66 * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance.
67 * @since 2.0.0
68 * @return void
69 */
70 public function add_edit_form_to_admin_bar_menu( $wp_admin_bar ) {
71
72 // Bail early if admin bar or user isn’t available.
73 if ( ! is_user_logged_in() || ! is_admin_bar_showing() || ! $wp_admin_bar instanceof WP_Admin_Bar ) {
74 return;
75 }
76
77 global $post;
78
79 // Bail if no valid post or wrong post type.
80 if ( empty( $post ) || SRFM_FORMS_POST_TYPE !== $post->post_type ) {
81 return;
82 }
83
84 $edit_link = get_edit_post_link( $post->ID );
85 if ( ! $edit_link ) {
86 return;
87 }
88
89 $wp_admin_bar->add_node(
90 [
91 'id' => 'edit-form',
92 'title' => sprintf(
93 '<span class="ab-icon dashicons dashicons-edit" style="line-height:1.2;margin-right:4px;"></span>
94 <span class="ab-label" style="position:relative;top:-1px;">%s</span>',
95 esc_html__( 'Edit Form', 'sureforms' )
96 ),
97 'href' => esc_url( $edit_link ),
98 'meta' => [
99 'title' => esc_attr__( 'Edit this form', 'sureforms' ),
100 ],
101 'html' => true,
102 ]
103 );
104 }
105
106 /**
107 * Remove this method in the future once _srfm_form_confirmation meta is updated.
108 * Normalize the _srfm_form_confirmation meta before it's sent to the REST API.
109 * Ensures the meta data is type-safe and includes necessary defaults like `hide_copy`.
110 *
111 * @param WP_REST_Response $response The REST response object.
112 * @param WP_Post $post The post object.
113 *
114 * @return WP_REST_Response Modified REST response with normalized meta.
115 * @since 1.7.3
116 */
117 public function sureforms_normalize_meta_for_rest( $response, $post ) {
118 $meta_raw = get_post_meta( $post->ID, '_srfm_form_confirmation', true );
119 $form_confirmation = maybe_unserialize( is_string( $meta_raw ) ? $meta_raw : '' );
120
121 if ( ! is_array( $form_confirmation ) ) {
122 return $response;
123 }
124
125 foreach ( $form_confirmation as $index => $item ) {
126 if ( ! is_array( $item ) ) {
127 continue;
128 }
129
130 $form_confirmation[ $index ]['hide_copy'] = ! empty( $item['hide_copy'] );
131 $form_confirmation[ $index ]['hide_download_all'] = ! empty( $item['hide_download_all'] );
132 }
133
134 $response_data = $response->get_data();
135 if ( is_array( $response_data ) ) {
136 if ( ! isset( $response_data['meta'] ) || ! is_array( $response_data['meta'] ) ) {
137 $response_data['meta'] = [];
138 }
139
140 $response_data['meta']['_srfm_form_confirmation'] = $form_confirmation;
141 $response->set_data( $response_data );
142 }
143 return $response;
144 }
145
146 /**
147 * Add SureForms menu.
148 *
149 * @param string $title Parent slug.
150 * @param string $subtitle Parent slug.
151 * @param string $image Parent slug.
152 * @param string $button_text Parent slug.
153 * @param string $button_url Parent slug.
154 * @param string $after_button After button content.
155 * @return void
156 * @since 0.0.1
157 */
158 public function get_blank_page_markup( $title, $subtitle, $image, $button_text = '', $button_url = '', $after_button = '' ) {
159 ?>
160 <div class="sureform-add-new-form">
161 <p class="sureform-blank-page-title"><?php echo esc_html( $title ); ?></p>
162 <p class="sureform-blank-page-subtitle"><?php echo esc_html( $subtitle ); ?></p>
163 <img src="<?php echo esc_url( SRFM_URL . '/images/' . $image . '.svg' ); ?>" alt=""/>
164 <?php if ( ! empty( $button_text ) && ! empty( $button_url ) ) { ?>
165 <div class="sureforms-add-new-form-container">
166 <a class="sf-add-new-form-button" href="<?php echo esc_url( $button_url ); ?>">
167 <div class="button-secondary"><?php echo esc_html( $button_text ); ?></div>
168 </a>
169 <?php echo wp_kses_post( $after_button ); ?>
170 </div>
171 <?php } ?>
172 </div>
173 <?php
174 }
175
176 /**
177 * Render blank state for add new form screen.
178 *
179 * @param string $post_type Post type.
180 * @return void
181 * @since 0.0.1
182 */
183 public function sureforms_render_blank_state( $post_type ) {
184 if ( SRFM_ENTRIES === $post_type ) {
185
186 $this->get_blank_page_markup(
187 esc_html__( 'No records found', 'sureforms' ),
188 esc_html__(
189 'This is where your form entries will appear',
190 'sureforms'
191 ),
192 'blank-entries'
193 );
194 }
195 }
196
197 /**
198 * Registers the forms and submissions post types.
199 *
200 * @return void
201 * @since 0.0.1
202 */
203 public function register_post_types() {
204 $form_labels = [
205 'name' => _x( 'Forms', 'post type general name', 'sureforms' ),
206 'singular_name' => _x( 'Form', 'post type singular name', 'sureforms' ),
207 'menu_name' => _x( 'Forms', 'admin menu', 'sureforms' ),
208 'add_new' => _x( 'Add New', 'form', 'sureforms' ),
209 'add_new_item' => __( 'Add New Form', 'sureforms' ),
210 'new_item' => __( 'New Form', 'sureforms' ),
211 'edit_item' => __( 'Edit Form', 'sureforms' ),
212 'view_item' => __( 'View Form', 'sureforms' ),
213 'view_items' => __( 'View Forms', 'sureforms' ),
214 'all_items' => __( 'Forms', 'sureforms' ),
215 'search_items' => __( 'Search Forms', 'sureforms' ),
216 'parent_item_colon' => __( 'Parent Forms:', 'sureforms' ),
217 'not_found' => __( 'No forms found.', 'sureforms' ),
218 'not_found_in_trash' => __( 'No forms found in Trash.', 'sureforms' ),
219 'item_published' => __( 'Form published.', 'sureforms' ),
220 'item_updated' => __( 'Form updated.', 'sureforms' ),
221 ];
222 register_post_type(
223 SRFM_FORMS_POST_TYPE,
224 [
225 'labels' => $form_labels,
226 'rewrite' => [ 'slug' => 'form' ],
227 'public' => true,
228 'show_in_rest' => true,
229 'has_archive' => true,
230 'show_ui' => true,
231 'supports' => [ 'title', 'author', 'editor', 'custom-fields' ],
232 'show_in_menu' => false,
233 'show_in_nav_menus' => true,
234 'capabilities' => [
235 'edit_post' => 'manage_options',
236 'read_post' => 'manage_options',
237 'delete_post' => 'manage_options',
238 'edit_posts' => 'manage_options',
239 'edit_others_posts' => 'manage_options',
240 'publish_posts' => 'manage_options',
241 'read_private_posts' => 'manage_options',
242 'create_posts' => 'manage_options',
243 ],
244 ]
245 );
246 // will be used later.
247 // register_post_status(
248 // 'unread',
249 // array(
250 // 'label' => _x( 'Unread', 'sureforms', 'sureforms' ),
251 // 'public' => true,
252 // 'exclude_from_search' => false,
253 // 'show_in_admin_all_list' => true,
254 // 'show_in_admin_status_list' => true,
255 // Translators: %s is the number of unread items.
256 // 'label_count' => _n_noop( 'Unread (%s)', 'Unread (%s)', 'sureforms' ),
257 // )
258 // );.
259 }
260
261 /**
262 * Redirects requests for SureForms archieve page to homeurl
263 *
264 * @since 1.4.0
265 * @return void
266 */
267 public function disable_sureforms_archive_page() {
268 if ( is_post_type_archive( SRFM_FORMS_POST_TYPE ) ) {
269 wp_safe_redirect( home_url(), 301 );
270 exit;
271 }
272 }
273
274 /**
275 * Remove add new form menu item.
276 *
277 * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance.
278 *
279 * @return void
280 * @since 0.0.1
281 */
282 public function remove_admin_bar_menu_item( $wp_admin_bar ) {
283 $wp_admin_bar->remove_node( 'new-sureforms_form' );
284 }
285
286 /**
287 * Show blank slate styles.
288 *
289 * @return void
290 * @since 0.0.1
291 */
292 public function get_blank_state_styles() {
293 ?>
294 <style type="text/css">
295 .sf-add-new-form-button:focus {
296 box-shadow: none !important;
297 outline: none !important;
298 }
299 #posts-filter .wp-list-table,
300 #posts-filter .tablenav.top,
301 .tablenav.bottom .actions,
302 .wrap .subsubsub {
303 display: none;
304 }
305 #posts-filter .tablenav.bottom {
306 height: auto;
307 }
308 .sureform-add-new-form {
309 display: flex;
310 flex-direction: column;
311 gap: 8px;
312 justify-content: center;
313 align-items: center;
314 padding: 24px 0 24px 0;
315 }
316 .sureform-blank-page-title {
317 color: var(--dashboard-heading);
318 font-family: Inter;
319 font-size: 22px;
320 font-style: normal;
321 font-weight: 600;
322 line-height: 28px;
323 margin: 0;
324 }
325 .sureform-blank-page-subtitle {
326 color: var(--dashboard-text);
327 margin: 0;
328 font-family: Inter;
329 font-size: 14px;
330 font-style: normal;
331 font-weight: 400;
332 line-height: 16px;
333 }
334 </style>
335 <?php
336 }
337
338 /**
339 * Show blank slate.
340 *
341 * @param string $which String which tablenav is being shown.
342 * @return void
343 * @since 0.0.1
344 */
345 public function maybe_render_blank_form_state( $which ) {
346 $screen = get_current_screen();
347 $post_type = $screen ? $screen->post_type : '';
348
349 if ( SRFM_FORMS_POST_TYPE === $post_type && 'bottom' === $which ) {
350
351 $counts = (array) wp_count_posts( SRFM_FORMS_POST_TYPE );
352 unset( $counts['auto-draft'] );
353 $count = array_sum( $counts );
354
355 if ( 0 < $count ) {
356 return;
357 }
358
359 $this->sureforms_render_blank_state( $post_type );
360
361 $this->get_blank_state_styles();
362
363 }
364 }
365
366 /**
367 * Registers the sureforms metas.
368 *
369 * @return void
370 * @since 0.0.1
371 */
372 public function register_post_metas() {
373 $check_icon = 'data:image/svg+xml;base64,' . base64_encode( strval( file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/check-icon.svg' ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
374
375 $metas = apply_filters(
376 'srfm_register_post_meta',
377 [
378 // General tab metas.
379 '_srfm_use_label_as_placeholder' => 'boolean',
380 '_srfm_submit_button_text' => 'string',
381 '_srfm_is_inline_button' => 'boolean',
382 // Submit Button.
383 '_srfm_submit_width_backend' => 'string',
384 '_srfm_button_border_radius' => 'integer',
385 '_srfm_submit_alignment' => 'string',
386 '_srfm_submit_alignment_backend' => 'string',
387 '_srfm_submit_width' => 'string',
388 '_srfm_inherit_theme_button' => 'boolean',
389 // Additional Classes.
390 '_srfm_additional_classes' => 'string',
391
392 // Advanced tab metas.
393 // Success Message.
394 '_srfm_submit_type' => 'string',
395 // Security.
396 '_srfm_captcha_security_type' => 'string',
397 '_srfm_form_recaptcha' => 'string',
398 // post meta to store if the form is AI generated.
399 '_srfm_is_ai_generated' => 'boolean',
400 ]
401 );
402
403 // Form Custom CSS meta.
404 register_post_meta(
405 'sureforms_form',
406 '_srfm_form_custom_css',
407 [
408 'show_in_rest' => [
409 'schema' => [
410 'type' => 'string',
411 'context' => [ 'edit' ],
412 ],
413 ],
414 'type' => 'string',
415 'single' => true,
416 'auth_callback' => static function() {
417 return Helper::current_user_can();
418 },
419 'sanitize_callback' => static function( $meta_value ) {
420 return wp_kses_post( $meta_value );
421 },
422 ]
423 );
424
425 // Get default values for meta keys.
426 $default_meta_keys = Create_New_Form::get_default_meta_keys();
427
428 foreach ( $metas as $meta => $type ) {
429 // Get default value if exists.
430 $default_value = $default_meta_keys[ $meta ] ?? null;
431
432 $meta_args = [
433 'show_in_rest' => [
434 'schema' => [
435 'type' => $type,
436 'context' => [ 'edit' ],
437 ],
438 ],
439 'single' => true,
440 'type' => $type,
441 'sanitize_callback' => 'sanitize_text_field',
442 'auth_callback' => static function() {
443 return Helper::current_user_can();
444 },
445 ];
446
447 // Add default value if it exists.
448 if ( null !== $default_value ) {
449 $meta_args['default'] = $default_value;
450 }
451
452 register_post_meta(
453 SRFM_FORMS_POST_TYPE,
454 $meta,
455 $meta_args
456 );
457 }
458
459 // Registers meta to handle values associated with form styling.
460 register_post_meta(
461 SRFM_FORMS_POST_TYPE,
462 '_srfm_instant_form_settings',
463 [
464 'single' => true,
465 'type' => 'object',
466 'auth_callback' => static function() {
467 return Helper::current_user_can();
468 },
469 'show_in_rest' => [
470 'schema' => [
471 'type' => 'object',
472 'context' => [ 'edit' ],
473 'properties' => [
474 'site_logo' => [
475 'type' => 'string',
476 ],
477 'site_logo_id' => [
478 'type' => 'integer',
479 ],
480 // Form page banner settings.
481 'cover_type' => [
482 'type' => 'string',
483 ],
484 'cover_color' => [
485 'type' => 'string',
486 ],
487 'cover_image' => [
488 'type' => 'string',
489 ],
490 'cover_image_id' => [
491 'type' => 'integer',
492 ],
493 // Form page background settings.
494 'bg_type' => [
495 'type' => 'string',
496 ],
497 'bg_color' => [
498 'type' => 'string',
499 ],
500 'bg_image' => [
501 'type' => 'string',
502 ],
503 'bg_image_id' => [
504 'type' => 'integer',
505 ],
506 'enable_instant_form' => [
507 'type' => 'boolean',
508 ],
509 'form_container_width' => [
510 'type' => 'integer',
511 ],
512 'single_page_form_title' => [
513 'type' => 'boolean',
514 ],
515 'use_banner_as_page_background' => [
516 'type' => 'boolean',
517 ],
518 ],
519 ],
520 ],
521 'default' => [
522 'bg_type' => 'color',
523 'bg_color' => '#ffffff',
524 'bg_image' => '',
525 'site_logo' => '',
526 'cover_type' => 'color',
527 'cover_color' => '#111C44',
528 'cover_image' => '',
529 'enable_instant_form' => false,
530 'form_container_width' => 620,
531 'single_page_form_title' => true,
532 'use_banner_as_page_background' => false,
533 ],
534 ]
535 );
536
537 register_post_meta(
538 SRFM_FORMS_POST_TYPE,
539 '_srfm_forms_styling',
540 [
541 'single' => true,
542 'type' => 'object',
543 'auth_callback' => static function() {
544 return Helper::current_user_can();
545 },
546 'show_in_rest' => [
547 'schema' => [
548 'type' => 'object',
549 'context' => [ 'edit' ],
550 'properties' => [
551 'primary_color' => [
552 'type' => 'string',
553 ],
554 'text_color' => [
555 'type' => 'string',
556 ],
557 'text_color_on_primary' => [
558 'type' => 'string',
559 ],
560 'field_spacing' => [
561 'type' => 'string',
562 ],
563 'submit_button_alignment' => [
564 'type' => 'string',
565 ],
566 'bg_type' => [
567 'type' => 'string',
568 ],
569 'bg_color' => [
570 'type' => 'string',
571 ],
572 'bg_image' => [
573 'type' => 'string',
574 ],
575 'bg_image_id' => [
576 'type' => 'integer',
577 ],
578 // Image Properties.
579 'bg_image_position' => [
580 'type' => 'object',
581 'properties' => [
582 'x' => [
583 'type' => 'number',
584 'format' => 'float',
585 ],
586 'y' => [
587 'type' => 'number',
588 'format' => 'float',
589 ],
590 ],
591 ],
592 'bg_image_attachment' => [
593 'type' => 'string',
594 ],
595 'bg_image_repeat' => [
596 'type' => 'string',
597 ],
598 'bg_image_size' => [
599 'type' => 'string',
600 ],
601 'bg_image_size_custom' => [
602 'type' => 'integer',
603 ],
604 'bg_image_size_custom_unit' => [
605 'type' => 'string',
606 ],
607 // Gradient Properties.
608 'bg_gradient' => [
609 'type' => 'string',
610 ],
611 'gradient_type' => [
612 'type' => 'string',
613 ],
614 'bg_gradient_type' => [
615 'type' => 'string',
616 ],
617 'bg_gradient_color_1' => [
618 'type' => 'string',
619 ],
620 'bg_gradient_color_2' => [
621 'type' => 'string',
622 ],
623 'bg_gradient_angle' => [
624 'type' => 'integer',
625 ],
626 'bg_gradient_location_1' => [
627 'type' => 'integer',
628 ],
629 'bg_gradient_location_2' => [
630 'type' => 'integer',
631 ],
632 // Overlay Properties.
633 'bg_overlay_size' => [
634 'type' => 'string',
635 ],
636 'bg_gradient_overlay_type' => [
637 'type' => 'string',
638 ],
639 'bg_overlay_opacity' => [
640 'type' => 'number',
641 ],
642 'bg_overlay_image' => [
643 'type' => 'string',
644 ],
645 'bg_overlay_image_id' => [
646 'type' => 'integer',
647 ],
648 'bg_image_overlay_color' => [
649 'type' => 'string',
650 ],
651 'bg_overlay_custom_size_unit' => [
652 'type' => 'string',
653 ],
654 'bg_overlay_custom_size' => [
655 'type' => 'integer',
656 ],
657 'bg_overlay_blend_mode' => [
658 'type' => 'string',
659 ],
660 'bg_overlay_position' => [
661 'type' => 'object',
662 'properties' => [
663 'x' => [
664 'type' => 'number',
665 'format' => 'float',
666 ],
667 'y' => [
668 'type' => 'number',
669 'format' => 'float',
670 ],
671 ],
672 ],
673 'bg_overlay_attachment' => [
674 'type' => 'string',
675 ],
676 'bg_overlay_repeat' => [
677 'type' => 'string',
678 ],
679 // Gradient Overlay Properties.
680 'bg_overlay_gradient' => [
681 'type' => 'string',
682 ],
683 'overlay_gradient_type' => [
684 'type' => 'string',
685 ],
686 'bg_overlay_gradient_type' => [
687 'type' => 'string',
688 ],
689 'bg_overlay_gradient_color_1' => [
690 'type' => 'string',
691 ],
692 'bg_overlay_gradient_color_2' => [
693 'type' => 'string',
694 ],
695 'bg_overlay_gradient_angle' => [
696 'type' => 'integer',
697 ],
698 'bg_overlay_gradient_location_1' => [
699 'type' => 'integer',
700 ],
701 'bg_overlay_gradient_location_2' => [
702 'type' => 'integer',
703 ],
704 // Form Padding.
705 'form_padding_top' => [
706 'type' => 'number',
707 ],
708 'form_padding_right' => [
709 'type' => 'number',
710 ],
711 'form_padding_bottom' => [
712 'type' => 'number',
713 ],
714 'form_padding_left' => [
715 'type' => 'number',
716 ],
717 'form_padding_unit' => [
718 'type' => 'string',
719 ],
720 'form_padding_link' => [
721 'type' => 'boolean',
722 ],
723 // Border Radius.
724 'form_border_radius_top' => [
725 'type' => 'number',
726 ],
727 'form_border_radius_right' => [
728 'type' => 'number',
729 ],
730 'form_border_radius_bottom' => [
731 'type' => 'number',
732 ],
733 'form_border_radius_left' => [
734 'type' => 'number',
735 ],
736 'form_border_radius_unit' => [
737 'type' => 'string',
738 ],
739 'form_border_radius_link' => [
740 'type' => 'boolean',
741 ],
742 // Form Padding and Border Radius.
743 // Form Padding.
744 'instant_form_padding_top' => [
745 'type' => 'number',
746 ],
747 'instant_form_padding_right' => [
748 'type' => 'number',
749 ],
750 'instant_form_padding_bottom' => [
751 'type' => 'number',
752 ],
753 'instant_form_padding_left' => [
754 'type' => 'number',
755 ],
756 'instant_form_padding_unit' => [
757 'type' => 'string',
758 ],
759 'instant_form_padding_link' => [
760 'type' => 'boolean',
761 ],
762 // Border Radius.
763 'instant_form_border_radius_top' => [
764 'type' => 'number',
765 ],
766 'instant_form_border_radius_right' => [
767 'type' => 'number',
768 ],
769 'instant_form_border_radius_bottom' => [
770 'type' => 'number',
771 ],
772 'instant_form_border_radius_left' => [
773 'type' => 'number',
774 ],
775 'instant_form_border_radius_unit' => [
776 'type' => 'string',
777 ],
778 'instant_form_border_radius_link' => [
779 'type' => 'boolean',
780 ],
781 ],
782 ],
783 ],
784 'default' => [
785 'primary_color' => '#111C44',
786 'text_color' => '#1E1E1E',
787 'text_color_on_primary' => '#FFFFFF',
788 'field_spacing' => 'medium',
789 'submit_button_alignment' => 'left',
790 'bg_type' => 'color',
791 'bg_color' => '#ffffff',
792 'bg_image' => '',
793 'bg_image_position' => [
794 'x' => 0.5,
795 'y' => 0.5,
796 ],
797 'bg_image_attachment' => 'scroll',
798 'bg_image_repeat' => 'no-repeat',
799 'bg_image_size' => 'cover',
800 'bg_image_size_custom' => 100, // Image width when set to custom.
801 'bg_image_size_custom_unit' => '%',
802 'gradient_type' => 'basic',
803 'bg_gradient_type' => 'linear',
804 'bg_gradient_color_1' => '#FFC9B2',
805 'bg_gradient_color_2' => '#C7CBFF',
806 'bg_gradient_angle' => 90,
807 'bg_gradient_location_1' => 0,
808 'bg_gradient_location_2' => 100,
809 'bg_overlay_size' => 'cover',
810 'bg_gradient_overlay_type' => '',
811 'bg_overlay_opacity' => 1,
812 'bg_overlay_image' => '',
813 'bg_image_overlay_color' => '#FFFFFF75',
814 'bg_overlay_custom_size_unit' => '%',
815 'bg_overlay_custom_size' => 100,
816 'bg_overlay_position' => [
817 'x' => 0.5,
818 'y' => 0.5,
819 ],
820 'bg_overlay_attachment' => 'scroll',
821 'bg_overlay_repeat' => 'no-repeat',
822 'bg_overlay_blend_mode' => 'normal',
823 // Gradient Overlay Properties.
824 'overlay_gradient_type' => 'basic',
825 'bg_overlay_gradient_type' => 'linear',
826 'bg_overlay_gradient_color_1' => '#FFC9B2',
827 'bg_overlay_gradient_color_2' => '#C7CBFF',
828 'bg_overlay_gradient_angle' => 90,
829 'bg_overlay_gradient_location_1' => 0,
830 'bg_overlay_gradient_location_2' => 100,
831 // Form Properties.
832 // Padding.
833 'form_padding_top' => 0,
834 'form_padding_right' => 0,
835 'form_padding_bottom' => 0,
836 'form_padding_left' => 0,
837 'form_padding_unit' => 'px',
838 'form_padding_link' => true,
839 // Border Radius.
840 'form_border_radius_top' => 0,
841 'form_border_radius_right' => 0,
842 'form_border_radius_bottom' => 0,
843 'form_border_radius_left' => 0,
844 'form_border_radius_unit' => 'px',
845 'form_border_radius_link' => true,
846 // Form Properties.
847 // Padding.
848 'instant_form_padding_top' => 32,
849 'instant_form_padding_right' => 32,
850 'instant_form_padding_bottom' => 32,
851 'instant_form_padding_left' => 32,
852 'instant_form_padding_unit' => 'px',
853 'instant_form_padding_link' => true,
854 // Border Radius.
855 'instant_form_border_radius_top' => 12,
856 'instant_form_border_radius_right' => 12,
857 'instant_form_border_radius_bottom' => 12,
858 'instant_form_border_radius_left' => 12,
859 'instant_form_border_radius_unit' => 'px',
860 'instant_form_border_radius_link' => true,
861 ],
862 ]
863 );
864
865 // Email notification Metas.
866 register_post_meta(
867 'sureforms_form',
868 '_srfm_email_notification',
869 [
870 'single' => true,
871 'type' => 'array',
872 'auth_callback' => static function() {
873 return Helper::current_user_can();
874 },
875 'show_in_rest' => [
876 'schema' => [
877 'type' => 'array',
878 'context' => [ 'edit' ],
879 'items' => [
880 'type' => 'object',
881 'properties' => [
882 'id' => [
883 'type' => 'integer',
884 ],
885 'status' => [
886 'type' => 'boolean',
887 ],
888 'is_raw_format' => [
889 'type' => 'boolean',
890 ],
891 'name' => [
892 'type' => 'string',
893 ],
894 'email_to' => [
895 'type' => 'string',
896 ],
897 'email_reply_to' => [
898 'type' => 'string',
899 ],
900 'from_name' => [
901 'type' => 'string',
902 ],
903 'from_email' => [
904 'type' => 'string',
905 ],
906 'email_cc' => [
907 'type' => 'string',
908 ],
909 'email_bcc' => [
910 'type' => 'string',
911 ],
912 'subject' => [
913 'type' => 'string',
914 ],
915 'email_body' => [
916 'type' => 'string',
917 ],
918 ],
919 ],
920 ],
921 ],
922 'default' => [
923 [
924 'id' => 1,
925 'status' => true,
926 'is_raw_format' => false,
927 'name' => __( 'Admin Notification Email', 'sureforms' ),
928 'email_to' => '{admin_email}',
929 'email_reply_to' => '{admin_email}',
930 'from_name' => '{site_title}',
931 'from_email' => '{admin_email}',
932 'email_cc' => '{admin_email}',
933 'email_bcc' => '{admin_email}',
934 'subject' => sprintf( /* translators: %s: Form title smart tag */ __( 'New Form Submission - %s', 'sureforms' ), '{form_title}' ),
935 'email_body' => '{all_data}',
936 ],
937 ],
938 ]
939 );
940
941 // Compliance Settings metas.
942 register_post_meta(
943 'sureforms_form',
944 '_srfm_compliance',
945 [
946 'single' => true,
947 'type' => 'array',
948 'auth_callback' => static function() {
949 return Helper::current_user_can();
950 },
951 'show_in_rest' => [
952 'schema' => [
953 'type' => 'array',
954 'context' => [ 'edit' ],
955 'items' => [
956 'type' => 'object',
957 'properties' => [
958 'id' => [
959 'type' => 'string',
960 ],
961 'gdpr' => [
962 'type' => 'boolean',
963 ],
964 'do_not_store_entries' => [
965 'type' => 'boolean',
966 ],
967 'auto_delete_entries' => [
968 'type' => 'boolean',
969 ],
970 'auto_delete_days' => [
971 'type' => 'string',
972 ],
973 ],
974 ],
975 ],
976 ],
977 'default' => [
978 [
979 'id' => 'gdpr',
980 'gdpr' => false,
981 'do_not_store_entries' => false,
982 'auto_delete_entries' => false,
983 'auto_delete_days' => '',
984 ],
985 ],
986 ]
987 );
988
989 ob_start();
990 ?>
991 <p style="text-align: center;"><img src="<?php echo esc_attr( $check_icon ); ?>" alt="" aria-hidden="true" /></p><h2 style="text-align: center;"><?php echo esc_html__( 'Thank you', 'sureforms' ); ?></h2><p style="text-align: center;"><?php echo esc_html__( 'Your form has been submitted successfully. We\'ll review your details and get back to you soon.', 'sureforms' ); ?></p>
992 <?php
993 $default_confirmation_message = ob_get_clean();
994
995 // form confirmation.
996 register_post_meta(
997 'sureforms_form',
998 '_srfm_form_confirmation',
999 [
1000 'single' => true,
1001 'type' => 'array',
1002 'auth_callback' => static function() {
1003 return Helper::current_user_can();
1004 },
1005 'sanitize_callback' => static function( $meta_value ) {
1006 if ( ! is_array( $meta_value ) ) {
1007 return [];
1008 }
1009 $sanitized = [];
1010 foreach ( $meta_value as $item ) {
1011 if ( ! is_array( $item ) ) {
1012 continue;
1013 }
1014 $sanitized_item = [
1015 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
1016 'confirmation_type' => isset( $item['confirmation_type'] ) ? sanitize_text_field( $item['confirmation_type'] ) : '',
1017 'page_url' => isset( $item['page_url'] ) ? esc_url_raw( $item['page_url'] ) : '',
1018 'custom_url' => isset( $item['custom_url'] ) ? esc_url_raw( $item['custom_url'] ) : '',
1019 'message' => isset( $item['message'] ) ? Helper::strip_js_attributes( $item['message'] ) : '',
1020 'submission_action' => isset( $item['submission_action'] ) ? sanitize_text_field( $item['submission_action'] ) : '',
1021 'enable_query_params' => isset( $item['enable_query_params'] ) ? filter_var( $item['enable_query_params'], FILTER_VALIDATE_BOOLEAN ) : false,
1022 'query_params' => isset( $item['query_params'] ) && is_array( $item['query_params'] )
1023 ? array_map(
1024 static function ( $pair ) {
1025 if ( ! is_array( $pair ) ) {
1026 return [];
1027 }
1028 $key = key( $pair );
1029 $value = current( $pair );
1030
1031 return [
1032 sanitize_text_field( Helper::get_string_value( $key ) ) => sanitize_text_field( Helper::get_string_value( $value ) ),
1033 ];
1034 },
1035 $item['query_params']
1036 )
1037 : [],
1038 ];
1039
1040 $sanitized_item = apply_filters( 'srfm_form_confirmation_params', $sanitized_item, $item );
1041
1042 $sanitized[] = $sanitized_item;
1043 }
1044 return $sanitized;
1045 },
1046 'show_in_rest' => [
1047 'schema' => [
1048 'type' => 'array',
1049 'context' => [ 'edit' ],
1050 'items' => [
1051 'type' => 'object',
1052 'properties' => [
1053 'id' => [
1054 'type' => 'integer',
1055 ],
1056 'confirmation_type' => [
1057 'type' => 'string',
1058 ],
1059 'page_url' => [
1060 'type' => 'string',
1061 ],
1062 'custom_url' => [
1063 'type' => 'string',
1064 ],
1065 'message' => [
1066 'type' => 'string',
1067 ],
1068 'submission_action' => [
1069 'type' => 'string',
1070 ],
1071 'enable_query_params' => [
1072 'type' => 'boolean',
1073 ],
1074 'query_params' => [
1075 'type' => 'array',
1076 ],
1077 ],
1078 ],
1079 ],
1080 ],
1081 'default' => [
1082 [
1083 'id' => 1,
1084 'confirmation_type' => 'same page',
1085 'page_url' => '',
1086 'custom_url' => '',
1087 'message' => $default_confirmation_message,
1088 'submission_action' => 'hide form',
1089 ],
1090 ],
1091 ]
1092 );
1093
1094 // conditional logic.
1095 do_action( 'srfm_register_conditional_logic_post_meta' );
1096 /**
1097 * Hook for registering additional Post Meta
1098 */
1099 do_action( 'srfm_register_additional_post_meta' );
1100
1101 register_meta(
1102 'post',
1103 '_srfm_form_restriction',
1104 [
1105 'type' => 'string', // Will store as JSON string.
1106 'single' => true, // Store as single value.
1107 'show_in_rest' => [
1108 'schema' => [
1109 'type' => 'string',
1110 'context' => [ 'edit' ],
1111 ],
1112 ],
1113 // Custom callback to sanitize the data.
1114 'sanitize_callback' => [ $this, 'sanitize_form_restriction_data' ],
1115 'object_subtype' => SRFM_FORMS_POST_TYPE,
1116 'auth_callback' => static function () {
1117 return Helper::current_user_can();
1118 },
1119 'default' => wp_json_encode(
1120 [
1121 'status' => false,
1122 'maxEntries' => 0,
1123 'date' => '',
1124 'hours' => '12',
1125 'minutes' => '00',
1126 'meridiem' => 'AM',
1127 'message' => Translatable::get_default_form_restriction_message(),
1128 // Form Scheduling meta.
1129 'schedulingStatus' => false,
1130 'startDate' => '',
1131 'startHours' => '12',
1132 'startMinutes' => '00',
1133 'startMeridiem' => 'AM',
1134 'schedulingNotStartedMessage' => __( 'This form is not yet available. Check back after the scheduled start time.', 'sureforms' ),
1135 'schedulingEndedMessage' => __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1136 ]
1137 ),
1138 ]
1139 );
1140 }
1141
1142 /**
1143 * Sanitizes the form restriction data.
1144 *
1145 * @param mixed $meta_value The meta value to sanitize.
1146 * @return string|false Sanitized JSON string.
1147 */
1148 public function sanitize_form_restriction_data( $meta_value ) {
1149 if ( empty( $meta_value ) || ! is_string( $meta_value ) ) {
1150 return wp_json_encode( [] );
1151 }
1152
1153 $meta_value = json_decode( $meta_value, true );
1154
1155 if ( ! is_array( $meta_value ) || json_last_error() !== JSON_ERROR_NONE ) {
1156 // If the JSON is invalid, return an empty array as JSON.
1157 return wp_json_encode( [] );
1158 }
1159
1160 $sanitized = [
1161 'status' => isset( $meta_value['status'] ) ? wp_validate_boolean( $meta_value['status'] ) : false,
1162 'maxEntries' => isset( $meta_value['maxEntries'] ) ? absint( $meta_value['maxEntries'] ) : 0,
1163 'date' => isset( $meta_value['date'] ) ? sanitize_text_field( $meta_value['date'] ) : '',
1164 'hours' => isset( $meta_value['hours'] ) ? sanitize_text_field( $meta_value['hours'] ) : '12',
1165 'minutes' => isset( $meta_value['minutes'] ) ? sanitize_text_field( $meta_value['minutes'] ) : '00',
1166 'meridiem' => isset( $meta_value['meridiem'] ) ? sanitize_text_field( $meta_value['meridiem'] ) : 'AM',
1167 'message' => isset( $meta_value['message'] ) ? sanitize_textarea_field( $meta_value['message'] ) : Translatable::get_default_form_restriction_message(),
1168 // Form Scheduling meta.
1169 'schedulingStatus' => isset( $meta_value['schedulingStatus'] ) ? wp_validate_boolean( $meta_value['schedulingStatus'] ) : false,
1170 'startDate' => isset( $meta_value['startDate'] ) ? sanitize_text_field( $meta_value['startDate'] ) : '',
1171 'startHours' => isset( $meta_value['startHours'] ) ? sanitize_text_field( $meta_value['startHours'] ) : '12',
1172 'startMinutes' => isset( $meta_value['startMinutes'] ) ? sanitize_text_field( $meta_value['startMinutes'] ) : '00',
1173 'startMeridiem' => isset( $meta_value['startMeridiem'] ) ? sanitize_text_field( $meta_value['startMeridiem'] ) : 'AM',
1174 'schedulingNotStartedMessage' => isset( $meta_value['schedulingNotStartedMessage'] ) ? sanitize_textarea_field( $meta_value['schedulingNotStartedMessage'] ) : __( 'This form is not yet available. Check back after the scheduled start time.', 'sureforms' ),
1175 'schedulingEndedMessage' => isset( $meta_value['schedulingEndedMessage'] ) ? sanitize_textarea_field( $meta_value['schedulingEndedMessage'] ) : __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1176 ];
1177
1178 // Validate scheduling: start date/time must be before end date/time.
1179 if ( $sanitized['schedulingStatus'] && ! empty( $sanitized['startDate'] ) && ! empty( $sanitized['date'] ) ) {
1180 $start_datetime = $this->create_datetime_object(
1181 $sanitized['startDate'],
1182 $sanitized['startHours'],
1183 $sanitized['startMinutes'],
1184 $sanitized['startMeridiem']
1185 );
1186
1187 $end_datetime = $this->create_datetime_object(
1188 $sanitized['date'],
1189 $sanitized['hours'],
1190 $sanitized['minutes'],
1191 $sanitized['meridiem']
1192 );
1193
1194 // If start date/time is not before end date/time, disable scheduling.
1195 if ( $start_datetime && $end_datetime && $start_datetime >= $end_datetime ) {
1196 // Disable scheduling status due to invalid date range.
1197 $sanitized['schedulingStatus'] = false;
1198 }
1199 }
1200
1201 // Return the sanitized data as a JSON string.
1202 return wp_json_encode( $sanitized );
1203 }
1204
1205 /**
1206 * Custom Shortcode.
1207 *
1208 * @param array<mixed> $atts Attributes.
1209 * @return string|false. $content Post Content.
1210 * @since 0.0.1
1211 */
1212 public function forms_shortcode( $atts ) {
1213 $atts = shortcode_atts(
1214 [
1215 'id' => '',
1216 'show_title' => true,
1217 ],
1218 $atts
1219 );
1220
1221 $id = intval( $atts['id'] );
1222 $post = get_post( $id );
1223
1224 if ( ! empty( $id ) && $post && ( 'publish' === $post->post_status || 'protected' === $post->post_status ) ) {
1225 return Generate_Form_Markup::get_form_markup( $id, ! filter_var( $atts['show_title'], FILTER_VALIDATE_BOOLEAN ), '', 'post', true );
1226 }
1227
1228 return esc_html__( 'This form has been deleted or is unavailable.', 'sureforms' );
1229 }
1230
1231 /**
1232 * Redirect to home page if instant form is not enabled.
1233 *
1234 * @since 0.0.1
1235 * @return void
1236 */
1237 public function srfm_instant_form_redirect() {
1238
1239 $form_id = Helper::get_integer_value( get_the_ID() );
1240
1241 $instant_form_settings = Helper::get_array_value( Helper::get_post_meta( $form_id, '_srfm_instant_form_settings' ) );
1242 $enable_instant_form = ! empty( $instant_form_settings['enable_instant_form'] ) ? boolval( $instant_form_settings['enable_instant_form'] ) : false;
1243
1244 if ( $enable_instant_form ) {
1245 return;
1246 }
1247
1248 $form_preview = '';
1249
1250 $form_preview_attr = isset( $_GET['preview'] ) ? sanitize_text_field( wp_unslash( $_GET['preview'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not needed here.
1251
1252 if ( $form_preview_attr ) {
1253 $form_preview = filter_var( $form_preview_attr, FILTER_VALIDATE_BOOLEAN );
1254 }
1255
1256 if ( is_singular( 'sureforms_form' ) && ! $form_preview && ! Helper::current_user_can() ) {
1257 wp_safe_redirect( home_url() );
1258 return;
1259 }
1260 }
1261
1262 /**
1263 * Restrict RankMath meta boxes in edit page.
1264 *
1265 * @since 0.0.5
1266 * @return void
1267 */
1268 public function restrict_data() {
1269 add_filter( 'rank_math/excluded_post_types', [ $this, 'unset_sureforms_post_type' ] );
1270 }
1271
1272 /**
1273 * Remove SureForms post type from RankMath and Yoast.
1274 *
1275 * @param array<mixed> $post_types Post types.
1276 * @since 0.0.5
1277 * @return array<mixed> $post_types Modified post types.
1278 */
1279 public function unset_sureforms_post_type( $post_types ) {
1280 return array_filter(
1281 $post_types,
1282 static function( $post_type ) {
1283 if ( is_array( $post_type ) && isset( $post_type['name'] ) ) {
1284 return SRFM_FORMS_POST_TYPE !== $post_type['name'];
1285 }
1286 return SRFM_FORMS_POST_TYPE !== $post_type;
1287 }
1288 );
1289 }
1290
1291 /**
1292 * Restrict unwanted insertions from the AIOSEO plugin.
1293 *
1294 * This method ensures that the SureForms post type is excluded from AIOSEO's
1295 * public post types unless the current page is related to AIOSEO settings.
1296 *
1297 * @return void
1298 * @since 1.6.0
1299 */
1300 public function restrict_in_aioseo_plugin() {
1301 /**
1302 * Checks if the AIOSEO plugin is installed and excludes the SureForms post type from AIOSEO's public post types.
1303 *
1304 * - Verifies the presence of the AIOSEO_DIR constant to ensure AIOSEO is installed.
1305 * - Allows AIOSEO functionality on its own settings pages by checking the `REQUEST_URI` and `page` query parameter.
1306 * - Excludes the SureForms post type from AIOSEO's public post types using the `aioseo_public_post_types` filter.
1307 *
1308 * Security Note:
1309 * - Nonce verification is intentionally skipped (`phpcs:ignore WordPress.Security.NonceVerification.Recommended`)
1310 * because this code is only performing a read operation to check the current request URI and query parameters.
1311 * It does not modify or process sensitive data, making nonce verification unnecessary in this context.
1312 */
1313 // Check if AIOSEO is installed by verifying the AIOSEO_DIR constant.
1314 if ( ! defined( 'AIOSEO_DIR' ) ) {
1315 return;
1316 }
1317
1318 // Allow AIOSEO functionality on its own settings pages.
1319 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
1320 $request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
1321 if ( strpos( $request_uri, 'admin.php' ) !== false ) {
1322 if ( isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here because Safe here as we're only reading the `page` parameter.
1323 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here because only we are reading the `page` parameter.
1324 if ( strpos( $page, 'aioseo' ) !== false ) {
1325 return;
1326 }
1327 }
1328 }
1329 }
1330
1331 // Exclude the SureForms post type from AIOSEO's public post types.
1332 add_filter( 'aioseo_public_post_types', [ $this, 'unset_sureforms_post_type' ] );
1333 }
1334
1335 /**
1336 * Creates a DateTime object from date string and time components.
1337 *
1338 * @param string $date_str Date string.
1339 * @param string $hours Hours in 12-hour format.
1340 * @param string $minutes Minutes.
1341 * @param string $meridiem AM or PM.
1342 * @since 2.4.0
1343 * @return \DateTime|null DateTime object or null if invalid.
1344 */
1345 private function create_datetime_object( $date_str, $hours, $minutes, $meridiem ) {
1346 if ( empty( $date_str ) ) {
1347 return null;
1348 }
1349
1350 try {
1351 $date = new \DateTime( $date_str );
1352
1353 // Convert 12-hour format to 24-hour format.
1354 $hour_24 = intval( $hours );
1355 if ( 'PM' === $meridiem && 12 !== $hour_24 ) {
1356 $hour_24 += 12;
1357 } elseif ( 'AM' === $meridiem && 12 === $hour_24 ) {
1358 $hour_24 = 0;
1359 }
1360
1361 $date->setTime( $hour_24, intval( $minutes ), 0 );
1362 return $date;
1363 } catch ( \Exception $e ) {
1364 return null;
1365 }
1366 }
1367
1368 /**
1369 * Restrict interference of other plugins with SureForms.
1370 *
1371 * @since 0.0.5
1372 * @return void
1373 */
1374 private function restrict_unwanted_insertions() {
1375 // Restrict RankMath metaboxes in edit page.
1376 add_action( 'cmb2_admin_init', [ $this, 'restrict_data' ] );
1377
1378 // Restrict Yoast columns.
1379 add_filter( 'wpseo_accessible_post_types', [ $this, 'unset_sureforms_post_type' ] );
1380 add_filter( 'wpseo_metabox_prio', '__return_false' );
1381
1382 // Restrict AIOSEO columns.
1383 $this->restrict_in_aioseo_plugin();
1384 }
1385 }
1386