PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 1.12.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v1.12.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 in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 1.12.0, at inc/post-types.php

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