PluginProbe ʕ •ᴥ•ʔ
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.3
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.3
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 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.12.3 1.13.0 1.13.1 1.13.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.8.0 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.2 2.6.0
sureforms / inc / post-types.php
sureforms / inc Last commit date
abilities 3 weeks ago admin 3 months ago ai-form-builder 1 month ago blocks 2 months ago compatibility 3 weeks ago database 3 weeks ago email 3 weeks ago fields 3 weeks ago global-settings 1 month ago lib 1 month ago migrator 2 months ago page-builders 3 weeks ago payments 3 weeks ago single-form-settings 2 months ago traits 2 months ago activator.php 1 year ago admin-ajax.php 2 months ago background-process.php 9 months ago create-new-form.php 3 months ago duplicate-form.php 3 months ago entries.php 3 weeks ago events-scheduler.php 2 years ago export.php 3 months ago field-validation.php 3 weeks ago form-restriction.php 2 months ago form-styling.php 1 month ago form-submit.php 3 weeks ago forms-data.php 5 months ago frontend-assets.php 1 month ago generate-form-markup.php 3 weeks ago gutenberg-hooks.php 3 weeks ago helper.php 3 weeks ago learn.php 4 months ago onboarding.php 2 months ago post-types.php 1 month ago rest-api.php 3 weeks ago smart-tags.php 4 months ago submit-token.php 4 months ago translatable.php 1 month ago updater-callbacks.php 3 weeks ago updater.php 3 weeks ago
post-types.php
1482 lines
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 // Disable default SureForms styling.
827 'disable_default_styles' => [
828 'type' => 'boolean',
829 ],
830 ],
831 ],
832 ],
833 'default' => [
834 'primary_color' => '#111C44',
835 'text_color' => '#1E1E1E',
836 'text_color_on_primary' => '#FFFFFF',
837 'field_spacing' => 'medium',
838 'submit_button_alignment' => 'left',
839 'bg_type' => 'color',
840 'bg_color' => '#ffffff',
841 'bg_image' => '',
842 'bg_image_position' => [
843 'x' => 0.5,
844 'y' => 0.5,
845 ],
846 'bg_image_attachment' => 'scroll',
847 'bg_image_repeat' => 'no-repeat',
848 'bg_image_size' => 'cover',
849 'bg_image_size_custom' => 100, // Image width when set to custom.
850 'bg_image_size_custom_unit' => '%',
851 'gradient_type' => 'basic',
852 'bg_gradient_type' => 'linear',
853 'bg_gradient_color_1' => '#FFC9B2',
854 'bg_gradient_color_2' => '#C7CBFF',
855 'bg_gradient_angle' => 90,
856 'bg_gradient_location_1' => 0,
857 'bg_gradient_location_2' => 100,
858 'bg_overlay_size' => 'cover',
859 'bg_gradient_overlay_type' => '',
860 'bg_overlay_opacity' => 1,
861 'bg_overlay_image' => '',
862 'bg_image_overlay_color' => '#FFFFFF75',
863 'bg_overlay_custom_size_unit' => '%',
864 'bg_overlay_custom_size' => 100,
865 'bg_overlay_position' => [
866 'x' => 0.5,
867 'y' => 0.5,
868 ],
869 'bg_overlay_attachment' => 'scroll',
870 'bg_overlay_repeat' => 'no-repeat',
871 'bg_overlay_blend_mode' => 'normal',
872 // Gradient Overlay Properties.
873 'overlay_gradient_type' => 'basic',
874 'bg_overlay_gradient_type' => 'linear',
875 'bg_overlay_gradient_color_1' => '#FFC9B2',
876 'bg_overlay_gradient_color_2' => '#C7CBFF',
877 'bg_overlay_gradient_angle' => 90,
878 'bg_overlay_gradient_location_1' => 0,
879 'bg_overlay_gradient_location_2' => 100,
880 // Form Properties.
881 // Padding.
882 'form_padding_top' => 0,
883 'form_padding_right' => 0,
884 'form_padding_bottom' => 0,
885 'form_padding_left' => 0,
886 'form_padding_unit' => 'px',
887 'form_padding_link' => true,
888 // Border Radius.
889 'form_border_radius_top' => 0,
890 'form_border_radius_right' => 0,
891 'form_border_radius_bottom' => 0,
892 'form_border_radius_left' => 0,
893 'form_border_radius_unit' => 'px',
894 'form_border_radius_link' => true,
895 // Form Properties.
896 // Padding.
897 'instant_form_padding_top' => 32,
898 'instant_form_padding_right' => 32,
899 'instant_form_padding_bottom' => 32,
900 'instant_form_padding_left' => 32,
901 'instant_form_padding_unit' => 'px',
902 'instant_form_padding_link' => true,
903 // Border Radius.
904 'instant_form_border_radius_top' => 12,
905 'instant_form_border_radius_right' => 12,
906 'instant_form_border_radius_bottom' => 12,
907 'instant_form_border_radius_left' => 12,
908 'instant_form_border_radius_unit' => 'px',
909 'instant_form_border_radius_link' => true,
910 // Disable default SureForms styling.
911 'disable_default_styles' => false,
912 ],
913 ]
914 );
915
916 // Email notification Metas.
917 register_post_meta(
918 'sureforms_form',
919 '_srfm_email_notification',
920 [
921 'single' => true,
922 'type' => 'array',
923 'auth_callback' => static function() {
924 return Helper::current_user_can();
925 },
926 'sanitize_callback' => static function( $meta_value ) {
927 if ( ! is_array( $meta_value ) ) {
928 return [];
929 }
930 $sanitized = [];
931 foreach ( $meta_value as $item ) {
932 if ( ! is_array( $item ) ) {
933 continue;
934 }
935 $sanitized[] = [
936 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
937 'status' => isset( $item['status'] ) ? filter_var( $item['status'], FILTER_VALIDATE_BOOLEAN ) : false,
938 'is_raw_format' => isset( $item['is_raw_format'] ) ? filter_var( $item['is_raw_format'], FILTER_VALIDATE_BOOLEAN ) : false,
939 'name' => isset( $item['name'] ) ? sanitize_text_field( $item['name'] ) : '',
940 'email_to' => isset( $item['email_to'] ) ? sanitize_text_field( $item['email_to'] ) : '',
941 'email_reply_to' => isset( $item['email_reply_to'] ) ? sanitize_text_field( $item['email_reply_to'] ) : '',
942 'from_name' => isset( $item['from_name'] ) ? sanitize_text_field( $item['from_name'] ) : '',
943 'from_email' => isset( $item['from_email'] ) ? sanitize_text_field( $item['from_email'] ) : '',
944 'email_cc' => isset( $item['email_cc'] ) ? sanitize_text_field( $item['email_cc'] ) : '',
945 'email_bcc' => isset( $item['email_bcc'] ) ? sanitize_text_field( $item['email_bcc'] ) : '',
946 'subject' => isset( $item['subject'] ) ? sanitize_text_field( $item['subject'] ) : '',
947 'email_body' => isset( $item['email_body'] ) ? wp_kses_post( $item['email_body'] ) : '',
948 ];
949 }
950 return $sanitized;
951 },
952 'show_in_rest' => [
953 'schema' => [
954 'type' => 'array',
955 'context' => [ 'edit' ],
956 'items' => [
957 'type' => 'object',
958 'properties' => [
959 'id' => [
960 'type' => 'integer',
961 ],
962 'status' => [
963 'type' => 'boolean',
964 ],
965 'is_raw_format' => [
966 'type' => 'boolean',
967 ],
968 'name' => [
969 'type' => 'string',
970 ],
971 'email_to' => [
972 'type' => 'string',
973 ],
974 'email_reply_to' => [
975 'type' => 'string',
976 ],
977 'from_name' => [
978 'type' => 'string',
979 ],
980 'from_email' => [
981 'type' => 'string',
982 ],
983 'email_cc' => [
984 'type' => 'string',
985 ],
986 'email_bcc' => [
987 'type' => 'string',
988 ],
989 'subject' => [
990 'type' => 'string',
991 ],
992 'email_body' => [
993 'type' => 'string',
994 ],
995 ],
996 ],
997 ],
998 ],
999 'default' => [
1000 [
1001 'id' => 1,
1002 'status' => true,
1003 'is_raw_format' => false,
1004 'name' => __( 'Admin Notification Email', 'sureforms' ),
1005 'email_to' => '{admin_email}',
1006 'email_reply_to' => '{admin_email}',
1007 'from_name' => '{site_title}',
1008 'from_email' => '{admin_email}',
1009 'email_cc' => '{admin_email}',
1010 'email_bcc' => '{admin_email}',
1011 'subject' => sprintf( /* translators: %s: Form title smart tag */ __( 'New Form Submission - %s', 'sureforms' ), '{form_title}' ),
1012 'email_body' => '{all_data}',
1013 ],
1014 ],
1015 ]
1016 );
1017
1018 // Compliance Settings metas.
1019 register_post_meta(
1020 'sureforms_form',
1021 '_srfm_compliance',
1022 [
1023 'single' => true,
1024 'type' => 'array',
1025 'auth_callback' => static function() {
1026 return Helper::current_user_can();
1027 },
1028 'sanitize_callback' => static function( $meta_value ) {
1029 if ( ! is_array( $meta_value ) ) {
1030 return [];
1031 }
1032 $sanitized = [];
1033 foreach ( $meta_value as $item ) {
1034 if ( ! is_array( $item ) ) {
1035 continue;
1036 }
1037 $sanitized[] = [
1038 'id' => isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '',
1039 'gdpr' => isset( $item['gdpr'] ) ? filter_var( $item['gdpr'], FILTER_VALIDATE_BOOLEAN ) : false,
1040 'do_not_store_entries' => isset( $item['do_not_store_entries'] ) ? filter_var( $item['do_not_store_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1041 'auto_delete_entries' => isset( $item['auto_delete_entries'] ) ? filter_var( $item['auto_delete_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1042 'auto_delete_days' => isset( $item['auto_delete_days'] ) ? sanitize_text_field( $item['auto_delete_days'] ) : '',
1043 ];
1044 }
1045 return $sanitized;
1046 },
1047 'show_in_rest' => [
1048 'schema' => [
1049 'type' => 'array',
1050 'context' => [ 'edit' ],
1051 'items' => [
1052 'type' => 'object',
1053 'properties' => [
1054 'id' => [
1055 'type' => 'string',
1056 ],
1057 'gdpr' => [
1058 'type' => 'boolean',
1059 ],
1060 'do_not_store_entries' => [
1061 'type' => 'boolean',
1062 ],
1063 'auto_delete_entries' => [
1064 'type' => 'boolean',
1065 ],
1066 'auto_delete_days' => [
1067 'type' => 'string',
1068 ],
1069 ],
1070 ],
1071 ],
1072 ],
1073 'default' => [
1074 [
1075 'id' => 'gdpr',
1076 'gdpr' => false,
1077 'do_not_store_entries' => false,
1078 'auto_delete_entries' => false,
1079 'auto_delete_days' => '',
1080 ],
1081 ],
1082 ]
1083 );
1084
1085 ob_start();
1086 ?>
1087 <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>
1088 <?php
1089 $default_confirmation_message = ob_get_clean();
1090
1091 // form confirmation.
1092 register_post_meta(
1093 'sureforms_form',
1094 '_srfm_form_confirmation',
1095 [
1096 'single' => true,
1097 'type' => 'array',
1098 'auth_callback' => static function() {
1099 return Helper::current_user_can();
1100 },
1101 'sanitize_callback' => static function( $meta_value ) {
1102 if ( ! is_array( $meta_value ) ) {
1103 return [];
1104 }
1105 $sanitized = [];
1106 foreach ( $meta_value as $item ) {
1107 if ( ! is_array( $item ) ) {
1108 continue;
1109 }
1110 $sanitized_item = [
1111 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
1112 'confirmation_type' => isset( $item['confirmation_type'] ) ? sanitize_text_field( $item['confirmation_type'] ) : '',
1113 'page_url' => isset( $item['page_url'] ) ? esc_url_raw( $item['page_url'] ) : '',
1114 'custom_url' => isset( $item['custom_url'] ) ? esc_url_raw( $item['custom_url'] ) : '',
1115 'message' => isset( $item['message'] ) ? wp_kses_post( $item['message'] ) : '',
1116 'submission_action' => isset( $item['submission_action'] ) ? sanitize_text_field( $item['submission_action'] ) : '',
1117 'enable_query_params' => isset( $item['enable_query_params'] ) ? filter_var( $item['enable_query_params'], FILTER_VALIDATE_BOOLEAN ) : false,
1118 'query_params' => isset( $item['query_params'] ) && is_array( $item['query_params'] )
1119 ? array_map(
1120 static function ( $pair ) {
1121 if ( ! is_array( $pair ) ) {
1122 return [];
1123 }
1124 $key = key( $pair );
1125 $value = current( $pair );
1126
1127 return [
1128 sanitize_text_field( Helper::get_string_value( $key ) ) => sanitize_text_field( Helper::get_string_value( $value ) ),
1129 ];
1130 },
1131 $item['query_params']
1132 )
1133 : [],
1134 ];
1135
1136 $sanitized_item = apply_filters( 'srfm_form_confirmation_params', $sanitized_item, $item );
1137
1138 $sanitized[] = $sanitized_item;
1139 }
1140 return $sanitized;
1141 },
1142 'show_in_rest' => [
1143 'schema' => [
1144 'type' => 'array',
1145 'context' => [ 'edit' ],
1146 'items' => [
1147 'type' => 'object',
1148 'properties' => [
1149 'id' => [
1150 'type' => 'integer',
1151 ],
1152 'confirmation_type' => [
1153 'type' => 'string',
1154 ],
1155 'page_url' => [
1156 'type' => 'string',
1157 ],
1158 'custom_url' => [
1159 'type' => 'string',
1160 ],
1161 'message' => [
1162 'type' => 'string',
1163 ],
1164 'submission_action' => [
1165 'type' => 'string',
1166 ],
1167 'enable_query_params' => [
1168 'type' => 'boolean',
1169 ],
1170 'query_params' => [
1171 'type' => 'array',
1172 ],
1173 ],
1174 ],
1175 ],
1176 ],
1177 'default' => [
1178 [
1179 'id' => 1,
1180 'confirmation_type' => 'same page',
1181 'page_url' => '',
1182 'custom_url' => '',
1183 'message' => $default_confirmation_message,
1184 'submission_action' => 'hide form',
1185 ],
1186 ],
1187 ]
1188 );
1189
1190 // conditional logic.
1191 do_action( 'srfm_register_conditional_logic_post_meta' );
1192 /**
1193 * Hook for registering additional Post Meta
1194 */
1195 do_action( 'srfm_register_additional_post_meta' );
1196
1197 register_meta(
1198 'post',
1199 '_srfm_form_restriction',
1200 [
1201 'type' => 'string', // Will store as JSON string.
1202 'single' => true, // Store as single value.
1203 'show_in_rest' => [
1204 'schema' => [
1205 'type' => 'string',
1206 'context' => [ 'edit' ],
1207 ],
1208 ],
1209 // Custom callback to sanitize the data.
1210 'sanitize_callback' => [ $this, 'sanitize_form_restriction_data' ],
1211 'object_subtype' => SRFM_FORMS_POST_TYPE,
1212 'auth_callback' => static function () {
1213 return Helper::current_user_can();
1214 },
1215 'default' => wp_json_encode(
1216 [
1217 'status' => false,
1218 'maxEntries' => 0,
1219 'date' => '',
1220 'hours' => '12',
1221 'minutes' => '00',
1222 'meridiem' => 'AM',
1223 'message' => Translatable::get_default_form_restriction_message(),
1224 // Form Scheduling meta.
1225 'schedulingStatus' => false,
1226 'startDate' => '',
1227 'startHours' => '12',
1228 'startMinutes' => '00',
1229 'startMeridiem' => 'AM',
1230 'schedulingNotStartedMessage' => __( 'This form is not yet available. Check back after the scheduled start time.', 'sureforms' ),
1231 'schedulingEndedMessage' => __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1232 ]
1233 ),
1234 ]
1235 );
1236 }
1237
1238 /**
1239 * Sanitizes the form restriction data.
1240 *
1241 * @param mixed $meta_value The meta value to sanitize.
1242 * @return string|false Sanitized JSON string.
1243 */
1244 public function sanitize_form_restriction_data( $meta_value ) {
1245 if ( empty( $meta_value ) || ! is_string( $meta_value ) ) {
1246 return wp_json_encode( [] );
1247 }
1248
1249 $meta_value = json_decode( $meta_value, true );
1250
1251 if ( ! is_array( $meta_value ) || json_last_error() !== JSON_ERROR_NONE ) {
1252 // If the JSON is invalid, return an empty array as JSON.
1253 return wp_json_encode( [] );
1254 }
1255
1256 $sanitized = [
1257 'status' => isset( $meta_value['status'] ) ? wp_validate_boolean( $meta_value['status'] ) : false,
1258 'maxEntries' => isset( $meta_value['maxEntries'] ) ? absint( $meta_value['maxEntries'] ) : 0,
1259 'date' => isset( $meta_value['date'] ) ? sanitize_text_field( $meta_value['date'] ) : '',
1260 'hours' => isset( $meta_value['hours'] ) ? sanitize_text_field( $meta_value['hours'] ) : '12',
1261 'minutes' => isset( $meta_value['minutes'] ) ? sanitize_text_field( $meta_value['minutes'] ) : '00',
1262 'meridiem' => isset( $meta_value['meridiem'] ) ? sanitize_text_field( $meta_value['meridiem'] ) : 'AM',
1263 'message' => isset( $meta_value['message'] ) ? sanitize_textarea_field( $meta_value['message'] ) : Translatable::get_default_form_restriction_message(),
1264 // Form Scheduling meta.
1265 'schedulingStatus' => isset( $meta_value['schedulingStatus'] ) ? wp_validate_boolean( $meta_value['schedulingStatus'] ) : false,
1266 'startDate' => isset( $meta_value['startDate'] ) ? sanitize_text_field( $meta_value['startDate'] ) : '',
1267 'startHours' => isset( $meta_value['startHours'] ) ? sanitize_text_field( $meta_value['startHours'] ) : '12',
1268 'startMinutes' => isset( $meta_value['startMinutes'] ) ? sanitize_text_field( $meta_value['startMinutes'] ) : '00',
1269 'startMeridiem' => isset( $meta_value['startMeridiem'] ) ? sanitize_text_field( $meta_value['startMeridiem'] ) : 'AM',
1270 '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' ),
1271 'schedulingEndedMessage' => isset( $meta_value['schedulingEndedMessage'] ) ? sanitize_textarea_field( $meta_value['schedulingEndedMessage'] ) : __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1272 ];
1273
1274 // Validate scheduling: start date/time must be before end date/time.
1275 if ( $sanitized['schedulingStatus'] && ! empty( $sanitized['startDate'] ) && ! empty( $sanitized['date'] ) ) {
1276 $start_datetime = $this->create_datetime_object(
1277 $sanitized['startDate'],
1278 $sanitized['startHours'],
1279 $sanitized['startMinutes'],
1280 $sanitized['startMeridiem']
1281 );
1282
1283 $end_datetime = $this->create_datetime_object(
1284 $sanitized['date'],
1285 $sanitized['hours'],
1286 $sanitized['minutes'],
1287 $sanitized['meridiem']
1288 );
1289
1290 // If start date/time is not before end date/time, disable scheduling.
1291 if ( $start_datetime && $end_datetime && $start_datetime >= $end_datetime ) {
1292 // Disable scheduling status due to invalid date range.
1293 $sanitized['schedulingStatus'] = false;
1294 }
1295 }
1296
1297 // Return the sanitized data as a JSON string.
1298 return wp_json_encode( $sanitized );
1299 }
1300
1301 /**
1302 * Custom Shortcode.
1303 *
1304 * @param array<mixed> $atts Attributes.
1305 * @return string|false. $content Post Content.
1306 * @since 0.0.1
1307 */
1308 public function forms_shortcode( $atts ) {
1309 $atts = shortcode_atts(
1310 [
1311 'id' => '',
1312 'show_title' => true,
1313 ],
1314 $atts
1315 );
1316
1317 $id = intval( $atts['id'] );
1318 $post = get_post( $id );
1319
1320 if ( ! empty( $id ) && $post && ( 'publish' === $post->post_status || 'protected' === $post->post_status ) ) {
1321 return Generate_Form_Markup::get_form_markup( $id, ! filter_var( $atts['show_title'], FILTER_VALIDATE_BOOLEAN ), '', 'post', true );
1322 }
1323
1324 return esc_html__( 'This form has been deleted or is unavailable.', 'sureforms' );
1325 }
1326
1327 /**
1328 * Redirect to home page if instant form is not enabled.
1329 *
1330 * @since 0.0.1
1331 * @return void
1332 */
1333 public function srfm_instant_form_redirect() {
1334
1335 $form_id = Helper::get_integer_value( get_the_ID() );
1336
1337 $instant_form_settings = Helper::get_array_value( Helper::get_post_meta( $form_id, '_srfm_instant_form_settings' ) );
1338 $enable_instant_form = ! empty( $instant_form_settings['enable_instant_form'] ) ? boolval( $instant_form_settings['enable_instant_form'] ) : false;
1339
1340 if ( $enable_instant_form ) {
1341 return;
1342 }
1343
1344 $form_preview = '';
1345
1346 $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.
1347
1348 if ( $form_preview_attr ) {
1349 $form_preview = filter_var( $form_preview_attr, FILTER_VALIDATE_BOOLEAN );
1350 }
1351
1352 if ( is_singular( 'sureforms_form' ) && ! $form_preview && ! Helper::current_user_can() ) {
1353 wp_safe_redirect( home_url() );
1354 return;
1355 }
1356 }
1357
1358 /**
1359 * Restrict RankMath meta boxes in edit page.
1360 *
1361 * @since 0.0.5
1362 * @return void
1363 */
1364 public function restrict_data() {
1365 add_filter( 'rank_math/excluded_post_types', [ $this, 'unset_sureforms_post_type' ] );
1366 }
1367
1368 /**
1369 * Remove SureForms post type from RankMath and Yoast.
1370 *
1371 * @param array<mixed> $post_types Post types.
1372 * @since 0.0.5
1373 * @return array<mixed> $post_types Modified post types.
1374 */
1375 public function unset_sureforms_post_type( $post_types ) {
1376 return array_filter(
1377 $post_types,
1378 static function( $post_type ) {
1379 if ( is_array( $post_type ) && isset( $post_type['name'] ) ) {
1380 return SRFM_FORMS_POST_TYPE !== $post_type['name'];
1381 }
1382 return SRFM_FORMS_POST_TYPE !== $post_type;
1383 }
1384 );
1385 }
1386
1387 /**
1388 * Restrict unwanted insertions from the AIOSEO plugin.
1389 *
1390 * This method ensures that the SureForms post type is excluded from AIOSEO's
1391 * public post types unless the current page is related to AIOSEO settings.
1392 *
1393 * @return void
1394 * @since 1.6.0
1395 */
1396 public function restrict_in_aioseo_plugin() {
1397 /**
1398 * Checks if the AIOSEO plugin is installed and excludes the SureForms post type from AIOSEO's public post types.
1399 *
1400 * - Verifies the presence of the AIOSEO_DIR constant to ensure AIOSEO is installed.
1401 * - Allows AIOSEO functionality on its own settings pages by checking the `REQUEST_URI` and `page` query parameter.
1402 * - Excludes the SureForms post type from AIOSEO's public post types using the `aioseo_public_post_types` filter.
1403 *
1404 * Security Note:
1405 * - Nonce verification is intentionally skipped (`phpcs:ignore WordPress.Security.NonceVerification.Recommended`)
1406 * because this code is only performing a read operation to check the current request URI and query parameters.
1407 * It does not modify or process sensitive data, making nonce verification unnecessary in this context.
1408 */
1409 // Check if AIOSEO is installed by verifying the AIOSEO_DIR constant.
1410 if ( ! defined( 'AIOSEO_DIR' ) ) {
1411 return;
1412 }
1413
1414 // Allow AIOSEO functionality on its own settings pages.
1415 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
1416 $request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
1417 if ( strpos( $request_uri, 'admin.php' ) !== false ) {
1418 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.
1419 $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.
1420 if ( strpos( $page, 'aioseo' ) !== false ) {
1421 return;
1422 }
1423 }
1424 }
1425 }
1426
1427 // Exclude the SureForms post type from AIOSEO's public post types.
1428 add_filter( 'aioseo_public_post_types', [ $this, 'unset_sureforms_post_type' ] );
1429 }
1430
1431 /**
1432 * Creates a DateTime object from date string and time components.
1433 *
1434 * @param string $date_str Date string.
1435 * @param string $hours Hours in 12-hour format.
1436 * @param string $minutes Minutes.
1437 * @param string $meridiem AM or PM.
1438 * @since 2.4.0
1439 * @return \DateTime|null DateTime object or null if invalid.
1440 */
1441 private function create_datetime_object( $date_str, $hours, $minutes, $meridiem ) {
1442 if ( empty( $date_str ) ) {
1443 return null;
1444 }
1445
1446 try {
1447 $date = new \DateTime( $date_str );
1448
1449 // Convert 12-hour format to 24-hour format.
1450 $hour_24 = intval( $hours );
1451 if ( 'PM' === $meridiem && 12 !== $hour_24 ) {
1452 $hour_24 += 12;
1453 } elseif ( 'AM' === $meridiem && 12 === $hour_24 ) {
1454 $hour_24 = 0;
1455 }
1456
1457 $date->setTime( $hour_24, intval( $minutes ), 0 );
1458 return $date;
1459 } catch ( \Exception $e ) {
1460 return null;
1461 }
1462 }
1463
1464 /**
1465 * Restrict interference of other plugins with SureForms.
1466 *
1467 * @since 0.0.5
1468 * @return void
1469 */
1470 private function restrict_unwanted_insertions() {
1471 // Restrict RankMath metaboxes in edit page.
1472 add_action( 'cmb2_admin_init', [ $this, 'restrict_data' ] );
1473
1474 // Restrict Yoast columns.
1475 add_filter( 'wpseo_accessible_post_types', [ $this, 'unset_sureforms_post_type' ] );
1476 add_filter( 'wpseo_metabox_prio', '__return_false' );
1477
1478 // Restrict AIOSEO columns.
1479 $this->restrict_in_aioseo_plugin();
1480 }
1481 }
1482