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