PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.1.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.1.0
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
sureforms / inc / admin-ajax.php

admin-ajax.php in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 2.1.0, at inc/admin-ajax.php

470 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sureforms Admin Ajax Class.
4 *
5 * Class file for public functions.
6 *
7 * @package sureforms
8 */
9
10 namespace SRFM\Inc;
11
12 use BSF_UTM_Analytics;
13 use SRFM\Inc\Traits\Get_Instance;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 if ( ! function_exists( 'get_plugins' ) ) {
20 require_once ABSPATH . 'wp-admin/includes/plugin.php';
21 }
22
23 /**
24 * Public Class
25 *
26 * @since 0.0.1
27 */
28 class Admin_Ajax {
29 use Get_Instance;
30
31 /**
32 * Constructor
33 *
34 * @since 0.0.1
35 */
36 public function __construct() {
37 add_action( 'wp_ajax_sureforms_recommended_plugin_activate', [ $this, 'required_plugin_activate' ] );
38 add_action( 'wp_ajax_sureforms_recommended_plugin_install', 'wp_ajax_install_plugin' );
39 add_action( 'wp_ajax_sureforms_integration', [ $this, 'generate_data_for_suretriggers_integration' ] );
40 add_action( 'wp_ajax_srfm_download_export', [ $this, 'download_export_file' ] );
41
42 add_filter( SRFM_SLUG . '_admin_filter', [ $this, 'localize_script_integration' ] );
43
44 // adding support for rest-nonce endpoint. To regenerate latest nonce incase of form submission failure.
45 add_action( 'wp_ajax_rest-nonce', [ $this, 'print_rest_nonce' ], 999 );
46 add_action( 'wp_ajax_nopriv_rest-nonce', [ $this, 'print_rest_nonce' ], 999 );
47 }
48
49 /**
50 * Required Plugin Activate
51 *
52 * @return void
53 * @since 0.0.1
54 */
55 public function required_plugin_activate() {
56
57 $response_data = [ 'message' => $this->get_error_msg( 'permission' ) ];
58
59 if ( ! Helper::current_user_can() ) {
60 wp_send_json_error( $response_data );
61 }
62
63 if ( empty( $_POST ) ) {
64 $response_data = [ 'message' => $this->get_error_msg( 'invalid' ) ];
65 wp_send_json_error( $response_data );
66 }
67
68 /**
69 * Nonce verification.
70 */
71 if ( ! check_ajax_referer( 'sf_plugin_manager_nonce', 'security', false ) ) {
72 $response_data = [ 'message' => $this->get_error_msg( 'nonce' ) ];
73 wp_send_json_error( $response_data );
74 }
75
76 if ( ! isset( $_POST['init'] ) || ! sanitize_text_field( wp_unslash( $_POST['init'] ) ) ) {
77 wp_send_json_error(
78 [
79 'success' => false,
80 'message' => __( 'No plugin specified', 'sureforms' ),
81 ]
82 );
83 }
84
85 $plugin_init = isset( $_POST['init'] ) ? sanitize_text_field( wp_unslash( $_POST['init'] ) ) : '';
86
87 $plugin_slug = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '';
88
89 $activate = activate_plugin( $plugin_init, '', false, true );
90
91 if ( is_wp_error( $activate ) ) {
92 wp_send_json_error(
93 [
94 'success' => false,
95 'message' => $activate->get_error_message(),
96 ]
97 );
98 }
99
100 if ( class_exists( 'BSF_UTM_Analytics' ) && is_callable( 'BSF_UTM_Analytics::update_referer' ) ) {
101 $plugin_slug = pathinfo( $plugin_slug, PATHINFO_FILENAME );
102 BSF_UTM_Analytics::update_referer( 'sureforms', $plugin_slug );
103 }
104
105 wp_send_json_success(
106 [
107 'success' => true,
108 'message' => __( 'Plugin Successfully Activated', 'sureforms' ),
109 ]
110 );
111 }
112
113 /**
114 * Get ajax error message.
115 *
116 * @param string $type Message type.
117 * @return string
118 * @since 0.0.2
119 */
120 public function get_error_msg( $type ) {
121
122 if ( ! isset( $this->errors[ $type ] ) ) {
123 $type = 'default';
124 }
125 if ( ! isset( $this->errors ) ) {
126 return '';
127 }
128 return $this->errors[ $type ];
129 }
130
131 /**
132 * Localize the variables required for integration plugins.
133 *
134 * @param array<mixed> $values localized values.
135 * @return array<mixed>
136 * @since 0.0.1
137 */
138 public function localize_script_integration( $values ) {
139 $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
140 return array_merge(
141 $values,
142 [
143 'ajax_url' => admin_url( 'admin-ajax.php' ),
144 'sfPluginManagerNonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
145 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
146 'isRTL' => is_rtl(),
147 'current_screen_id' => $is_screen_sureforms_menu ? 'sureforms_menu' : '',
148 'form_id' => get_post() ? get_post()->ID : '',
149 'suretriggers_nonce' => wp_create_nonce( 'suretriggers_nonce' ),
150 ]
151 );
152 }
153
154 /**
155 * Generates data required for suretriggers integration
156 *
157 * @since 0.0.8
158 * @return void
159 */
160 public function generate_data_for_suretriggers_integration() {
161 if ( ! Helper::current_user_can() ) {
162 wp_send_json_error( [ 'message' => __( 'You do not have permission to access this page.', 'sureforms' ) ] );
163 }
164
165 if ( ! check_ajax_referer( 'suretriggers_nonce', 'security', false ) ) {
166 wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ] );
167 }
168
169 if ( empty( $_POST['formId'] ) ) {
170 wp_send_json_error( [ 'message' => __( 'Form ID is required.', 'sureforms' ) ] );
171 }
172
173 if ( ! Helper::is_suretriggers_ready() ) {
174 wp_send_json_error(
175 [
176 'code' => 'invalid_secret_key',
177 'message' => __( 'OttoKit is not configured properly.', 'sureforms' ),
178 ]
179 );
180 }
181
182 $form_id = Helper::get_integer_value( sanitize_text_field( wp_unslash( $_POST['formId'] ) ) );
183 $form = get_post( $form_id );
184
185 if ( is_null( $form ) || SRFM_FORMS_POST_TYPE !== $form->post_type ) {
186 wp_send_json_error( [ 'message' => __( 'Invalid form ID.', 'sureforms' ) ] );
187 }
188
189 // Translators: %s: Form ID.
190 $form_name = ! empty( $form->post_title ) ? $form->post_title : sprintf( __( 'SureForms id: %s', 'sureforms' ), $form_id );
191 $api_url = apply_filters( 'suretriggers_get_iframe_url', SRFM_SURETRIGGERS_INTEGRATION_BASE_URL );
192
193 // This is the format of data required by SureTriggers for adding iframe in target id.
194 $body = [
195 'client_id' => 'SureForms',
196 'st_embed_url' => $api_url,
197 'embedded_identifier' => $form_id,
198 'target' => 'suretriggers-iframe-wrapper', // div where we want SureTriggers to add iframe should have this target id.
199 'event' => [
200 'label' => __( 'Form Submitted', 'sureforms' ),
201 'value' => 'sureforms_form_submitted',
202 'description' => __( 'Runs when a form is submitted', 'sureforms' ),
203 ],
204 'summary' => $form_name,
205 'selected_options' => [
206 'form_id' => [
207 'value' => $form_id,
208 'label' => $form_name,
209 ],
210 ],
211 'integration' => 'SureForms',
212 'sample_response' => [
213 'form_id' => $form_id,
214 'to_emails' => [
215 'dev-email@wpengine.local',
216 ],
217 'form_name' => $form_name,
218 'data' => $this->get_form_fields( $form_id ),
219 ],
220 ];
221
222 // Adding entry_id in body sample response if do_not_store_entries is not enabled.
223 $compliance = get_post_meta( $form_id, '_srfm_compliance', true );
224 $do_not_store_entries = is_array( $compliance ) && isset( $compliance[0]['do_not_store_entries'] )
225 ? $compliance[0]['do_not_store_entries']
226 : null;
227
228 if ( ! $do_not_store_entries ) {
229 $body['sample_response']['entry_id'] = 12;
230 }
231
232 wp_send_json_success(
233 [
234 'message' => 'success',
235 'data' => apply_filters( 'srfm_suretriggers_integration_data_filter', $body, $form_id ),
236 ]
237 );
238 }
239
240 /**
241 * This function populates data for particular form.
242 *
243 * @param int $form_id Form ID.
244 * @since 0.0.8
245 * @return array<mixed>
246 */
247 public function get_form_fields( $form_id ) {
248 if ( empty( $form_id ) || ! is_int( $form_id ) ) {
249 return [];
250 }
251
252 if ( SRFM_FORMS_POST_TYPE !== get_post_type( $form_id ) ) {
253 return [];
254 }
255
256 $post = get_post( $form_id );
257
258 if ( is_null( $post ) ) {
259 return [];
260 }
261
262 $blocks = parse_blocks( $post->post_content );
263
264 $blocks = array_filter(
265 $blocks,
266 static function( $block ) {
267 if ( 'srfm/html' === $block['blockName'] ) {
268 return false;
269 }
270 return true;
271 }
272 );
273
274 $blocks = array_values( $blocks );
275
276 if ( empty( $blocks ) ) {
277 return [];
278 }
279
280 $data = [];
281
282 foreach ( $blocks as $block ) {
283 if ( ! empty( $block['blockName'] ) && 0 === strpos( $block['blockName'], 'srfm/' ) ) {
284
285 /**
286 * Determine whether to skip this field from the sample data.
287 *
288 * @param bool $should_skip Default value indicating if field should be skipped.
289 * @param array $block_details Array containing block attributes, including 'block_name'.
290 *
291 * @since 2.0.0
292 *
293 * @hook srfm_should_skip_field_from_sample_data
294 */
295 $should_skip_this_field = apply_filters(
296 'srfm_should_skip_field_from_sample_data',
297 false,
298 [
299 'block_name' => $block['blockName'],
300 ]
301 );
302
303 if ( $should_skip_this_field ) {
304 continue;
305 }
306
307 if ( ! empty( $block['attrs']['slug'] ) ) {
308 $data[ $block['attrs']['slug'] ] = $this->get_sample_data( $block['blockName'] );
309 }
310 }
311 }
312
313 if ( empty( $data ) ) {
314 return [];
315 }
316
317 return $data;
318 }
319
320 /**
321 * Returns sample data for a block.
322 *
323 * @param string $block_name Block name.
324 * @since 0.0.8
325 * @return mixed
326 */
327 public function get_sample_data( $block_name ) {
328 if ( empty( $block_name ) ) {
329 return __( 'Sample data', 'sureforms' );
330 }
331
332 $dummy_data = [
333 'srfm/input' => __( 'Sample input data', 'sureforms' ),
334 'srfm/email' => 'noreply@sureforms.com',
335 'srfm/textarea' => __( 'Sample textarea data', 'sureforms' ),
336 'srfm/number' => 123,
337 'srfm/checkbox' => 'checkbox value',
338 'srfm/gdpr' => 'GDPR value',
339 'srfm/phone' => '1234567890',
340 'srfm/address' => __( 'Address data', 'sureforms' ),
341 'srfm/address-compact' => __( 'Address data', 'sureforms' ),
342 'srfm/dropdown' => __( 'Selected dropdown option', 'sureforms' ),
343 'srfm/multi-choice' => __( 'Selected Multichoice option', 'sureforms' ),
344 'srfm/radio' => __( 'Selected radio option', 'sureforms' ),
345 'srfm/submit' => __( 'Submit', 'sureforms' ),
346 'srfm/url' => 'https://example.com',
347 'srfm/date-time-picker' => '2022-01-01 12:00:00',
348 'srfm/hidden' => __( 'Hidden Value', 'sureforms' ),
349 'srfm/slider' => 50,
350 'srfm/password' => 'DummyPassword123',
351 'srfm/rating' => 4,
352 'srfm/upload' => 'https://example.com/uploads/file.pdf',
353 ];
354
355 /**
356 * Filter the sample data for specific block types.
357 *
358 * Allows plugins and themes to add custom sample data for their block types
359 * or modify existing sample data. This is particularly useful for dynamic
360 * block types that require complex sample data structures.
361 *
362 * @since 0.0.8
363 *
364 * @param array $dummy_data {
365 * Array of sample data keyed by block name.
366 *
367 * @type string|array $block_name Sample data for the block.
368 * }
369 * @param array $filter_args {
370 * Additional filter arguments.
371 *
372 * @type string $block_name The name of the block being processed.
373 * }
374 */
375 $dummy_data = Helper::apply_filters_as_array( 'srfm_sample_data_filter', $dummy_data, [ 'block_name' => $block_name ] );
376
377 if ( ! empty( $dummy_data[ $block_name ] ) ) {
378 return $dummy_data[ $block_name ];
379 }
380 return __( 'Sample data', 'sureforms' );
381 }
382
383 /**
384 * This function will echo wp_rest nonce
385 * was required to provide nonce for fallback of wp.apiFetch
386 *
387 * @since 1.2.4
388 * @return void
389 */
390 public function print_rest_nonce() {
391 echo esc_js( wp_create_nonce( 'wp_rest' ) );
392 exit;
393 }
394
395 /**
396 * Download exported file.
397 *
398 * @since 2.0.0
399 * @return void
400 */
401 public function download_export_file() {
402 // Check user permissions.
403 if ( ! Helper::current_user_can() ) {
404 wp_die( esc_html__( 'You do not have permission to access this file.', 'sureforms' ) );
405 }
406
407 // Verify nonce for security.
408 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'srfm_download_export' ) ) {
409 wp_die( esc_html__( 'Security check failed.', 'sureforms' ) );
410 }
411
412 // Get and sanitize the file parameter.
413 $file = isset( $_GET['file'] ) ? sanitize_file_name( wp_unslash( $_GET['file'] ) ) : '';
414
415 if ( empty( $file ) ) {
416 wp_die( esc_html__( 'Invalid file request.', 'sureforms' ) );
417 }
418
419 // Build the full file path.
420 $temp_dir = wp_normalize_path( trailingslashit( get_temp_dir() ) );
421 $filepath = $temp_dir . $file;
422
423 // Security check: ensure the file is in the temp directory.
424 if ( strpos( wp_normalize_path( $filepath ), $temp_dir ) !== 0 ) {
425 wp_die( esc_html__( 'Invalid file path.', 'sureforms' ) );
426 }
427
428 // Check if file exists.
429 if ( ! file_exists( $filepath ) ) {
430 wp_die( esc_html__( 'File not found.', 'sureforms' ) );
431 }
432
433 // Get file info.
434 $file_size = filesize( $filepath );
435 $file_info = pathinfo( $filepath );
436
437 // Determine content type and filename based on file extension.
438 $content_type = 'application/octet-stream';
439 $filename = $file_info['basename'];
440 if ( isset( $file_info['extension'] ) ) {
441 if ( 'csv' === $file_info['extension'] ) {
442 $content_type = 'text/csv';
443 } elseif ( 'zip' === $file_info['extension'] ) {
444 $content_type = 'application/zip';
445 $filename = 'SureForms Entries.zip';
446 }
447 }
448
449 // Set headers for download.
450 header( 'Content-Type: ' . $content_type );
451 header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
452 header( 'Content-Length: ' . $file_size );
453 header( 'Cache-Control: private, max-age=0, must-revalidate' );
454 header( 'Pragma: public' );
455
456 // Clear output buffers.
457 if ( ob_get_level() ) {
458 ob_end_clean();
459 }
460
461 // Output the file.
462 readfile( $filepath ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_readfile -- Need direct file output for download.
463
464 // Clean up the temporary file.
465 unlink( $filepath );
466
467 exit;
468 }
469 }
470