PluginProbe ʕ •ᴥ•ʔ
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / trunk
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz vtrunk
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 4 days ago forms-data.php 5 months ago frontend-assets.php 4 days ago generate-form-markup.php 1 week ago gutenberg-hooks.php 1 week ago helper.php 4 days ago learn.php 4 months ago onboarding.php 2 months ago post-types.php 1 week ago rest-api.php 1 week ago smart-tags.php 4 days 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
1509 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 // Form Custom CSS meta.
452 register_post_meta(
453 'sureforms_form',
454 '_srfm_form_custom_css',
455 [
456 'show_in_rest' => [
457 'schema' => [
458 'type' => 'string',
459 'context' => [ 'edit' ],
460 ],
461 ],
462 'type' => 'string',
463 'single' => true,
464 'auth_callback' => static function() {
465 return Helper::current_user_can();
466 },
467 'sanitize_callback' => static function( $meta_value ) {
468 return wp_kses_post( $meta_value );
469 },
470 ]
471 );
472
473 // Get default values for meta keys.
474 $default_meta_keys = Create_New_Form::get_default_meta_keys();
475
476 foreach ( $metas as $meta => $type ) {
477 // Get default value if exists.
478 $default_value = $default_meta_keys[ $meta ] ?? null;
479
480 $meta_args = [
481 'show_in_rest' => [
482 'schema' => [
483 'type' => $type,
484 'context' => [ 'edit' ],
485 ],
486 ],
487 'single' => true,
488 'type' => $type,
489 'sanitize_callback' => 'sanitize_text_field',
490 'auth_callback' => static function() {
491 return Helper::current_user_can();
492 },
493 ];
494
495 // Add default value if it exists.
496 if ( null !== $default_value ) {
497 $meta_args['default'] = $default_value;
498 }
499
500 register_post_meta(
501 SRFM_FORMS_POST_TYPE,
502 $meta,
503 $meta_args
504 );
505 }
506
507 // Registers meta to handle values associated with form styling.
508 register_post_meta(
509 SRFM_FORMS_POST_TYPE,
510 '_srfm_instant_form_settings',
511 [
512 'single' => true,
513 'type' => 'object',
514 'auth_callback' => static function() {
515 return Helper::current_user_can();
516 },
517 'sanitize_callback' => static function( $meta_value ) {
518 if ( ! is_array( $meta_value ) ) {
519 return [];
520 }
521 return [
522 'site_logo' => isset( $meta_value['site_logo'] ) ? esc_url_raw( $meta_value['site_logo'] ) : '',
523 'site_logo_id' => isset( $meta_value['site_logo_id'] ) ? absint( $meta_value['site_logo_id'] ) : 0,
524 'cover_type' => isset( $meta_value['cover_type'] ) ? sanitize_text_field( $meta_value['cover_type'] ) : '',
525 'cover_color' => isset( $meta_value['cover_color'] ) ? sanitize_text_field( $meta_value['cover_color'] ) : '',
526 'cover_image' => isset( $meta_value['cover_image'] ) ? esc_url_raw( $meta_value['cover_image'] ) : '',
527 'cover_image_id' => isset( $meta_value['cover_image_id'] ) ? absint( $meta_value['cover_image_id'] ) : 0,
528 'bg_type' => isset( $meta_value['bg_type'] ) ? sanitize_text_field( $meta_value['bg_type'] ) : '',
529 'bg_color' => isset( $meta_value['bg_color'] ) ? sanitize_text_field( $meta_value['bg_color'] ) : '',
530 'bg_image' => isset( $meta_value['bg_image'] ) ? esc_url_raw( $meta_value['bg_image'] ) : '',
531 'bg_image_id' => isset( $meta_value['bg_image_id'] ) ? absint( $meta_value['bg_image_id'] ) : 0,
532 'enable_instant_form' => isset( $meta_value['enable_instant_form'] ) ? filter_var( $meta_value['enable_instant_form'], FILTER_VALIDATE_BOOLEAN ) : false,
533 'form_container_width' => isset( $meta_value['form_container_width'] ) ? absint( $meta_value['form_container_width'] ) : 620,
534 'single_page_form_title' => isset( $meta_value['single_page_form_title'] ) ? filter_var( $meta_value['single_page_form_title'], FILTER_VALIDATE_BOOLEAN ) : true,
535 '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,
536 ];
537 },
538 'show_in_rest' => [
539 'schema' => [
540 'type' => 'object',
541 'context' => [ 'edit' ],
542 'properties' => [
543 'site_logo' => [
544 'type' => 'string',
545 ],
546 'site_logo_id' => [
547 'type' => 'integer',
548 ],
549 // Form page banner settings.
550 'cover_type' => [
551 'type' => 'string',
552 ],
553 'cover_color' => [
554 'type' => 'string',
555 ],
556 'cover_image' => [
557 'type' => 'string',
558 ],
559 'cover_image_id' => [
560 'type' => 'integer',
561 ],
562 // Form page background settings.
563 'bg_type' => [
564 'type' => 'string',
565 ],
566 'bg_color' => [
567 'type' => 'string',
568 ],
569 'bg_image' => [
570 'type' => 'string',
571 ],
572 'bg_image_id' => [
573 'type' => 'integer',
574 ],
575 'enable_instant_form' => [
576 'type' => 'boolean',
577 ],
578 'form_container_width' => [
579 'type' => 'integer',
580 ],
581 'single_page_form_title' => [
582 'type' => 'boolean',
583 ],
584 'use_banner_as_page_background' => [
585 'type' => 'boolean',
586 ],
587 ],
588 ],
589 ],
590 'default' => [
591 'bg_type' => 'color',
592 'bg_color' => '#ffffff',
593 'bg_image' => '',
594 'site_logo' => '',
595 'cover_type' => 'color',
596 'cover_color' => '#111C44',
597 'cover_image' => '',
598 'enable_instant_form' => false,
599 'form_container_width' => 620,
600 'single_page_form_title' => true,
601 'use_banner_as_page_background' => false,
602 ],
603 ]
604 );
605
606 register_post_meta(
607 SRFM_FORMS_POST_TYPE,
608 '_srfm_forms_styling',
609 [
610 'single' => true,
611 'type' => 'object',
612 'auth_callback' => static function() {
613 return Helper::current_user_can();
614 },
615 'sanitize_callback' => static function( $meta_value ) {
616 return Helper::sanitize_by_type( $meta_value );
617 },
618 'show_in_rest' => [
619 'schema' => [
620 'type' => 'object',
621 'context' => [ 'edit' ],
622 'properties' => [
623 'primary_color' => [
624 'type' => 'string',
625 ],
626 'text_color' => [
627 'type' => 'string',
628 ],
629 'text_color_on_primary' => [
630 'type' => 'string',
631 ],
632 'field_spacing' => [
633 'type' => 'string',
634 ],
635 'submit_button_alignment' => [
636 'type' => 'string',
637 ],
638 'bg_type' => [
639 'type' => 'string',
640 ],
641 'bg_color' => [
642 'type' => 'string',
643 ],
644 'bg_image' => [
645 'type' => 'string',
646 ],
647 'bg_image_id' => [
648 'type' => 'integer',
649 ],
650 // Image Properties.
651 'bg_image_position' => [
652 'type' => 'object',
653 'properties' => [
654 'x' => [
655 'type' => 'number',
656 'format' => 'float',
657 ],
658 'y' => [
659 'type' => 'number',
660 'format' => 'float',
661 ],
662 ],
663 ],
664 'bg_image_attachment' => [
665 'type' => 'string',
666 ],
667 'bg_image_repeat' => [
668 'type' => 'string',
669 ],
670 'bg_image_size' => [
671 'type' => 'string',
672 ],
673 'bg_image_size_custom' => [
674 'type' => 'integer',
675 ],
676 'bg_image_size_custom_unit' => [
677 'type' => 'string',
678 ],
679 // Gradient Properties.
680 'bg_gradient' => [
681 'type' => 'string',
682 ],
683 'gradient_type' => [
684 'type' => 'string',
685 ],
686 'bg_gradient_type' => [
687 'type' => 'string',
688 ],
689 'bg_gradient_color_1' => [
690 'type' => 'string',
691 ],
692 'bg_gradient_color_2' => [
693 'type' => 'string',
694 ],
695 'bg_gradient_angle' => [
696 'type' => 'integer',
697 ],
698 'bg_gradient_location_1' => [
699 'type' => 'integer',
700 ],
701 'bg_gradient_location_2' => [
702 'type' => 'integer',
703 ],
704 // Overlay Properties.
705 'bg_overlay_size' => [
706 'type' => 'string',
707 ],
708 'bg_gradient_overlay_type' => [
709 'type' => 'string',
710 ],
711 'bg_overlay_opacity' => [
712 'type' => 'number',
713 ],
714 'bg_overlay_image' => [
715 'type' => 'string',
716 ],
717 'bg_overlay_image_id' => [
718 'type' => 'integer',
719 ],
720 'bg_image_overlay_color' => [
721 'type' => 'string',
722 ],
723 'bg_overlay_custom_size_unit' => [
724 'type' => 'string',
725 ],
726 'bg_overlay_custom_size' => [
727 'type' => 'integer',
728 ],
729 'bg_overlay_blend_mode' => [
730 'type' => 'string',
731 ],
732 'bg_overlay_position' => [
733 'type' => 'object',
734 'properties' => [
735 'x' => [
736 'type' => 'number',
737 'format' => 'float',
738 ],
739 'y' => [
740 'type' => 'number',
741 'format' => 'float',
742 ],
743 ],
744 ],
745 'bg_overlay_attachment' => [
746 'type' => 'string',
747 ],
748 'bg_overlay_repeat' => [
749 'type' => 'string',
750 ],
751 // Gradient Overlay Properties.
752 'bg_overlay_gradient' => [
753 'type' => 'string',
754 ],
755 'overlay_gradient_type' => [
756 'type' => 'string',
757 ],
758 'bg_overlay_gradient_type' => [
759 'type' => 'string',
760 ],
761 'bg_overlay_gradient_color_1' => [
762 'type' => 'string',
763 ],
764 'bg_overlay_gradient_color_2' => [
765 'type' => 'string',
766 ],
767 'bg_overlay_gradient_angle' => [
768 'type' => 'integer',
769 ],
770 'bg_overlay_gradient_location_1' => [
771 'type' => 'integer',
772 ],
773 'bg_overlay_gradient_location_2' => [
774 'type' => 'integer',
775 ],
776 // Form Padding.
777 'form_padding_top' => [
778 'type' => 'number',
779 ],
780 'form_padding_right' => [
781 'type' => 'number',
782 ],
783 'form_padding_bottom' => [
784 'type' => 'number',
785 ],
786 'form_padding_left' => [
787 'type' => 'number',
788 ],
789 'form_padding_unit' => [
790 'type' => 'string',
791 ],
792 'form_padding_link' => [
793 'type' => 'boolean',
794 ],
795 // Border Radius.
796 'form_border_radius_top' => [
797 'type' => 'number',
798 ],
799 'form_border_radius_right' => [
800 'type' => 'number',
801 ],
802 'form_border_radius_bottom' => [
803 'type' => 'number',
804 ],
805 'form_border_radius_left' => [
806 'type' => 'number',
807 ],
808 'form_border_radius_unit' => [
809 'type' => 'string',
810 ],
811 'form_border_radius_link' => [
812 'type' => 'boolean',
813 ],
814 // Form Padding and Border Radius.
815 // Form Padding.
816 'instant_form_padding_top' => [
817 'type' => 'number',
818 ],
819 'instant_form_padding_right' => [
820 'type' => 'number',
821 ],
822 'instant_form_padding_bottom' => [
823 'type' => 'number',
824 ],
825 'instant_form_padding_left' => [
826 'type' => 'number',
827 ],
828 'instant_form_padding_unit' => [
829 'type' => 'string',
830 ],
831 'instant_form_padding_link' => [
832 'type' => 'boolean',
833 ],
834 // Border Radius.
835 'instant_form_border_radius_top' => [
836 'type' => 'number',
837 ],
838 'instant_form_border_radius_right' => [
839 'type' => 'number',
840 ],
841 'instant_form_border_radius_bottom' => [
842 'type' => 'number',
843 ],
844 'instant_form_border_radius_left' => [
845 'type' => 'number',
846 ],
847 'instant_form_border_radius_unit' => [
848 'type' => 'string',
849 ],
850 'instant_form_border_radius_link' => [
851 'type' => 'boolean',
852 ],
853 // Disable default SureForms styling.
854 'disable_default_styles' => [
855 'type' => 'boolean',
856 ],
857 ],
858 ],
859 ],
860 'default' => [
861 'primary_color' => '#111C44',
862 'text_color' => '#1E1E1E',
863 'text_color_on_primary' => '#FFFFFF',
864 'field_spacing' => 'medium',
865 'submit_button_alignment' => 'left',
866 'bg_type' => 'color',
867 'bg_color' => '#ffffff',
868 'bg_image' => '',
869 'bg_image_position' => [
870 'x' => 0.5,
871 'y' => 0.5,
872 ],
873 'bg_image_attachment' => 'scroll',
874 'bg_image_repeat' => 'no-repeat',
875 'bg_image_size' => 'cover',
876 'bg_image_size_custom' => 100, // Image width when set to custom.
877 'bg_image_size_custom_unit' => '%',
878 'gradient_type' => 'basic',
879 'bg_gradient_type' => 'linear',
880 'bg_gradient_color_1' => '#FFC9B2',
881 'bg_gradient_color_2' => '#C7CBFF',
882 'bg_gradient_angle' => 90,
883 'bg_gradient_location_1' => 0,
884 'bg_gradient_location_2' => 100,
885 'bg_overlay_size' => 'cover',
886 'bg_gradient_overlay_type' => '',
887 'bg_overlay_opacity' => 1,
888 'bg_overlay_image' => '',
889 'bg_image_overlay_color' => '#FFFFFF75',
890 'bg_overlay_custom_size_unit' => '%',
891 'bg_overlay_custom_size' => 100,
892 'bg_overlay_position' => [
893 'x' => 0.5,
894 'y' => 0.5,
895 ],
896 'bg_overlay_attachment' => 'scroll',
897 'bg_overlay_repeat' => 'no-repeat',
898 'bg_overlay_blend_mode' => 'normal',
899 // Gradient Overlay Properties.
900 'overlay_gradient_type' => 'basic',
901 'bg_overlay_gradient_type' => 'linear',
902 'bg_overlay_gradient_color_1' => '#FFC9B2',
903 'bg_overlay_gradient_color_2' => '#C7CBFF',
904 'bg_overlay_gradient_angle' => 90,
905 'bg_overlay_gradient_location_1' => 0,
906 'bg_overlay_gradient_location_2' => 100,
907 // Form Properties.
908 // Padding.
909 'form_padding_top' => 0,
910 'form_padding_right' => 0,
911 'form_padding_bottom' => 0,
912 'form_padding_left' => 0,
913 'form_padding_unit' => 'px',
914 'form_padding_link' => true,
915 // Border Radius.
916 'form_border_radius_top' => 0,
917 'form_border_radius_right' => 0,
918 'form_border_radius_bottom' => 0,
919 'form_border_radius_left' => 0,
920 'form_border_radius_unit' => 'px',
921 'form_border_radius_link' => true,
922 // Form Properties.
923 // Padding.
924 'instant_form_padding_top' => 32,
925 'instant_form_padding_right' => 32,
926 'instant_form_padding_bottom' => 32,
927 'instant_form_padding_left' => 32,
928 'instant_form_padding_unit' => 'px',
929 'instant_form_padding_link' => true,
930 // Border Radius.
931 'instant_form_border_radius_top' => 12,
932 'instant_form_border_radius_right' => 12,
933 'instant_form_border_radius_bottom' => 12,
934 'instant_form_border_radius_left' => 12,
935 'instant_form_border_radius_unit' => 'px',
936 'instant_form_border_radius_link' => true,
937 // Disable default SureForms styling.
938 'disable_default_styles' => false,
939 ],
940 ]
941 );
942
943 // Email notification Metas.
944 register_post_meta(
945 'sureforms_form',
946 '_srfm_email_notification',
947 [
948 'single' => true,
949 'type' => 'array',
950 'auth_callback' => static function() {
951 return Helper::current_user_can();
952 },
953 'sanitize_callback' => static function( $meta_value ) {
954 if ( ! is_array( $meta_value ) ) {
955 return [];
956 }
957 $sanitized = [];
958 foreach ( $meta_value as $item ) {
959 if ( ! is_array( $item ) ) {
960 continue;
961 }
962 $sanitized[] = [
963 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
964 'status' => isset( $item['status'] ) ? filter_var( $item['status'], FILTER_VALIDATE_BOOLEAN ) : false,
965 'is_raw_format' => isset( $item['is_raw_format'] ) ? filter_var( $item['is_raw_format'], FILTER_VALIDATE_BOOLEAN ) : false,
966 'name' => isset( $item['name'] ) ? sanitize_text_field( $item['name'] ) : '',
967 'email_to' => isset( $item['email_to'] ) ? sanitize_text_field( $item['email_to'] ) : '',
968 'email_reply_to' => isset( $item['email_reply_to'] ) ? sanitize_text_field( $item['email_reply_to'] ) : '',
969 'from_name' => isset( $item['from_name'] ) ? sanitize_text_field( $item['from_name'] ) : '',
970 'from_email' => isset( $item['from_email'] ) ? sanitize_text_field( $item['from_email'] ) : '',
971 'email_cc' => isset( $item['email_cc'] ) ? sanitize_text_field( $item['email_cc'] ) : '',
972 'email_bcc' => isset( $item['email_bcc'] ) ? sanitize_text_field( $item['email_bcc'] ) : '',
973 'subject' => isset( $item['subject'] ) ? sanitize_text_field( $item['subject'] ) : '',
974 'email_body' => isset( $item['email_body'] ) ? wp_kses_post( $item['email_body'] ) : '',
975 ];
976 }
977 return $sanitized;
978 },
979 'show_in_rest' => [
980 'schema' => [
981 'type' => 'array',
982 'context' => [ 'edit' ],
983 'items' => [
984 'type' => 'object',
985 'properties' => [
986 'id' => [
987 'type' => 'integer',
988 ],
989 'status' => [
990 'type' => 'boolean',
991 ],
992 'is_raw_format' => [
993 'type' => 'boolean',
994 ],
995 'name' => [
996 'type' => 'string',
997 ],
998 'email_to' => [
999 'type' => 'string',
1000 ],
1001 'email_reply_to' => [
1002 'type' => 'string',
1003 ],
1004 'from_name' => [
1005 'type' => 'string',
1006 ],
1007 'from_email' => [
1008 'type' => 'string',
1009 ],
1010 'email_cc' => [
1011 'type' => 'string',
1012 ],
1013 'email_bcc' => [
1014 'type' => 'string',
1015 ],
1016 'subject' => [
1017 'type' => 'string',
1018 ],
1019 'email_body' => [
1020 'type' => 'string',
1021 ],
1022 ],
1023 ],
1024 ],
1025 ],
1026 'default' => [
1027 [
1028 'id' => 1,
1029 'status' => true,
1030 'is_raw_format' => false,
1031 'name' => __( 'Admin Notification Email', 'sureforms' ),
1032 'email_to' => '{admin_email}',
1033 'email_reply_to' => '{admin_email}',
1034 'from_name' => '{site_title}',
1035 'from_email' => '{admin_email}',
1036 'email_cc' => '{admin_email}',
1037 'email_bcc' => '{admin_email}',
1038 'subject' => sprintf( /* translators: %s: Form title smart tag */ __( 'New Form Submission - %s', 'sureforms' ), '{form_title}' ),
1039 'email_body' => '{all_data}',
1040 ],
1041 ],
1042 ]
1043 );
1044
1045 // Compliance Settings metas.
1046 register_post_meta(
1047 'sureforms_form',
1048 '_srfm_compliance',
1049 [
1050 'single' => true,
1051 'type' => 'array',
1052 'auth_callback' => static function() {
1053 return Helper::current_user_can();
1054 },
1055 'sanitize_callback' => static function( $meta_value ) {
1056 if ( ! is_array( $meta_value ) ) {
1057 return [];
1058 }
1059 $sanitized = [];
1060 foreach ( $meta_value as $item ) {
1061 if ( ! is_array( $item ) ) {
1062 continue;
1063 }
1064 $sanitized[] = [
1065 'id' => isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '',
1066 'gdpr' => isset( $item['gdpr'] ) ? filter_var( $item['gdpr'], FILTER_VALIDATE_BOOLEAN ) : false,
1067 'do_not_store_entries' => isset( $item['do_not_store_entries'] ) ? filter_var( $item['do_not_store_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1068 'auto_delete_entries' => isset( $item['auto_delete_entries'] ) ? filter_var( $item['auto_delete_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1069 'auto_delete_days' => isset( $item['auto_delete_days'] ) ? sanitize_text_field( $item['auto_delete_days'] ) : '',
1070 ];
1071 }
1072 return $sanitized;
1073 },
1074 'show_in_rest' => [
1075 'schema' => [
1076 'type' => 'array',
1077 'context' => [ 'edit' ],
1078 'items' => [
1079 'type' => 'object',
1080 'properties' => [
1081 'id' => [
1082 'type' => 'string',
1083 ],
1084 'gdpr' => [
1085 'type' => 'boolean',
1086 ],
1087 'do_not_store_entries' => [
1088 'type' => 'boolean',
1089 ],
1090 'auto_delete_entries' => [
1091 'type' => 'boolean',
1092 ],
1093 'auto_delete_days' => [
1094 'type' => 'string',
1095 ],
1096 ],
1097 ],
1098 ],
1099 ],
1100 'default' => [
1101 [
1102 'id' => 'gdpr',
1103 'gdpr' => false,
1104 'do_not_store_entries' => false,
1105 'auto_delete_entries' => false,
1106 'auto_delete_days' => '',
1107 ],
1108 ],
1109 ]
1110 );
1111
1112 ob_start();
1113 ?>
1114 <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>
1115 <?php
1116 $default_confirmation_message = ob_get_clean();
1117
1118 // form confirmation.
1119 register_post_meta(
1120 'sureforms_form',
1121 '_srfm_form_confirmation',
1122 [
1123 'single' => true,
1124 'type' => 'array',
1125 'auth_callback' => static function() {
1126 return Helper::current_user_can();
1127 },
1128 'sanitize_callback' => static function( $meta_value ) {
1129 if ( ! is_array( $meta_value ) ) {
1130 return [];
1131 }
1132 $sanitized = [];
1133 foreach ( $meta_value as $item ) {
1134 if ( ! is_array( $item ) ) {
1135 continue;
1136 }
1137 $sanitized_item = [
1138 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
1139 'confirmation_type' => isset( $item['confirmation_type'] ) ? sanitize_text_field( $item['confirmation_type'] ) : '',
1140 'page_url' => isset( $item['page_url'] ) ? esc_url_raw( $item['page_url'] ) : '',
1141 'custom_url' => isset( $item['custom_url'] ) ? esc_url_raw( $item['custom_url'] ) : '',
1142 'message' => isset( $item['message'] ) ? wp_kses_post( $item['message'] ) : '',
1143 'submission_action' => isset( $item['submission_action'] ) ? sanitize_text_field( $item['submission_action'] ) : '',
1144 'enable_query_params' => isset( $item['enable_query_params'] ) ? filter_var( $item['enable_query_params'], FILTER_VALIDATE_BOOLEAN ) : false,
1145 'query_params' => isset( $item['query_params'] ) && is_array( $item['query_params'] )
1146 ? array_map(
1147 static function ( $pair ) {
1148 if ( ! is_array( $pair ) ) {
1149 return [];
1150 }
1151 $key = key( $pair );
1152 $value = current( $pair );
1153
1154 return [
1155 sanitize_text_field( Helper::get_string_value( $key ) ) => sanitize_text_field( Helper::get_string_value( $value ) ),
1156 ];
1157 },
1158 $item['query_params']
1159 )
1160 : [],
1161 ];
1162
1163 $sanitized_item = apply_filters( 'srfm_form_confirmation_params', $sanitized_item, $item );
1164
1165 $sanitized[] = $sanitized_item;
1166 }
1167 return $sanitized;
1168 },
1169 'show_in_rest' => [
1170 'schema' => [
1171 'type' => 'array',
1172 'context' => [ 'edit' ],
1173 'items' => [
1174 'type' => 'object',
1175 'properties' => [
1176 'id' => [
1177 'type' => 'integer',
1178 ],
1179 'confirmation_type' => [
1180 'type' => 'string',
1181 ],
1182 'page_url' => [
1183 'type' => 'string',
1184 ],
1185 'custom_url' => [
1186 'type' => 'string',
1187 ],
1188 'message' => [
1189 'type' => 'string',
1190 ],
1191 'submission_action' => [
1192 'type' => 'string',
1193 ],
1194 'enable_query_params' => [
1195 'type' => 'boolean',
1196 ],
1197 'query_params' => [
1198 'type' => 'array',
1199 ],
1200 ],
1201 ],
1202 ],
1203 ],
1204 'default' => [
1205 [
1206 'id' => 1,
1207 'confirmation_type' => 'same page',
1208 'page_url' => '',
1209 'custom_url' => '',
1210 'message' => $default_confirmation_message,
1211 'submission_action' => 'hide form',
1212 ],
1213 ],
1214 ]
1215 );
1216
1217 // conditional logic.
1218 do_action( 'srfm_register_conditional_logic_post_meta' );
1219 /**
1220 * Hook for registering additional Post Meta
1221 */
1222 do_action( 'srfm_register_additional_post_meta' );
1223
1224 register_meta(
1225 'post',
1226 '_srfm_form_restriction',
1227 [
1228 'type' => 'string', // Will store as JSON string.
1229 'single' => true, // Store as single value.
1230 'show_in_rest' => [
1231 'schema' => [
1232 'type' => 'string',
1233 'context' => [ 'edit' ],
1234 ],
1235 ],
1236 // Custom callback to sanitize the data.
1237 'sanitize_callback' => [ $this, 'sanitize_form_restriction_data' ],
1238 'object_subtype' => SRFM_FORMS_POST_TYPE,
1239 'auth_callback' => static function () {
1240 return Helper::current_user_can();
1241 },
1242 'default' => wp_json_encode(
1243 [
1244 'status' => false,
1245 'maxEntries' => 0,
1246 'date' => '',
1247 'hours' => '12',
1248 'minutes' => '00',
1249 'meridiem' => 'AM',
1250 'message' => Translatable::get_default_form_restriction_message(),
1251 // Form Scheduling meta.
1252 'schedulingStatus' => false,
1253 'startDate' => '',
1254 'startHours' => '12',
1255 'startMinutes' => '00',
1256 'startMeridiem' => 'AM',
1257 'schedulingNotStartedMessage' => __( 'This form is not yet available. Check back after the scheduled start time.', 'sureforms' ),
1258 'schedulingEndedMessage' => __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1259 ]
1260 ),
1261 ]
1262 );
1263 }
1264
1265 /**
1266 * Sanitizes the form restriction data.
1267 *
1268 * @param mixed $meta_value The meta value to sanitize.
1269 * @return string|false Sanitized JSON string.
1270 */
1271 public function sanitize_form_restriction_data( $meta_value ) {
1272 if ( empty( $meta_value ) || ! is_string( $meta_value ) ) {
1273 return wp_json_encode( [] );
1274 }
1275
1276 $meta_value = json_decode( $meta_value, true );
1277
1278 if ( ! is_array( $meta_value ) || json_last_error() !== JSON_ERROR_NONE ) {
1279 // If the JSON is invalid, return an empty array as JSON.
1280 return wp_json_encode( [] );
1281 }
1282
1283 $sanitized = [
1284 'status' => isset( $meta_value['status'] ) ? wp_validate_boolean( $meta_value['status'] ) : false,
1285 'maxEntries' => isset( $meta_value['maxEntries'] ) ? absint( $meta_value['maxEntries'] ) : 0,
1286 'date' => isset( $meta_value['date'] ) ? sanitize_text_field( $meta_value['date'] ) : '',
1287 'hours' => isset( $meta_value['hours'] ) ? sanitize_text_field( $meta_value['hours'] ) : '12',
1288 'minutes' => isset( $meta_value['minutes'] ) ? sanitize_text_field( $meta_value['minutes'] ) : '00',
1289 'meridiem' => isset( $meta_value['meridiem'] ) ? sanitize_text_field( $meta_value['meridiem'] ) : 'AM',
1290 'message' => isset( $meta_value['message'] ) ? sanitize_textarea_field( $meta_value['message'] ) : Translatable::get_default_form_restriction_message(),
1291 // Form Scheduling meta.
1292 'schedulingStatus' => isset( $meta_value['schedulingStatus'] ) ? wp_validate_boolean( $meta_value['schedulingStatus'] ) : false,
1293 'startDate' => isset( $meta_value['startDate'] ) ? sanitize_text_field( $meta_value['startDate'] ) : '',
1294 'startHours' => isset( $meta_value['startHours'] ) ? sanitize_text_field( $meta_value['startHours'] ) : '12',
1295 'startMinutes' => isset( $meta_value['startMinutes'] ) ? sanitize_text_field( $meta_value['startMinutes'] ) : '00',
1296 'startMeridiem' => isset( $meta_value['startMeridiem'] ) ? sanitize_text_field( $meta_value['startMeridiem'] ) : 'AM',
1297 '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' ),
1298 'schedulingEndedMessage' => isset( $meta_value['schedulingEndedMessage'] ) ? sanitize_textarea_field( $meta_value['schedulingEndedMessage'] ) : __( 'This form is closed. The submission period has ended.', 'sureforms' ),
1299 ];
1300
1301 // Validate scheduling: start date/time must be before end date/time.
1302 if ( $sanitized['schedulingStatus'] && ! empty( $sanitized['startDate'] ) && ! empty( $sanitized['date'] ) ) {
1303 $start_datetime = $this->create_datetime_object(
1304 $sanitized['startDate'],
1305 $sanitized['startHours'],
1306 $sanitized['startMinutes'],
1307 $sanitized['startMeridiem']
1308 );
1309
1310 $end_datetime = $this->create_datetime_object(
1311 $sanitized['date'],
1312 $sanitized['hours'],
1313 $sanitized['minutes'],
1314 $sanitized['meridiem']
1315 );
1316
1317 // If start date/time is not before end date/time, disable scheduling.
1318 if ( $start_datetime && $end_datetime && $start_datetime >= $end_datetime ) {
1319 // Disable scheduling status due to invalid date range.
1320 $sanitized['schedulingStatus'] = false;
1321 }
1322 }
1323
1324 // Return the sanitized data as a JSON string.
1325 return wp_json_encode( $sanitized );
1326 }
1327
1328 /**
1329 * Custom Shortcode.
1330 *
1331 * @param array<mixed> $atts Attributes.
1332 * @return string|false. $content Post Content.
1333 * @since 0.0.1
1334 */
1335 public function forms_shortcode( $atts ) {
1336 $atts = shortcode_atts(
1337 [
1338 'id' => '',
1339 'show_title' => true,
1340 ],
1341 $atts
1342 );
1343
1344 $id = intval( $atts['id'] );
1345 $post = get_post( $id );
1346
1347 if ( ! empty( $id ) && $post && ( 'publish' === $post->post_status || 'protected' === $post->post_status ) ) {
1348 return Generate_Form_Markup::get_form_markup( $id, ! filter_var( $atts['show_title'], FILTER_VALIDATE_BOOLEAN ), '', 'post', true );
1349 }
1350
1351 return esc_html__( 'This form has been deleted or is unavailable.', 'sureforms' );
1352 }
1353
1354 /**
1355 * Redirect to home page if instant form is not enabled.
1356 *
1357 * @since 0.0.1
1358 * @return void
1359 */
1360 public function srfm_instant_form_redirect() {
1361
1362 $form_id = Helper::get_integer_value( get_the_ID() );
1363
1364 $instant_form_settings = Helper::get_array_value( Helper::get_post_meta( $form_id, '_srfm_instant_form_settings' ) );
1365 $enable_instant_form = ! empty( $instant_form_settings['enable_instant_form'] ) ? boolval( $instant_form_settings['enable_instant_form'] ) : false;
1366
1367 if ( $enable_instant_form ) {
1368 return;
1369 }
1370
1371 $form_preview = '';
1372
1373 $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.
1374
1375 if ( $form_preview_attr ) {
1376 $form_preview = filter_var( $form_preview_attr, FILTER_VALIDATE_BOOLEAN );
1377 }
1378
1379 if ( is_singular( 'sureforms_form' ) && ! $form_preview && ! Helper::current_user_can() ) {
1380 wp_safe_redirect( home_url() );
1381 return;
1382 }
1383 }
1384
1385 /**
1386 * Restrict RankMath meta boxes in edit page.
1387 *
1388 * @since 0.0.5
1389 * @return void
1390 */
1391 public function restrict_data() {
1392 add_filter( 'rank_math/excluded_post_types', [ $this, 'unset_sureforms_post_type' ] );
1393 }
1394
1395 /**
1396 * Remove SureForms post type from RankMath and Yoast.
1397 *
1398 * @param array<mixed> $post_types Post types.
1399 * @since 0.0.5
1400 * @return array<mixed> $post_types Modified post types.
1401 */
1402 public function unset_sureforms_post_type( $post_types ) {
1403 return array_filter(
1404 $post_types,
1405 static function( $post_type ) {
1406 if ( is_array( $post_type ) && isset( $post_type['name'] ) ) {
1407 return SRFM_FORMS_POST_TYPE !== $post_type['name'];
1408 }
1409 return SRFM_FORMS_POST_TYPE !== $post_type;
1410 }
1411 );
1412 }
1413
1414 /**
1415 * Restrict unwanted insertions from the AIOSEO plugin.
1416 *
1417 * This method ensures that the SureForms post type is excluded from AIOSEO's
1418 * public post types unless the current page is related to AIOSEO settings.
1419 *
1420 * @return void
1421 * @since 1.6.0
1422 */
1423 public function restrict_in_aioseo_plugin() {
1424 /**
1425 * Checks if the AIOSEO plugin is installed and excludes the SureForms post type from AIOSEO's public post types.
1426 *
1427 * - Verifies the presence of the AIOSEO_DIR constant to ensure AIOSEO is installed.
1428 * - Allows AIOSEO functionality on its own settings pages by checking the `REQUEST_URI` and `page` query parameter.
1429 * - Excludes the SureForms post type from AIOSEO's public post types using the `aioseo_public_post_types` filter.
1430 *
1431 * Security Note:
1432 * - Nonce verification is intentionally skipped (`phpcs:ignore WordPress.Security.NonceVerification.Recommended`)
1433 * because this code is only performing a read operation to check the current request URI and query parameters.
1434 * It does not modify or process sensitive data, making nonce verification unnecessary in this context.
1435 */
1436 // Check if AIOSEO is installed by verifying the AIOSEO_DIR constant.
1437 if ( ! defined( 'AIOSEO_DIR' ) ) {
1438 return;
1439 }
1440
1441 // Allow AIOSEO functionality on its own settings pages.
1442 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
1443 $request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
1444 if ( strpos( $request_uri, 'admin.php' ) !== false ) {
1445 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.
1446 $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.
1447 if ( strpos( $page, 'aioseo' ) !== false ) {
1448 return;
1449 }
1450 }
1451 }
1452 }
1453
1454 // Exclude the SureForms post type from AIOSEO's public post types.
1455 add_filter( 'aioseo_public_post_types', [ $this, 'unset_sureforms_post_type' ] );
1456 }
1457
1458 /**
1459 * Creates a DateTime object from date string and time components.
1460 *
1461 * @param string $date_str Date string.
1462 * @param string $hours Hours in 12-hour format.
1463 * @param string $minutes Minutes.
1464 * @param string $meridiem AM or PM.
1465 * @since 2.4.0
1466 * @return \DateTime|null DateTime object or null if invalid.
1467 */
1468 private function create_datetime_object( $date_str, $hours, $minutes, $meridiem ) {
1469 if ( empty( $date_str ) ) {
1470 return null;
1471 }
1472
1473 try {
1474 $date = new \DateTime( $date_str );
1475
1476 // Convert 12-hour format to 24-hour format.
1477 $hour_24 = intval( $hours );
1478 if ( 'PM' === $meridiem && 12 !== $hour_24 ) {
1479 $hour_24 += 12;
1480 } elseif ( 'AM' === $meridiem && 12 === $hour_24 ) {
1481 $hour_24 = 0;
1482 }
1483
1484 $date->setTime( $hour_24, intval( $minutes ), 0 );
1485 return $date;
1486 } catch ( \Exception $e ) {
1487 return null;
1488 }
1489 }
1490
1491 /**
1492 * Restrict interference of other plugins with SureForms.
1493 *
1494 * @since 0.0.5
1495 * @return void
1496 */
1497 private function restrict_unwanted_insertions() {
1498 // Restrict RankMath metaboxes in edit page.
1499 add_action( 'cmb2_admin_init', [ $this, 'restrict_data' ] );
1500
1501 // Restrict Yoast columns.
1502 add_filter( 'wpseo_accessible_post_types', [ $this, 'unset_sureforms_post_type' ] );
1503 add_filter( 'wpseo_metabox_prio', '__return_false' );
1504
1505 // Restrict AIOSEO columns.
1506 $this->restrict_in_aioseo_plugin();
1507 }
1508 }
1509