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