PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 1.5.1
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v1.5.1
2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 All 96 releases
← All changes | inc/export.php +90 -257 trunk1.5.1 View file →
@@ -8,8 +8,9 @@
8 8
9 9 namespace SRFM\Inc;
10 10
11 11 use SRFM\Inc\Traits\Get_Instance;
12 +use WP_REST_Server;
12 13
13 14 if ( ! defined( 'ABSPATH' ) ) {
14 15 exit; // Exit if accessed directly.
15 16 }
@@ -37,10 +38,8 @@
37 38 '_srfm_instant_form_settings',
38 39 '_srfm_page_break_settings',
39 40 '_srfm_conversational_form',
40 41 '_srfm_premium_common',
41 - '_srfm_forms_styling_starter',
42 - '_srfm_user_registration_settings',
43 42 ];
44 43
45 44 /**
46 45 * Constructor
@@ -47,34 +46,43 @@
47 46 *
48 47 * @since 0.0.1
49 48 */
50 49 public function __construct() {
51 - // Modern REST API endpoints are registered in rest-api.php.
50 + add_action( 'wp_ajax_export_form', [ $this, 'handle_export_form' ] );
51 + add_action( 'rest_api_init', [ $this, 'register_custom_endpoint' ] );
52 52 }
53 53
54 54 /**
55 - * Get unserialized post meta keys.
55 + * Handle Export form
56 56 *
57 - * Retrieves the list of post meta keys that need to be unserialized during export.
58 - * Allows filtering of meta keys via 'srfm_export_and_import_post_meta_keys' filter.
59 - *
60 - * @since 1.9.0
61 - * @return array<string> Array of post meta keys to unserialize.
57 + * @since 0.0.1
58 + * @return void
62 59 */
63 - public function get_unserialized_post_metas() {
64 - return Helper::apply_filters_as_array( 'srfm_export_and_import_post_meta_keys', $this->unserialized_post_metas );
65 - }
60 + public function handle_export_form() {
61 + if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'export_form_nonce' ) ) {
62 + $error_message = __( 'Nonce verification failed.', 'sureforms' );
66 63
67 - /**
68 - * Get forms with meta by post IDs.
69 - * Uses:
70 - * - On websitedemos.net, for exporting the Spectra Block Patterns & Pages with SureForms form.
71 - *
72 - * @since 1.13.0
73 - * @param array<int,string>|array<int, int> $post_ids Array of post IDs to retrieve forms for.
74 - * @return array Array of forms with their post data and meta data.
75 - */
76 - public function get_forms_with_meta( $post_ids = [] ) {
64 + $error_data = [
65 + 'error' => $error_message,
66 + ];
67 + wp_send_json_error( $error_data );
68 + }
69 +
70 + // check if the user has permission to export forms.
71 + if ( ! Helper::current_user_can( 'manage_options' ) ) {
72 + wp_send_json_error(
73 + [
74 + 'error' => __( 'You do not have permission to export forms.', 'sureforms' ),
75 + ]
76 + );
77 + }
78 +
79 + if ( isset( $_POST['post_id'] ) ) {
80 + $post_ids = explode( ',', sanitize_text_field( wp_unslash( $_POST['post_id'] ) ) );
81 + } else {
82 + $post_ids = [];
83 + }
84 +
77 85 $posts = [];
78 86
79 87 foreach ( $post_ids as $post_id ) {
80 88 $post_id = intval( $post_id );
@@ -79,17 +87,9 @@
79 87 foreach ( $post_ids as $post_id ) {
80 88 $post_id = intval( $post_id );
81 89 $post = get_post( $post_id );
82 90 $post_meta = get_post_meta( $post_id );
83 -
84 - // The view counter belongs to this site's traffic, not to the form. These
85 - // payloads feed shared starter templates, so shipping it would hand every
86 - // importer a stranger's numbers. The import side already refuses the key,
87 - // so this is about not exporting it in the first place.
88 - if ( is_array( $post_meta ) ) {
89 - unset( $post_meta[ \SRFM\Inc\Form_Views::META_KEY ] );
90 - }
91 - $posts[] = [
91 + $posts[] = [
92 92 'post' => $post,
93 93 'post_meta' => $post_meta,
94 94 ];
95 95 }
@@ -97,10 +97,9 @@
97 97 // Unserialize the post metas that are serialized.
98 98 // This is needed because the post metas are serialized before saving.
99 99 foreach ( $posts as $key => $post ) {
100 100 $post_metas = isset( $post['post_meta'] ) && is_array( $post['post_meta'] ) ? $post['post_meta'] : [];
101 -
102 - foreach ( $this->get_unserialized_post_metas() as $meta_key ) {
101 + foreach ( $this->unserialized_post_metas as $meta_key ) {
103 102 if ( isset( $post_metas[ $meta_key ] ) && is_array( $post_metas[ $meta_key ] ) ) {
104 103 $post_metas[ $meta_key ] = maybe_unserialize( $post_metas[ $meta_key ][0] );
105 104 }
106 105 }
@@ -106,169 +105,66 @@
106 105 }
107 106 $posts[ $key ]['post_meta'] = $post_metas;
108 107 }
109 108
110 - return $posts;
109 + wp_send_json( $posts );
111 110 }
112 111
113 112 /**
114 - * Handle Export form via REST API
113 + * Handle Import Form
115 114 *
116 115 * @param \WP_REST_Request $request Full details about the request.
117 - * @since 2.0.0
118 - * @return \WP_REST_Response|\WP_Error
116 + *
117 + * @since 0.0.1
118 + * @return void
119 119 */
120 - public function handle_export_form_rest( $request ) {
121 - $nonce = sanitize_text_field( Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) ) );
120 + public function handle_import_form( $request ) {
122 121
123 - if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
124 - return new \WP_Error(
125 - 'invalid_nonce',
126 - __( 'Nonce verification failed.', 'sureforms' ),
127 - [ 'status' => 403 ]
128 - );
129 - }
122 + $nonce = Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) );
130 123
131 - $params = $request->get_params();
132 - $post_ids = [];
133 -
134 - // Handle post_ids parameter - can be array or comma-separated string.
135 - if ( isset( $params['post_ids'] ) ) {
136 - if ( is_array( $params['post_ids'] ) ) {
137 - $post_ids = array_map( 'intval', $params['post_ids'] );
138 - } else {
139 - $post_ids = array_map( 'intval', explode( ',', sanitize_text_field( Helper::get_string_value( $params['post_ids'] ) ) ) );
140 - }
141 - }
142 -
143 - // Validate that all post IDs are valid sureforms_form posts.
144 - $validated_post_ids = [];
145 - foreach ( $post_ids as $post_id ) {
146 - $post = get_post( $post_id );
147 - if ( $post && 'sureforms_form' === $post->post_type ) {
148 - $validated_post_ids[] = $post_id;
149 - }
150 - }
151 -
152 - if ( empty( $validated_post_ids ) ) {
153 - return new \WP_Error(
154 - 'no_valid_forms',
155 - __( 'No valid forms found for export.', 'sureforms' ),
156 - [ 'status' => 400 ]
124 + if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'wp_rest' ) ) {
125 + wp_send_json_error(
126 + [
127 + 'data' => __( 'Nonce verification failed.', 'sureforms' ),
128 + 'status' => false,
129 + ]
157 130 );
158 131 }
159 132
160 - $posts = $this->get_forms_with_meta( $validated_post_ids );
161 -
162 - return new \WP_REST_Response(
163 - [
164 - 'success' => true,
165 - 'data' => $posts,
166 - 'count' => count( $posts ),
167 - ]
168 - );
169 - }
170 -
171 - /**
172 - * Handle Import form via REST API
173 - *
174 - * @param \WP_REST_Request $request Full details about the request.
175 - * @since 2.0.0
176 - * @return \WP_REST_Response|\WP_Error
177 - */
178 - public function handle_import_form_rest( $request ) {
179 - $nonce = sanitize_text_field( Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) ) );
180 -
181 - if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
182 - return new \WP_Error(
183 - 'invalid_nonce',
184 - __( 'Nonce verification failed.', 'sureforms' ),
185 - [ 'status' => 403 ]
133 + if ( ! current_user_can( 'manage_options' ) ) {
134 + // Return error if user does not have permissions to manage options.
135 + wp_send_json_error(
136 + [
137 + 'data' => esc_html__( 'You do not have permissions to manage options.', 'sureforms' ),
138 + 'status' => false,
139 + ]
186 140 );
187 141 }
188 142
189 - $params = $request->get_params();
190 -
191 - // Get forms data from the request.
192 - $forms_data = isset( $params['forms_data'] ) && is_array( $params['forms_data'] ) ? $params['forms_data'] : [];
193 - $default_status = isset( $params['default_status'] ) ? sanitize_text_field( Helper::get_string_value( $params['default_status'] ) ) : 'draft';
194 -
195 - if ( empty( $forms_data ) ) {
196 - return new \WP_Error(
197 - 'no_forms_data',
198 - __( 'No forms data provided for import.', 'sureforms' ),
199 - [ 'status' => 400 ]
200 - );
143 + // Get the raw POST data.
144 + $post_data = file_get_contents( 'php://input' );
145 + if ( ! $post_data ) {
146 + wp_send_json_error( __( 'Failed to import form.', 'sureforms' ) );
201 147 }
202 -
203 - // Validate forms data structure.
204 - foreach ( $forms_data as $form_data ) {
205 - if ( ! is_array( $form_data ) || ! isset( $form_data['post'] ) || ! isset( $form_data['post_meta'] ) ) {
206 - return new \WP_Error(
207 - 'invalid_form_data',
208 - __( 'Invalid form data structure provided.', 'sureforms' ),
209 - [ 'status' => 400 ]
210 - );
211 - }
148 + $data = json_decode( $post_data, true );
149 + if ( ! is_iterable( $data ) ) {
150 + wp_send_json_error( __( 'Failed to import form.', 'sureforms' ) );
212 151 }
152 + foreach ( $data as $form_data ) {
213 153
214 - $result = $this->import_forms_with_meta( $forms_data, $default_status );
215 -
216 - if ( is_wp_error( $result ) ) {
217 - return $result;
218 - }
219 -
220 - return new \WP_REST_Response(
221 - [
222 - 'success' => true,
223 - 'message' => __( 'Forms imported successfully!', 'sureforms' ),
224 - 'forms_mapping' => $result,
225 - 'imported_count' => count( $result ),
226 - ]
227 - );
228 - }
229 -
230 - /**
231 - * Import Forms with Meta
232 - * Uses:
233 - * - In Design Library for importing the Spectra Block Patterns and Pages with SureForms form.
234 - *
235 - * @param array<array<array<string>>> $data Form data to import.
236 - * @param string $default_status Default post status for imported forms. Default is 'draft'.
237 - *
238 - * @since 1.13.0
239 - * @return array<int, int>|\WP_Error Returns mapping array on success, WP_Error on failure.
240 - */
241 - public function import_forms_with_meta( $data, $default_status = 'draft' ) {
242 - $forms_mapping = [];
243 - foreach ( $data as $form_data ) {
244 154 // sanitize the data before saving.
245 - $old_id = intval( $form_data['post']['ID'] );
246 155 $post_content = wp_kses_post( $form_data['post']['post_content'] );
247 156 $post_title = sanitize_text_field( $form_data['post']['post_title'] );
248 157 $post_meta = $form_data['post_meta'];
249 158 $post_type = sanitize_text_field( $form_data['post']['post_type'] );
250 159
251 - // Remove percent-encoded slugs from imported form content.
252 - // Non-Latin labels produce broken slugs like %e3%83%95%e3%83%aa
253 - // via sanitize_title(). Clearing them lets process_blocks()
254 - // regenerate clean block-name-based slugs on save.
255 - $cleaned_content = preg_replace(
256 - '/"slug":"(%[a-fA-F0-9]{2}[^"]*)"/',
257 - '"slug":""',
258 - $post_content
259 - );
260 - if ( is_string( $cleaned_content ) ) {
261 - $post_content = $cleaned_content;
262 - }
160 + $post_content = addslashes( $post_content );
263 161
264 - $post_content = wp_slash( $post_content );
265 -
266 162 // Check if sureforms/form exists in post_content.
267 163 if ( 'sureforms_form' === $post_type ) {
268 164 $new_post = [
269 165 'post_title' => $post_title,
270 - 'post_status' => $default_status,
166 + 'post_status' => 'draft',
271 167 'post_type' => 'sureforms_form',
272 168 ];
273 169
274 170 $post_id = wp_insert_post( $new_post );
@@ -273,13 +169,9 @@
273 169
274 170 $post_id = wp_insert_post( $new_post );
275 171
276 172 // Update the post content formId to the new post id.
277 - $post_content = str_replace(
278 - '\"formId\":' . intval( $form_data['post']['ID'] ),
279 - '\"formId\":' . intval( $post_id ),
280 - $post_content
281 - );
173 + $post_content = str_replace( '\"formId\":' . $form_data['post']['ID'], '\"formId\":' . $post_id, $post_content );
282 174
283 175 // update the post content.
284 176 wp_update_post(
285 177 [
@@ -288,109 +180,50 @@
288 180 ]
289 181 );
290 182
291 183 if ( ! $post_id ) {
292 - return new \WP_Error( 'import_forms_failed', __( 'Unable to import form.', 'sureforms' ) );
184 + http_response_code( 400 );
185 + wp_send_json_error( __( 'Failed to import form.', 'sureforms' ) );
293 186 }
294 -
295 - $forms_mapping[ $old_id ] = $post_id;
296 -
297 187 // Update post meta.
298 - $allowed_keys = $this->get_allowed_import_meta_keys();
299 - $unserialized_meta_keys = $this->get_unserialized_post_metas();
300 - $registered = get_registered_meta_keys( 'post', SRFM_FORMS_POST_TYPE );
301 188 foreach ( $post_meta as $meta_key => $meta_value ) {
302 - // 1. Whitelist check — skip unknown keys from crafted import files.
303 - if ( ! in_array( $meta_key, $allowed_keys, true ) ) {
304 - continue;
305 - }
306 -
307 - // Note: add_post_meta() internally runs wp_unslash() on the value before
308 - // invoking the registered sanitize_callback. Imported values are unslashed,
309 - // so without re-slashing, backslashes are stripped — corrupting JSON-string
310 - // metas (e.g. _srfm_save_resume, _srfm_conditional_confirmation) whose escaped
311 - // quotes (\") then fail json_decode() in their sanitizers, wiping the value to
312 - // an empty string. wp_slash() pre-escapes so wp_unslash() restores the original.
313 - if ( in_array( $meta_key, $unserialized_meta_keys, true ) ) {
314 - // Complex array metas — sanitize_callback registered via register_post_meta()
315 - // is automatically invoked by add_post_meta() → update_metadata() pipeline.
316 - // When Pro is inactive, some keys may lack a registered callback — apply fallback.
317 - if ( empty( $registered[ $meta_key ]['sanitize_callback'] ) ) {
318 - $meta_value = Helper::sanitize_by_type( $meta_value );
319 - }
320 - add_post_meta( $post_id, $meta_key, wp_slash( $meta_value ) );
189 + // Check if the meta key is one of the unserialized post metas then add it as is.
190 + if ( in_array( $meta_key, $this->unserialized_post_metas, true ) ) {
191 + add_post_meta( $post_id, $meta_key, $meta_value );
321 192 } else {
322 - // Scalar metas — unwrap single-element arrays produced by get_post_meta().
323 - $raw_value = is_array( $meta_value ) && isset( $meta_value[0] ) ? $meta_value[0] : $meta_value;
324 - // Fallback sanitization — skip when a registered callback already handles it.
325 - if ( is_string( $raw_value ) && empty( $registered[ $meta_key ]['sanitize_callback'] ) ) {
326 - $raw_value = sanitize_text_field( $raw_value );
193 + if ( is_array( $meta_value ) && isset( $meta_value[0] ) ) {
194 + add_post_meta( $post_id, $meta_key, $meta_value[0] );
195 + } else {
196 + add_post_meta( $post_id, $meta_key, $meta_value );
327 197 }
328 - add_post_meta( $post_id, $meta_key, wp_slash( $raw_value ) );
329 198 }
330 199 }
331 200 } else {
332 - return new \WP_Error( 'import_forms_invalid_post_type', __( 'Unable to import form.', 'sureforms' ) );
201 + http_response_code( 400 );
202 + wp_send_json_error( __( 'Failed to import form.', 'sureforms' ) );
333 203 }
334 204 }
335 205
336 - return $forms_mapping;
206 + // Return the responses.
207 + wp_send_json_success();
337 208 }
338 209
339 210 /**
340 - * Get the list of meta keys allowed during import.
211 + * Add custom API Route submit-form
341 212 *
342 - * Only meta keys present in this list will be written to the DB during import.
343 - * Unknown keys from crafted import files are silently ignored.
344 - *
345 - * @since 2.8.0
346 - * @return array<string>
213 + * @return void
214 + * @since 0.0.1
347 215 */
348 - private function get_allowed_import_meta_keys(): array {
349 - $scalar_metas = [
350 - '_srfm_additional_classes',
351 - '_srfm_bg_color',
352 - '_srfm_bg_image',
353 - '_srfm_bg_type',
354 - '_srfm_button_border_radius',
355 - '_srfm_captcha_security_type',
356 - '_srfm_cover_image',
357 - '_srfm_form_container_width',
358 - '_srfm_form_custom_css',
359 - '_srfm_form_recaptcha',
360 - '_srfm_form_restriction',
361 - '_srfm_inherit_theme_button',
362 - '_srfm_instant_form',
363 - '_srfm_is_ai_generated',
364 - '_srfm_is_inline_button',
365 - '_srfm_single_page_form_title',
366 - '_srfm_submit_alignment',
367 - '_srfm_submit_alignment_backend',
368 - '_srfm_submit_button_text',
369 - '_srfm_submit_type',
370 - '_srfm_submit_width',
371 - '_srfm_submit_width_backend',
372 - '_srfm_use_label_as_placeholder',
373 - ];
374 -
375 - /**
376 - * Filter the list of scalar meta keys allowed during import.
377 - *
378 - * Pro and other extensions can hook into this to add their own scalar meta keys.
379 - *
380 - * @since 2.8.0
381 - * @param array<string> $scalar_metas List of scalar meta keys.
382 - */
383 - $scalar_metas = apply_filters( 'srfm_import_scalar_meta_keys', $scalar_metas );
384 -
385 - // Ensure filter consumers cannot inject non-SureForms meta keys.
386 - $scalar_metas = array_filter(
387 - $scalar_metas,
388 - static function ( $key ) {
389 - return str_starts_with( $key, '_srfm_' );
390 - }
216 + public function register_custom_endpoint() {
217 + register_rest_route(
218 + 'sureforms/v1',
219 + '/sureforms_import',
220 + [
221 + 'methods' => WP_REST_Server::EDITABLE,
222 + 'callback' => [ $this, 'handle_import_form' ],
223 + 'permission_callback' => static function () {
224 + return Helper::current_user_can( 'manage_options' );
225 + },
226 + ]
391 227 );
392 -
393 - return array_merge( $this->get_unserialized_post_metas(), $scalar_metas );
394 228 }
395 -
396 229 }