PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 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 All 98 releases
← All changes | inc/post-types.php +964 -755 0.0.13 → 2.12.8 View file →
@@ -7,13 +7,12 @@
7 7 */
8 8
9 9 namespace SRFM\Inc;
10 10
11 -use SRFM\Inc\Database\Tables\Entries;
11 +use SRFM\Inc\Traits\Get_Instance;
12 12 use WP_Admin_Bar;
13 -use SRFM\Inc\Traits\Get_Instance;
14 -use SRFM\Inc\Generate_Form_Markup;
15 -use SRFM\Inc\Helper;
13 +use WP_Post;
14 +use WP_REST_Response;
16 15
17 16 if ( ! defined( 'ABSPATH' ) ) {
18 17 exit; // Exit if accessed directly.
19 18 }
@@ -34,26 +33,178 @@
34 33 public function __construct() {
35 34 $this->restrict_unwanted_insertions();
36 35 add_action( 'init', [ $this, 'register_post_types' ] );
37 36 add_action( 'init', [ $this, 'register_post_metas' ] );
38 - add_filter( 'manage_sureforms_form_posts_columns', [ $this, 'custom_form_columns' ] );
39 - add_action( 'manage_sureforms_form_posts_custom_column', [ $this, 'custom_form_column_data' ], 10, 2 );
40 - add_filter( 'manage_sureforms_entry_posts_columns', [ $this, 'custom_entry_columns' ] );
41 - add_action( 'manage_sureforms_entry_posts_custom_column', [ $this, 'custom_entry_column_data' ], 10, 2 );
42 37 add_shortcode( 'sureforms', [ $this, 'forms_shortcode' ] );
43 - add_action( 'add_meta_boxes', [ $this, 'entries_meta_box' ] );
44 - add_action( 'restrict_manage_posts', [ $this, 'add_tax_filter' ] );
45 38 add_action( 'manage_posts_extra_tablenav', [ $this, 'maybe_render_blank_form_state' ] );
46 - add_action( 'in_admin_header', [ $this, 'embed_page_header' ] );
47 - add_action( 'admin_head', [ $this, 'remove_entries_publishing_actions' ] );
48 - add_filter( 'post_row_actions', [ $this, 'modify_entries_list_row_actions' ], 10, 2 );
49 - add_filter( 'post_updated_messages', [ $this, 'entries_updated_message' ] );
50 - add_filter( 'bulk_actions-edit-sureforms_form', [ $this, 'register_modify_bulk_actions' ], 99 );
51 - add_action( 'admin_notices', [ $this, 'import_form_popup' ] );
52 - add_action( 'admin_bar_menu', [ $this, 'remove_admin_bar_menu_item' ], 80, 1 );
53 - add_action( 'template_redirect', [ $this, 'srfm_instant_form_redirect' ] );}
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' ] );
54 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 +
55 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 + /**
56 207 * Add SureForms menu.
57 208 *
58 209 * @param string $title Parent slug.
59 210 * @param string $subtitle Parent slug.
@@ -64,20 +215,23 @@
64 215 * @return void
65 216 * @since 0.0.1
66 217 */
67 218 public function get_blank_page_markup( $title, $subtitle, $image, $button_text = '', $button_url = '', $after_button = '' ) {
68 - echo '<div class="sureform-add-new-form">';
69 -
70 - echo '<p class="sureform-blank-page-title">' . esc_html( $title ) . '</p>';
71 -
72 - echo '<p class="sureform-blank-page-subtitle">' . esc_html( $subtitle ) . '</p>';
73 -
74 - echo '<img src="' . esc_url( SRFM_URL . '/images/' . $image . '.svg' ) . '">';
75 -
76 - if ( ! empty( $button_text ) && ! empty( $button_url ) ) {
77 - echo '<div class="sureforms-add-new-form-container"><a class="sf-add-new-form-button" href="' . esc_url( $button_url ) . '"><div class="button-secondary">' . esc_html( $button_text ) . '</div></a>' . wp_kses_post( $after_button ) . '</div>';
78 - }
79 - echo '</div>';
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
80 234 }
81 235
82 236 /**
83 237 * Render blank state for add new form screen.
@@ -86,30 +240,11 @@
86 240 * @return void
87 241 * @since 0.0.1
88 242 */
89 243 public function sureforms_render_blank_state( $post_type ) {
244 + if ( SRFM_ENTRIES === $post_type ) {
90 245
91 - if ( SRFM_FORMS_POST_TYPE === $post_type ) {
92 - $page_name = 'add-new-form';
93 - $new_form_url = admin_url( 'admin.php?page=' . $page_name );
94 - $import_button = '<button class="button button-secondary srfm-import-btn">' . __( 'Import Form', 'sureforms' ) . '</button>';
95 -
96 246 $this->get_blank_page_markup(
97 - esc_html__( 'Let’s build your first form', 'sureforms' ),
98 - esc_html__(
99 - 'Craft beautiful and functional forms in minutes',
100 - 'sureforms'
101 - ),
102 - 'add-new-form',
103 - esc_html__( 'Add New Form', 'sureforms' ),
104 - $new_form_url,
105 - $import_button
106 - );
107 - }
108 -
109 - if ( SRFM_ENTRIES_POST_TYPE === $post_type ) {
110 -
111 - $this->get_blank_page_markup(
112 247 esc_html__( 'No records found', 'sureforms' ),
113 248 esc_html__(
114 249 'This is where your form entries will appear',
115 250 'sureforms'
@@ -150,67 +285,23 @@
150 285 'labels' => $form_labels,
151 286 'rewrite' => [ 'slug' => 'form' ],
152 287 'public' => true,
153 288 'show_in_rest' => true,
154 - 'has_archive' => false,
289 + 'has_archive' => true,
155 290 'show_ui' => true,
156 291 'supports' => [ 'title', 'author', 'editor', 'custom-fields' ],
157 - 'show_in_menu' => 'sureforms_menu',
292 + 'show_in_menu' => false,
158 293 'show_in_nav_menus' => true,
159 - ]
160 - );
161 -
162 - $result_labels = [
163 - 'name' => _x( 'Entries', 'post type general name', 'sureforms' ),
164 - 'singular_name' => _x( 'Entry', 'post type singular name', 'sureforms' ),
165 - 'menu_name' => _x( 'Entries', 'admin menu', 'sureforms' ),
166 - 'name_admin_bar' => _x( 'Entry', 'add new on admin bar', 'sureforms' ),
167 - 'add_new' => _x( 'Add New', 'Entry', 'sureforms' ),
168 - 'add_new_item' => __( 'Add New Entry', 'sureforms' ),
169 - 'new_item' => __( 'New Entry', 'sureforms' ),
170 - 'edit_item' => __( 'View Entry', 'sureforms' ),
171 - 'view_item' => __( 'View Entry', 'sureforms' ),
172 - 'all_items' => __( 'Entries', 'sureforms' ),
173 - 'search_items' => __( 'Search Entries', 'sureforms' ),
174 - 'parent_item_colon' => __( 'Parent Entries:', 'sureforms' ),
175 - 'not_found' => __( 'No results found.', 'sureforms' ),
176 - 'not_found_in_trash' => __( 'No results found in Trash.', 'sureforms' ),
177 - ];
178 - register_post_type(
179 - SRFM_ENTRIES_POST_TYPE,
180 - [
181 - 'labels' => $result_labels,
182 - 'supports' => [ 'title' ],
183 - 'public' => false,
184 - 'show_in_rest' => true,
185 - 'exclude_from_search' => true,
186 - 'publicly_queryable' => false,
187 - 'has_archive' => true,
188 - 'capability_type' => 'post',
189 - 'capabilities' => [
190 - 'create_posts' => 'do_not_allow',
191 - ],
192 - 'map_meta_cap' => true,
193 - 'show_ui' => false, // Hide the entries post type from the admin menu.
194 - 'show_in_menu' => 'sureforms_menu',
195 - ]
196 - );
197 - register_taxonomy(
198 - 'sureforms_tax',
199 - 'sureforms_entry',
200 - [
201 - 'label' => __( 'Form ID', 'sureforms' ),
202 - 'hierarchical' => true,
203 294 'capabilities' => [
204 - 'assign_terms' => 'god',
205 - 'edit_terms' => 'god',
206 - 'manage_terms' => 'god',
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',
207 303 ],
208 - 'public' => false,
209 - 'show_in_rest' => true,
210 - 'show_admin_column' => false,
211 - 'show_in_nav_menus' => false,
212 - 'show_ui' => false,
213 304 ]
214 305 );
215 306 // will be used later.
216 307 // register_post_status(
@@ -227,108 +318,21 @@
227 318 // );.
228 319 }
229 320
230 321 /**
231 - * Remove add new form menu item.
322 + * Redirects requests for SureForms archieve page to homeurl
232 323 *
233 - * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance.
234 - *
324 + * @since 1.4.0
235 325 * @return void
236 - * @since 0.0.1
237 326 */
238 - public function remove_admin_bar_menu_item( $wp_admin_bar ) {
239 - $wp_admin_bar->remove_node( 'new-sureforms_form' );
240 - }
241 -
242 - /**
243 - * Modify post update message for Entry post type.
244 - *
245 - * @param string $messages Post type.
246 - * @return string
247 - * @since 0.0.1
248 - */
249 - public function entries_updated_message( $messages ) {
250 - global $post_ID;
251 -
252 - $post_type = get_post_type( $post_ID );
253 -
254 - if ( SRFM_ENTRIES_POST_TYPE === $post_type ) {
255 - // @phpstan-ignore-next-line -- False positive
256 - $messages['post'][1] = __( 'Entry updated.', 'sureforms' );
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;
257 331 }
258 -
259 - return $messages;
260 332 }
261 333
262 334 /**
263 - * Remove publishing actions from single entries page.
264 - *
265 - * @return void
266 - * @since 0.0.1
267 - */
268 - public function remove_entries_publishing_actions() {
269 - global $typenow;
270 - if ( 'sureforms_entry' === $typenow ) { ?>
271 - <style>
272 - .misc-pub-post-status {
273 - display: none !important;
274 - }
275 - .misc-pub-visibility {
276 - display: none !important;
277 - }
278 - </style>
279 - <?php
280 - }
281 - }
282 -
283 - /**
284 - * Modify list row actions.
285 - *
286 - * @param array<mixed> $actions An array of row action links.
287 - * @param \WP_Post $post The current WP_Post object.
288 - *
289 - * @return array<mixed> $actions Modified row action links.
290 - * @since 0.0.1
291 - */
292 - public function modify_entries_list_row_actions( $actions, $post ) {
293 - if ( 'sureforms_entry' === $post->post_type ) {
294 - $actions['edit'] = '<a href="' . get_edit_post_link( $post->ID ) . '">View</a>';
295 - }
296 - if ( 'sureforms_form' === $post->post_type ) {
297 - $actions['export'] = '<a href="#" onclick="exportForm(' . $post->ID . ')">Export</a>';
298 - }
299 -
300 - return $actions;
301 - }
302 -
303 - /**
304 - * Modify list bulk actions.
305 - *
306 - * @param array<mixed> $bulk_actions An array of bulk action links.
307 - * @since 0.0.1
308 - * @return array<mixed> $bulk_actions Modified action links.
309 - */
310 - public function register_modify_bulk_actions( $bulk_actions ) {
311 -
312 - $white_listed_actions = [
313 - 'edit',
314 - 'trash',
315 - 'delete',
316 - 'untrash',
317 - ];
318 -
319 - // remove all actions except white listed actions.
320 - $bulk_actions = array_intersect_key( $bulk_actions, array_flip( $white_listed_actions ) );
321 -
322 - // Add export action only if edit and trash actions are present in bulk actions.
323 - if ( isset( $bulk_actions['edit'] ) && isset( $bulk_actions['trash'] ) ) {
324 - $bulk_actions['export'] = __( 'Export', 'sureforms' );
325 - }
326 -
327 - return $bulk_actions;
328 - }
329 -
330 - /**
331 335 * Show blank slate styles.
332 336 *
333 337 * @return void
334 338 * @since 0.0.1
@@ -333,9 +337,51 @@
333 337 * @return void
334 338 * @since 0.0.1
335 339 */
336 340 public function get_blank_state_styles() {
337 - echo '<style type="text/css">.sf-add-new-form-button:focus { box-shadow:none !important; outline:none !important; } #posts-filter .wp-list-table, #posts-filter .tablenav.top, .tablenav.bottom .actions, .wrap .subsubsub { display: none; } #posts-filter .tablenav.bottom { height: auto; } .sureform-add-new-form{ display: flex; flex-direction: column; gap: 8px; justify-content: center; align-items: center; padding: 24px 0 24px 0; } .sureform-blank-page-title { color: var(--dashboard-heading); font-family: Inter; font-size: 22px; font-style: normal; font-weight: 600; line-height: 28px; margin: 0; } .sureform-blank-page-subtitle { color: var(--dashboard-text); margin: 0; font-family: Inter; font-size: 14px; font-style: normal; font-weight: 400; line-height: 16px; }</style>';
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
338 384 }
339 385
340 386 /**
341 387 * Show blank slate.
@@ -362,54 +408,11 @@
362 408
363 409 $this->get_blank_state_styles();
364 410
365 411 }
366 -
367 - if ( SRFM_ENTRIES_POST_TYPE === $post_type && 'bottom' === $which ) {
368 -
369 - $counts = (array) wp_count_posts( SRFM_ENTRIES_POST_TYPE );
370 - unset( $counts['auto-draft'] );
371 - $count = array_sum( $counts );
372 -
373 - if ( 0 < $count ) {
374 - return;
375 - }
376 -
377 - $this->sureforms_render_blank_state( $post_type );
378 -
379 - $this->get_blank_state_styles();
380 -
381 - }
382 412 }
383 413
384 414 /**
385 - * Set up a div for the header to render into it.
386 - *
387 - * @return void
388 - * @since 0.0.1
389 - */
390 - public static function embed_page_header() {
391 - $screen = get_current_screen();
392 - $screen_id = $screen ? $screen->id : '';
393 -
394 - if ( 'edit-' . SRFM_FORMS_POST_TYPE === $screen_id || 'edit-' . SRFM_ENTRIES_POST_TYPE === $screen_id || 'sureforms_page_sureforms_entries' === $screen_id ) {
395 - ?>
396 - <style>
397 - .srfm-page-header {
398 - min-height: 65px;
399 - @media screen and ( max-width: 600px ) {
400 - padding-top: 46px;
401 - }
402 - }
403 - </style>
404 - <div id="srfm-page-header" class="srfm-page-header">
405 - <div class="srfm-page-pre-nav-content"></div>
406 - </div>
407 - <?php
408 - }
409 - }
410 -
411 - /**
412 415 * Registers the sureforms metas.
413 416 *
414 417 * @return void
415 418 * @since 0.0.1
@@ -439,42 +442,71 @@
439 442 '_srfm_submit_type' => 'string',
440 443 // Security.
441 444 '_srfm_captcha_security_type' => 'string',
442 445 '_srfm_form_recaptcha' => 'string',
446 + // post meta to store if the form is AI generated.
447 + '_srfm_is_ai_generated' => 'boolean',
443 448 ]
444 449 );
445 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 +
446 456 // Form Custom CSS meta.
447 457 register_post_meta(
448 458 'sureforms_form',
449 459 '_srfm_form_custom_css',
450 460 [
451 - 'show_in_rest' => true,
461 + 'show_in_rest' => [
462 + 'schema' => [
463 + 'type' => 'string',
464 + 'context' => [ 'edit' ],
465 + ],
466 + ],
452 467 'type' => 'string',
453 468 'single' => true,
454 - 'auth_callback' => function() {
455 - return current_user_can( 'edit_posts' );
469 + 'auth_callback' => static function() {
470 + return Helper::current_user_can();
456 471 },
457 - 'sanitize_callback' => function( $meta_value ) {
472 + 'sanitize_callback' => static function( $meta_value ) {
458 473 return wp_kses_post( $meta_value );
459 474 },
460 475 ]
461 476 );
462 477
478 + // Get default values for meta keys.
479 + $default_meta_keys = Create_New_Form::get_default_meta_keys();
480 +
463 481 foreach ( $metas as $meta => $type ) {
464 - register_meta(
465 - 'post',
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,
466 507 $meta,
467 - [
468 - 'object_subtype' => SRFM_FORMS_POST_TYPE,
469 - 'show_in_rest' => true,
470 - 'single' => true,
471 - 'type' => $type,
472 - 'sanitize_callback' => 'sanitize_text_field',
473 - 'auth_callback' => function() {
474 - return current_user_can( 'edit_posts' );
475 - },
476 - ]
508 + $meta_args
477 509 );
478 510 }
479 511
480 512 // Registers meta to handle values associated with form styling.
@@ -481,14 +513,38 @@
481 513 register_post_meta(
482 514 SRFM_FORMS_POST_TYPE,
483 515 '_srfm_instant_form_settings',
484 516 [
485 - 'single' => true,
486 - 'type' => 'object',
487 - 'auth_callback' => '__return_true',
488 - 'show_in_rest' => [
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' => [
489 544 'schema' => [
490 545 'type' => 'object',
546 + 'context' => [ 'edit' ],
491 547 'properties' => [
492 548 'site_logo' => [
493 549 'type' => 'string',
494 550 ],
@@ -535,15 +591,15 @@
535 591 ],
536 592 ],
537 593 ],
538 594 ],
539 - 'default' => [
595 + 'default' => [
540 596 'bg_type' => 'color',
541 597 'bg_color' => '#ffffff',
542 598 'bg_image' => '',
543 599 'site_logo' => '',
544 600 'cover_type' => 'color',
545 - 'cover_color' => '#0C78FB',
601 + 'cover_color' => '#111C44',
546 602 'cover_image' => '',
547 603 'enable_instant_form' => false,
548 604 'form_container_width' => 620,
549 605 'single_page_form_title' => true,
@@ -555,39 +611,337 @@
555 611 register_post_meta(
556 612 SRFM_FORMS_POST_TYPE,
557 613 '_srfm_forms_styling',
558 614 [
559 - 'single' => true,
560 - 'type' => 'object',
561 - 'auth_callback' => '__return_true',
562 - 'show_in_rest' => [
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' => [
563 624 'schema' => [
564 625 'type' => 'object',
626 + 'context' => [ 'edit' ],
565 627 'properties' => [
566 - 'primary_color' => [
628 + 'primary_color' => [
567 629 'type' => 'string',
568 630 ],
569 - 'text_color' => [
631 + 'text_color' => [
570 632 'type' => 'string',
571 633 ],
572 - 'text_color_on_primary' => [
634 + 'text_color_on_primary' => [
573 635 'type' => 'string',
574 636 ],
575 - 'field_spacing' => [
637 + 'field_spacing' => [
576 638 'type' => 'string',
577 639 ],
578 - 'submit_button_alignment' => [
640 + 'submit_button_alignment' => [
579 641 'type' => 'string',
580 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 + ],
581 862 ],
582 863 ],
583 864 ],
584 - 'default' => [
585 - 'primary_color' => '#0C78FB',
586 - 'text_color' => '#1E1E1E',
587 - 'text_color_on_primary' => '#FFFFFF',
588 - 'field_spacing' => 'medium',
589 - 'submit_button_alignment' => 'left',
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,
590 944 ],
591 945 ]
592 946 );
593 947
@@ -595,15 +949,44 @@
595 949 register_post_meta(
596 950 'sureforms_form',
597 951 '_srfm_email_notification',
598 952 [
599 - 'single' => true,
600 - 'type' => 'array',
601 - 'auth_callback' => '__return_true',
602 - 'show_in_rest' => [
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' => [
603 985 'schema' => [
604 - 'type' => 'array',
605 - 'items' => [
986 + 'type' => 'array',
987 + 'context' => [ 'edit' ],
988 + 'items' => [
606 989 'type' => 'object',
607 990 'properties' => [
608 991 'id' => [
609 992 'type' => 'integer',
@@ -622,8 +1005,14 @@
622 1005 ],
623 1006 'email_reply_to' => [
624 1007 'type' => 'string',
625 1008 ],
1009 + 'from_name' => [
1010 + 'type' => 'string',
1011 + ],
1012 + 'from_email' => [
1013 + 'type' => 'string',
1014 + ],
626 1015 'email_cc' => [
627 1016 'type' => 'string',
628 1017 ],
629 1018 'email_bcc' => [
@@ -638,19 +1027,21 @@
638 1027 ],
639 1028 ],
640 1029 ],
641 1030 ],
642 - 'default' => [
1031 + 'default' => [
643 1032 [
644 1033 'id' => 1,
645 1034 'status' => true,
646 1035 'is_raw_format' => false,
647 - 'name' => 'Admin Notification Email',
1036 + 'name' => __( 'Admin Notification Email', 'sureforms' ),
648 1037 'email_to' => '{admin_email}',
649 1038 'email_reply_to' => '{admin_email}',
1039 + 'from_name' => '{site_title}',
1040 + 'from_email' => '{admin_email}',
650 1041 'email_cc' => '{admin_email}',
651 1042 'email_bcc' => '{admin_email}',
652 - 'subject' => 'New Form Submission',
1043 + 'subject' => sprintf( /* translators: %s: Form title smart tag */ __( 'New Form Submission - %s', 'sureforms' ), '{form_title}' ),
653 1044 'email_body' => '{all_data}',
654 1045 ],
655 1046 ],
656 1047 ]
@@ -660,15 +1051,37 @@
660 1051 register_post_meta(
661 1052 'sureforms_form',
662 1053 '_srfm_compliance',
663 1054 [
664 - 'single' => true,
665 - 'type' => 'array',
666 - 'auth_callback' => '__return_true',
667 - 'show_in_rest' => [
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' => [
668 1080 'schema' => [
669 - 'type' => 'array',
670 - 'items' => [
1081 + 'type' => 'array',
1082 + 'context' => [ 'edit' ],
1083 + 'items' => [
671 1084 'type' => 'object',
672 1085 'properties' => [
673 1086 'id' => [
674 1087 'type' => 'string',
@@ -688,9 +1101,9 @@
688 1101 ],
689 1102 ],
690 1103 ],
691 1104 ],
692 - 'default' => [
1105 + 'default' => [
693 1106 [
694 1107 'id' => 'gdpr',
695 1108 'gdpr' => false,
696 1109 'do_not_store_entries' => false,
@@ -700,51 +1113,107 @@
700 1113 ],
701 1114 ]
702 1115 );
703 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 +
704 1123 // form confirmation.
705 1124 register_post_meta(
706 1125 'sureforms_form',
707 1126 '_srfm_form_confirmation',
708 1127 [
709 - 'single' => true,
710 - 'type' => 'array',
711 - 'auth_callback' => '__return_true',
712 - 'show_in_rest' => [
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' => [
713 1175 'schema' => [
714 - 'type' => 'array',
715 - 'items' => [
1176 + 'type' => 'array',
1177 + 'context' => [ 'edit' ],
1178 + 'items' => [
716 1179 'type' => 'object',
717 1180 'properties' => [
718 - 'id' => [
1181 + 'id' => [
719 1182 'type' => 'integer',
720 1183 ],
721 - 'confirmation_type' => [
1184 + 'confirmation_type' => [
722 1185 'type' => 'string',
723 1186 ],
724 - 'page_url' => [
1187 + 'page_url' => [
725 1188 'type' => 'string',
726 1189 ],
727 - 'custom_url' => [
1190 + 'custom_url' => [
728 1191 'type' => 'string',
729 1192 ],
730 - 'message' => [
1193 + 'message' => [
731 1194 'type' => 'string',
732 1195 ],
733 - 'submission_action' => [
1196 + 'submission_action' => [
734 1197 'type' => 'string',
735 1198 ],
1199 + 'enable_query_params' => [
1200 + 'type' => 'boolean',
1201 + ],
1202 + 'query_params' => [
1203 + 'type' => 'array',
1204 + ],
736 1205 ],
737 1206 ],
738 1207 ],
739 1208 ],
740 - 'default' => [
1209 + 'default' => [
741 1210 [
742 1211 'id' => 1,
743 1212 'confirmation_type' => 'same page',
744 1213 'page_url' => '',
745 1214 'custom_url' => '',
746 - 'message' => '<p style="text-align: center;"><img src="' . esc_attr( $check_icon ) . '"></img></p><h2 style="text-align: center;">Thank you</h2><p style="text-align: center;">We have received your email. You\'ll hear from us as soon as possible.</p><p style="text-align: center;">Please be sure to whitelist our {admin_email} email address to ensure our replies reach your inbox safely.</p>',
1215 + 'message' => $default_confirmation_message,
747 1216 'submission_action' => 'hide form',
748 1217 ],
749 1218 ],
750 1219 ]
@@ -749,50 +1218,8 @@
749 1218 ],
750 1219 ]
751 1220 );
752 1221
753 - // Sureforms entry metas.
754 - register_post_meta(
755 - 'sureforms_entry',
756 - '_srfm_submission_info',
757 - [
758 - 'single' => true,
759 - 'type' => 'array',
760 - 'auth_callback' => '__return_true',
761 - 'show_in_rest' => [
762 - 'schema' => [
763 - 'type' => 'array',
764 - 'items' => [
765 - 'type' => 'object',
766 - 'properties' => [
767 - 'user_ip' => [
768 - 'type' => 'string',
769 - ],
770 - 'browser_name' => [
771 - 'type' => 'string',
772 - ],
773 - 'device_name' => [
774 - 'type' => 'string',
775 - ],
776 - ],
777 - ],
778 - ],
779 - ],
780 - ]
781 - );
782 -
783 - // store form id in entry.
784 - register_post_meta(
785 - 'sureforms_entry',
786 - '_srfm_entry_form_id',
787 - [
788 - 'single' => true,
789 - 'type' => 'integer',
790 - 'auth_callback' => '__return_true',
791 - 'show_in_rest' => true,
792 - ]
793 - );
794 -
795 1222 // conditional logic.
796 1223 do_action( 'srfm_register_conditional_logic_post_meta' );
797 1224 /**
798 1225 * Hook for registering additional Post Meta
@@ -798,231 +1225,110 @@
798 1225 * Hook for registering additional Post Meta
799 1226 */
800 1227 do_action( 'srfm_register_additional_post_meta' );
801 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 + );
802 1268 }
803 1269
804 1270 /**
805 - * Sureforms entries meta box callback.
1271 + * Sanitizes the form restriction data.
806 1272 *
807 - * @param \WP_Post $post Template.
808 - * @return void
809 - * @since 0.0.1
1273 + * @param mixed $meta_value The meta value to sanitize.
1274 + * @return string|false Sanitized JSON string.
810 1275 */
811 - public function sureforms_meta_box_callback( \WP_Post $post ) {
812 - $meta_data = get_post_meta( $post->ID, 'srfm_entry_meta', true );
813 - if ( ! is_array( $meta_data ) ) {
814 - return;
1276 + public function sanitize_form_restriction_data( $meta_value ) {
1277 + if ( empty( $meta_value ) || ! is_string( $meta_value ) ) {
1278 + return wp_json_encode( [] );
815 1279 }
816 - $excluded_fields = [ 'srfm-honeypot-field', 'g-recaptcha-response', 'srfm-sender-email-field' ];
817 1280
818 - ?>
819 - <table class="widefat striped">
820 - <tbody>
821 - <tr><th><b><?php esc_html_e( 'Fields', 'sureforms' ); ?></b></th><th><b><?php esc_html_e( 'Values', 'sureforms' ); ?></b></th></tr>
822 - <?php
823 - foreach ( $meta_data as $field_name => $value ) :
824 - if ( in_array( $field_name, $excluded_fields, true ) ) {
825 - continue;
826 - }
1281 + $meta_value = json_decode( $meta_value, true );
827 1282
828 - if ( false === str_contains( $field_name, '-lbl-' ) ) {
829 - continue;
830 - }
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 + }
831 1287
832 - $label = explode( '-lbl-', $field_name )[1];
833 - // Getting the encrypted label. we are removing the block slug here.
834 - $label = explode( '-', $label )[0];
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 + ];
835 1305
836 - ?>
837 - <tr>
838 - <td><b><?php echo $label ? esc_html( Helper::decrypt( $label ) ) : ''; ?><b></td>
839 - <?php if ( strpos( $field_name, 'srfm-upload' ) !== false ) : ?>
840 - <style>
841 - .file-cards-container {
842 - display: flex;
843 - flex-wrap: wrap;
844 - gap: 10px;
845 - }
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 + );
846 1314
847 - .file-card {
848 - border: 1px solid #ddd;
849 - border-radius: 4px;
850 - padding: 10px;
851 - width: 100px; /* Reduced width */
852 - text-align: center;
853 - background: #f9f9f9;
854 - font-size: 12px; /* Reduced font size for smaller cards */
855 - }
1315 + $end_datetime = $this->create_datetime_object(
1316 + $sanitized['date'],
1317 + $sanitized['hours'],
1318 + $sanitized['minutes'],
1319 + $sanitized['meridiem']
1320 + );
856 1321
857 - .file-card-image img {
858 - max-width: 80px; /* Reduced max width */
859 - max-height: 80px; /* Reduced max height */
860 - object-fit: cover;
861 - }
862 -
863 - .file-card-icon {
864 - font-size: 24px; /* Reduced icon size */
865 - margin-bottom: 5px;
866 - }
867 -
868 - .file-card-details {
869 - margin-bottom: 5px;
870 - font-weight: bold;
871 - }
872 -
873 - .file-card-url a {
874 - color: #007bff;
875 - text-decoration: none;
876 - font-size: 12px; /* Reduced font size */
877 - }
878 -
879 - .file-card-url a:hover {
880 - text-decoration: underline;
881 - }
882 - </style>
883 - <td>
884 - <div class="file-cards-container">
885 - <?php
886 - $upload_values = $value;
887 - if ( ! empty( $upload_values ) && is_array( $upload_values ) ) {
888 - foreach ( $upload_values as $value ) {
889 - $value = Helper::get_string_value( $value );
890 - if ( ! empty( $value ) ) {
891 - $file_type = pathinfo( $value, PATHINFO_EXTENSION );
892 - $is_image = in_array( $file_type, [ 'gif', 'png', 'bmp', 'jpg', 'jpeg', 'svg' ], true );
893 - ?>
894 - <div class="file-card">
895 - <?php if ( $is_image ) : ?>
896 - <div class="file-card-image">
897 - <a target="_blank" href="<?php echo esc_attr( urldecode( $value ) ); ?>">
898 - <img src="<?php echo esc_attr( urldecode( $value ) ); ?>" alt="img" />
899 - </a>
900 - </div>
901 - <?php else : ?>
902 - <div class="file-card-icon">
903 - <?php echo '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16.333V4.667a1.333 1.333 0 011.333-1.333h13.334a1.333 1.333 0 011.333 1.333v11.666a1.333 1.333 0 01-1.333 1.333H5.333A1.333 1.333 0 014 16.333zm8-8h2v6h-2v-6zm-2 8h6v2H10v-2zm-6-6h4v6H4v-6zm0-4h16v2H4V6z"/></svg>'; ?>
904 - </div>
905 - <div class="file-card-details">
906 - <span><?php echo esc_html( strtoupper( $file_type ) ); ?></span>
907 - </div>
908 - <?php endif; ?>
909 - <div class="file-card-url">
910 - <a target="_blank" href="<?php echo esc_attr( urldecode( $value ) ); ?>"><?php echo esc_html__( 'Open', 'sureforms' ); ?></a>
911 - </div>
912 - </div>
913 - <?php
914 - }
915 - }
916 - }
917 - ?>
918 - </div>
919 - </td>
920 - <?php elseif ( strpos( $field_name, 'srfm-url' ) !== false ) : ?>
921 - <?php if ( ! $value ) : ?>
922 - <td><?php echo ''; ?></td>
923 - <?php else : ?>
924 - <?php
925 - if (
926 - substr( $value, 0, 7 ) !== 'http://' &&
927 - substr( $value, 0, 8 ) !== 'https://'
928 - ) {
929 - $value = 'https://' . $value;
930 - }
931 - ?>
932 - <td><a target="_blank" href="<?php echo esc_url( $value ); ?>"><?php echo esc_url( $value ); ?></a></td>
933 - <?php endif; ?>
934 - <?php else : ?>
935 - <td><?php echo false !== strpos( $value, PHP_EOL ) ? wp_kses_post( wpautop( $value ) ) : wp_kses_post( $value ); ?></td>
936 - <?php endif; ?>
937 - </tr>
938 - <?php endforeach; ?>
939 - </tbody>
940 - </table>
941 - <?php
942 - }
943 -
944 -
945 - /**
946 - * Add Sureforms entries meta box.
947 - *
948 - * @return void
949 - * @since 0.0.1
950 - */
951 - public function entries_meta_box() {
952 - add_meta_box(
953 - 'sureform_entry_meta',
954 - 'Form Data',
955 - [ $this, 'sureforms_meta_box_callback' ],
956 - 'sureforms_entry',
957 - 'normal',
958 - 'high'
959 - );
960 - add_meta_box(
961 - 'sureform_form_name_meta',
962 - 'Submission Info',
963 - [ $this, 'sureforms_form_name_meta_callback' ],
964 - 'sureforms_entry',
965 - 'side',
966 - 'low'
967 - );
968 - }
969 -
970 - /**
971 - * Sureforms box Form Name meta box callback.
972 - *
973 - * @param \WP_Post $post Template.
974 - * @return void
975 - * @since 0.0.1
976 - */
977 - public function sureforms_form_name_meta_callback( \WP_Post $post ) {
978 - $post_id = $post->ID;
979 - $taxonomy = 'sureforms_tax';
980 - $terms = wp_get_post_terms( $post_id, $taxonomy );
981 - if ( is_array( $terms ) && count( $terms ) > 0 ) {
982 - $form_id = intval( $terms[0]->slug );
983 - $form_name = ! empty( get_the_title( $form_id ) ) ? get_the_title( $form_id ) : 'SureForms Form';
984 - $submission_info = get_post_meta( $post_id, '_srfm_submission_info', true );
985 - if ( is_array( $submission_info ) && count( $submission_info ) > 0 ) {
986 - $user_ip = $submission_info[0]['user_ip'] ? $submission_info[0]['user_ip'] : '';
987 - $browser_name = $submission_info[0]['browser_name'] ? $submission_info[0]['browser_name'] : '';
988 - $device_name = $submission_info[0]['device_name'] ? $submission_info[0]['device_name'] : '';
989 - $entry_form_id = Helper::get_string_value( get_post_meta( $post_id, '_srfm_entry_form_id', true ) );
990 - } else {
991 - $user_ip = '';
992 - $browser_name = '';
993 - $device_name = '';
994 - $entry_form_id = '';
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;
995 1326 }
996 - ?>
997 - <table style="border-collapse: separate; border-spacing: 5px 5px;">
998 - <tr style="margin-bottom: 10px;">
999 - <td><b><?php echo esc_html( __( 'Form Name:', 'sureforms' ) ); ?></b></td>
1000 - <td><?php echo esc_html( $form_name ); ?></td>
1001 - </tr>
1002 - <tr style="margin-bottom: 10px;">
1003 - <td><b><?php echo esc_html( __( 'Form ID:', 'sureforms' ) ); ?></b></td>
1004 - <td><?php echo esc_html( $entry_form_id ); ?></td>
1005 - </tr>
1006 - <tr style="margin-bottom: 10px;">
1007 - <td><b><?php echo esc_html( __( 'User IP:', 'sureforms' ) ); ?></b></td>
1008 - <td><a target="_blank" rel="noopener" href="https://ipinfo.io/<?php echo esc_html( $user_ip ); ?>"><?php echo esc_html( $user_ip ); ?></a></td>
1009 - </tr>
1010 - <tr style="margin-bottom: 10px;">
1011 - <td><b><?php echo esc_html( __( 'Browser:', 'sureforms' ) ); ?></b></td>
1012 - <td><?php echo esc_html( $browser_name ); ?></td>
1013 - </tr>
1014 - <tr style="margin-bottom: 10px;">
1015 - <td><b><?php echo esc_html( __( 'Device:', 'sureforms' ) ); ?></b></td>
1016 - <td><?php echo esc_html( $device_name ); ?></td>
1017 - </tr>
1018 - </table>
1019 - <?php
1020 - } else {
1021 - ?>
1022 - <p><?php echo esc_html__( 'SureForms Form', 'sureforms' ); ?></p>
1023 - <?php
1024 1327 }
1328 +
1329 + // Return the sanitized data as a JSON string.
1330 + return wp_json_encode( $sanitized );
1025 1331 }
1026 1332
1027 1333 /**
1028 1334 * Custom Shortcode.
@@ -1030,9 +1336,9 @@
1030 1336 * @param array<mixed> $atts Attributes.
1031 1337 * @return string|false. $content Post Content.
1032 1338 * @since 0.0.1
1033 1339 */
1034 - public function forms_shortcode( array $atts ) {
1340 + public function forms_shortcode( $atts ) {
1035 1341 $atts = shortcode_atts(
1036 1342 [
1037 1343 'id' => '',
1038 1344 'show_title' => true,
@@ -1042,215 +1348,150 @@
1042 1348
1043 1349 $id = intval( $atts['id'] );
1044 1350 $post = get_post( $id );
1045 1351
1046 - if ( ! empty( $id ) && $post ) {
1047 - $content = Generate_Form_Markup::get_form_markup( $id, ! filter_var( $atts['show_title'], FILTER_VALIDATE_BOOLEAN ), '', 'post', true );
1048 - return $content;
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 );
1049 1354 }
1050 1355
1051 - return '';
1356 + return esc_html__( 'This form has been deleted or is unavailable.', 'sureforms' );
1052 1357 }
1053 1358
1054 1359 /**
1055 - * Add custom column header.
1360 + * Redirect to home page if instant form is not enabled.
1056 1361 *
1057 - * @param array<mixed> $columns Attributes.
1058 - * @return array<mixed> $columns Post Content.
1059 1362 * @since 0.0.1
1363 + * @return void
1060 1364 */
1061 - public function custom_form_columns( $columns ) {
1062 - $columns = [
1063 - 'cb' => $columns['cb'],
1064 - 'title' => $columns['title'],
1065 - 'sureforms' => __( 'Shortcode', 'sureforms' ),
1066 - 'entries' => __( 'Entries', 'sureforms' ),
1067 - 'author' => $columns['author'],
1068 - 'date' => $columns['date'],
1069 - ];
1070 - return $columns;
1071 - }
1365 + public function srfm_instant_form_redirect() {
1072 1366
1073 - /**
1074 - * Populate custom column with data.
1075 - *
1076 - * @param string $column Attributes.
1077 - * @param integer $post_id Attributes.
1078 - * @return void
1079 - * @since 0.0.1
1080 - */
1081 - public function custom_form_column_data( $column, $post_id ) {
1082 - if ( 'sureforms' === $column ) {
1083 - ob_start();
1084 - ?>
1085 - <div class="srfm-shortcode-container">
1086 - <input id="srfm-shortcode-input-<?php echo esc_attr( Helper::get_string_value( $post_id ) ); ?>" class="srfm-shortcode-input" type="text" readonly value="[sureforms id='<?php echo esc_attr( Helper::get_string_value( $post_id ) ); ?>']" />
1087 - <button type="button" class="components-button components-clipboard-button has-icon srfm-shortcode" onclick="handleFormShortcode(this)">
1088 - <span id="srfm-copy-icon" class="dashicon dashicons dashicons-admin-page"></span>
1089 - </button>
1090 - </div>
1091 - <?php
1092 - ob_end_flush();
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;
1093 1374 }
1094 - if ( 'entries' === $column ) {
1095 - // Entries URL to redirect user based on the form ID.
1096 - $entries_url = wp_nonce_url(
1097 - add_query_arg(
1098 - [
1099 - 'form_filter' => $post_id,
1100 - ],
1101 - admin_url( 'admin.php?page=sureforms_entries' )
1102 - ),
1103 - 'srfm_entries_action'
1104 - );
1105 1375
1106 - // Get the entry count for the form.
1107 - $entries_count = Entries::get_total_entries_by_status( 'all', $post_id );
1376 + $form_preview = '';
1108 1377
1109 - ob_start();
1110 - ?>
1111 - <p class="srfm-entries-number"><a href="<?php echo esc_url( $entries_url ); ?>"><?php echo esc_html( Helper::get_string_value( $entries_count ) ); ?></a></p>
1112 - <?php
1113 - ob_end_flush();
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 );
1114 1382 }
1383 +
1384 + if ( is_singular( 'sureforms_form' ) && ! $form_preview && ! Helper::current_user_can() ) {
1385 + wp_safe_redirect( home_url() );
1386 + return;
1387 + }
1115 1388 }
1116 1389
1117 1390 /**
1118 - * Add custom column header.
1391 + * Restrict RankMath meta boxes in edit page.
1119 1392 *
1120 - * @param array<mixed> $columns Attributes.
1121 - * @return array<mixed> $columns Post Content.
1122 - * @since 0.0.1
1393 + * @since 0.0.5
1394 + * @return void
1123 1395 */
1124 - public function custom_entry_columns( $columns ) {
1125 - $columns = [
1126 - 'cb' => $columns['cb'],
1127 - 'title' => __( 'First Field', 'sureforms' ),
1128 - 'form_name' => __( 'Form Name', 'sureforms' ),
1129 - 'entry_id' => __( 'ID', 'sureforms' ),
1130 - 'date' => __( 'Submitted On', 'sureforms' ),
1131 - ];
1132 - return $columns;
1396 + public function restrict_data() {
1397 + add_filter( 'rank_math/excluded_post_types', [ $this, 'unset_sureforms_post_type' ] );
1133 1398 }
1134 1399
1135 1400 /**
1136 - * Populate custom column with data.
1401 + * Remove SureForms post type from RankMath and Yoast.
1137 1402 *
1138 - * @param string $column Attributes.
1139 - * @param integer $post_id Attributes.
1140 - * @return void
1141 - * @since 0.0.1
1403 + * @param array<mixed> $post_types Post types.
1404 + * @since 0.0.5
1405 + * @return array<mixed> $post_types Modified post types.
1142 1406 */
1143 - public function custom_entry_column_data( $column, $post_id ) {
1144 - if ( 'entry_id' === $column ) {
1145 - $entry_id = strval( $post_id );
1146 - echo '<p>#' . esc_html( $entry_id ) . '</p>';
1147 - }
1148 - if ( 'form_name' === $column ) {
1149 - $taxonomy = 'sureforms_tax';
1150 - $terms = wp_get_post_terms( $post_id, $taxonomy );
1151 -
1152 - if ( is_array( $terms ) && count( $terms ) > 0 ) {
1153 - $form_id = intval( $terms[0]->slug );
1154 - $form_name = ! empty( get_the_title( $form_id ) ) ? get_the_title( $form_id ) : 'SureForms Form';
1155 - echo '<p>' . esc_html( $form_name . ' #' . $form_id ) . '</p>';
1156 - } else {
1157 - ?>
1158 - <p><?php echo esc_html__( 'SureForms Form', 'sureforms' ); ?></p>
1159 - <?php
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;
1160 1415 }
1161 - }
1416 + );
1162 1417 }
1163 1418
1164 1419 /**
1165 - * Add SureForms taxonomy filter.
1420 + * Restrict unwanted insertions from the AIOSEO plugin.
1166 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 + *
1167 1425 * @return void
1168 - * @since 0.0.1
1426 + * @since 1.6.0
1169 1427 */
1170 - public function add_tax_filter() {
1171 - $screen = get_current_screen();
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 + }
1172 1445
1173 - if ( ! is_null( $screen ) && 'edit-sureforms_entry' === $screen->id ) {
1174 - $forms = get_posts(
1175 - [
1176 - 'post_type' => SRFM_FORMS_POST_TYPE,
1177 - 'posts_per_page' => -1,
1178 - 'orderby' => 'title',
1179 - 'order' => 'ASC',
1180 - ]
1181 - );
1182 -
1183 - if ( ! empty( $forms ) ) {
1184 - $selected = isset( $_GET['sureforms_tax'] ) ? sanitize_key( wp_unslash( $_GET['sureforms_tax'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce Verification is not needed in this case. We are not getting the nonce value.
1185 - echo '<select name="sureforms_tax" id="srfm-tax-filter">';
1186 - echo '<option value="">' . esc_html__( ' All Form Entries', 'sureforms' ) . '</option>';
1187 -
1188 - foreach ( $forms as $form ) {
1189 - $selected_attr = selected( $selected, $form->ID, false );
1190 - echo '<option value="' . esc_attr( strval( $form->ID ) ) . '" ' . esc_attr( $selected_attr ) . '>' . esc_html( $form->post_title ) . '</option>';
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 + }
1191 1455 }
1192 -
1193 - echo '</select>';
1194 -
1195 1456 }
1196 1457 }
1197 - }
1198 1458
1199 - /**
1200 - * Show the import form popup
1201 - *
1202 - * @since 0.0.1
1203 - * @return void
1204 - */
1205 - public function import_form_popup() {
1206 - $screen = get_current_screen();
1207 - $id = $screen ? $screen->id : '';
1208 - if ( 'edit-sureforms_form' === $id ) {
1209 - ?>
1210 - <div class="srfm-import-plugin-wrap">
1211 - <div class="srfm-import-wrap">
1212 - <p class="srfm-import-help"><?php echo esc_html__( 'Please choose the SureForms export file (.json) that you wish to import.', 'sureforms' ); ?></p>
1213 - <form method="post" enctype="multipart/form-data" class="srfm-import-form">
1214 - <input type="file" id="srfm-import-file" onchange="handleFileChange(event)" name="import form" accept=".json">
1215 - <input type="submit" name="import-form-submit" id="import-form-submit" class="srfm-import-button" value="Import Now" disabled>
1216 - </form>
1217 - <p id="srfm-import-error"><?php echo esc_html__( 'There is some error in json file, please export the SureForms Forms again.', 'sureforms' ); ?></p>
1218 - </div>
1219 - </div>
1220 - <?php
1221 - }
1459 + // Exclude the SureForms post type from AIOSEO's public post types.
1460 + add_filter( 'aioseo_public_post_types', [ $this, 'unset_sureforms_post_type' ] );
1222 1461 }
1223 1462
1224 1463 /**
1225 - * Redirect to home page if instant form is not enabled.
1464 + * Creates a DateTime object from date string and time components.
1226 1465 *
1227 - * @since 0.0.1
1228 - * @return void
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.
1229 1472 */
1230 - public function srfm_instant_form_redirect() {
1231 -
1232 - $form_id = Helper::get_integer_value( get_the_ID() );
1233 -
1234 - $instant_form_settings = Helper::get_array_value( Helper::get_post_meta( $form_id, '_srfm_instant_form_settings' ) );
1235 - $enable_instant_form = ! empty( $instant_form_settings['enable_instant_form'] ) ? boolval( $instant_form_settings['enable_instant_form'] ) : false;
1236 -
1237 - if ( $enable_instant_form ) {
1238 - return;
1473 + private function create_datetime_object( $date_str, $hours, $minutes, $meridiem ) {
1474 + if ( empty( $date_str ) ) {
1475 + return null;
1239 1476 }
1240 1477
1241 - $form_preview = '';
1478 + try {
1479 + $date = new \DateTime( $date_str );
1242 1480
1243 - $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.
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 + }
1244 1488
1245 - if ( $form_preview_attr ) {
1246 - $form_preview = filter_var( $form_preview_attr, FILTER_VALIDATE_BOOLEAN );
1489 + $date->setTime( $hour_24, intval( $minutes ), 0 );
1490 + return $date;
1491 + } catch ( \Exception $e ) {
1492 + return null;
1247 1493 }
1248 -
1249 - if ( is_singular( 'sureforms_form' ) && ! $form_preview && ! current_user_can( 'manage_options' ) ) {
1250 - wp_safe_redirect( home_url() );
1251 - return;
1252 - }
1253 1494 }
1254 1495
1255 1496 /**
1256 1497 * Restrict interference of other plugins with SureForms.
@@ -1266,39 +1507,7 @@
1266 1507 add_filter( 'wpseo_accessible_post_types', [ $this, 'unset_sureforms_post_type' ] );
1267 1508 add_filter( 'wpseo_metabox_prio', '__return_false' );
1268 1509
1269 1510 // Restrict AIOSEO columns.
1270 - add_filter( 'aioseo_public_post_types', [ $this, 'unset_sureforms_post_type' ] );
1271 - }
1272 -
1273 - /**
1274 - * Restrict RankMath meta boxes in edit page.
1275 - *
1276 - * @since 0.0.5
1277 - * @return void
1278 - */
1279 - public function restrict_data() {
1280 - add_filter( 'rank_math/excluded_post_types', [ $this, 'unset_sureforms_post_type' ] );
1281 - }
1282 -
1283 - /**
1284 - * Remove SureForms post type from RankMath and Yoast.
1285 - *
1286 - * @param array<mixed> $post_types Post types.
1287 - * @since 0.0.5
1288 - * @return array<mixed> $post_types Modified post types.
1289 - */
1290 - public function unset_sureforms_post_type( $post_types ) {
1291 - $filtered_post_types = array_filter(
1292 - $post_types,
1293 - function( $post_type ) {
1294 - if ( is_array( $post_type ) && isset( $post_type['name'] ) ) {
1295 - return SRFM_FORMS_POST_TYPE !== $post_type['name'];
1296 - } else {
1297 - return SRFM_FORMS_POST_TYPE !== $post_type;
1298 - }
1299 - }
1300 - );
1301 -
1302 - return $filtered_post_types;
1511 + $this->restrict_in_aioseo_plugin();
1303 1512 }
1304 1513 }