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