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