PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 1.2.5
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v1.2.5
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 / form-submit.php
form-submit.php
896 lines 29.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sureforms Submit Class file.
4 *
5 * @package sureforms.
6 * @since 0.0.1
7 */
8
9 namespace SRFM\Inc;
10
11 use SRFM\Inc\Database\Tables\Entries;
12 use SRFM\Inc\Email\Email_Template;
13 use SRFM\Inc\Lib\Browser\Browser;
14 use SRFM\Inc\Traits\Get_Instance;
15 use WP_Error;
16 use WP_REST_Server;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit; // Exit if accessed directly.
20 }
21
22 if ( ! function_exists( 'wp_handle_upload' ) ) {
23 require_once ABSPATH . 'wp-admin/includes/file.php';
24 }
25
26 /**
27 * Sureforms Submit Class.
28 *
29 * @since 0.0.1
30 */
31 class Form_Submit {
32 use Get_Instance;
33
34 /**
35 * Namespace.
36 *
37 * @var string
38 */
39 protected $namespace = 'sureforms/v1';
40
41 /**
42 * Constructor
43 *
44 * @since 0.0.1
45 */
46 public function __construct() {
47 add_action( 'rest_api_init', [ $this, 'register_custom_endpoint' ] );
48 add_action( 'wp_ajax_validation_ajax_action', [ $this, 'field_unique_validation' ] );
49 add_action( 'wp_ajax_nopriv_validation_ajax_action', [ $this, 'field_unique_validation' ] );
50 // for quick action bar.
51 add_action( 'wp_ajax_srfm_global_update_allowed_block', [ $this, 'srfm_global_update_allowed_block' ] );
52 add_action( 'wp_ajax_srfm_global_sidebar_enabled', [ $this, 'srfm_global_sidebar_enabled' ] );
53 }
54
55 /**
56 * Add custom API Route submit-form
57 *
58 * @return void
59 * @since 0.0.1
60 */
61 public function register_custom_endpoint() {
62 register_rest_route(
63 $this->namespace,
64 '/submit-form',
65 [
66 'methods' => WP_REST_Server::EDITABLE,
67 'callback' => [ $this, 'handle_form_submission' ],
68 'permission_callback' => '__return_true',
69 ]
70 );
71 }
72
73 /**
74 * Check whether a given request has permission access route.
75 *
76 * @since 0.0.1
77 * @return WP_Error|bool
78 */
79 public function permissions_check() {
80 if ( ! current_user_can( 'manage_options' ) ) {
81 return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot access this route', 'sureforms' ), [ 'status' => rest_authorization_required_code() ] );
82 }
83 return true;
84 }
85
86 /**
87 * Validate Turnstile token
88 *
89 * @param string $secret_key Turnstile token.
90 * @param string|false $response Response.
91 * @param string|false $remote_ip Remote IP.
92 * @return array<mixed>|mixed Result of the validation.
93 */
94 public static function validate_turnstile_token( $secret_key, $response, $remote_ip ) {
95
96 if ( empty( $secret_key ) || ! is_string( $secret_key ) ) {
97 return [
98 'success' => false,
99 'error' => __( 'Cloudflare Turnstile secret key is invalid.', 'sureforms' ),
100 ];
101 }
102
103 if ( empty( $response ) ) {
104 return [
105 'success' => false,
106 'error' => __( 'Cloudflare Turnstile response is missing.', 'sureforms' ),
107 ];
108 }
109
110 $body = [
111 'secret' => $secret_key,
112 'response' => $response,
113 'remoteip' => $remote_ip,
114 ];
115
116 $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
117
118 $args = [
119 'body' => $body,
120 'timeout' => 15,
121 ];
122
123 $response = wp_remote_post( $url, $args );
124
125 if ( is_wp_error( $response ) ) {
126 $error_message = $response->get_error_message();
127 return [
128 'success' => false,
129 'error' => $error_message,
130 ];
131 }
132
133 return json_decode( wp_remote_retrieve_body( $response ), true );
134 }
135
136 /**
137 * Validate hCaptcha token
138 *
139 * @param string $secret_key hCaptcha token.
140 * @param string|false $response Response.
141 * @param string|false $remote_ip Remote IP.
142 * @since 0.0.5
143 * @return array<mixed>|mixed Result of the validation.
144 */
145 public static function validate_hcaptcha_token( $secret_key, $response, $remote_ip ) {
146
147 if ( empty( $secret_key ) || ! is_string( $secret_key ) ) {
148 return [
149 'success' => false,
150 'error' => __( 'hCaptcha secret key is invalid.', 'sureforms' ),
151 ];
152 }
153
154 if ( empty( $response ) ) {
155 return [
156 'success' => false,
157 'error' => __( 'hCaptcha response is missing.', 'sureforms' ),
158 ];
159 }
160
161 $body = [
162 'secret' => $secret_key,
163 'response' => $response,
164 'remoteip' => $remote_ip,
165 ];
166
167 $url = 'https://api.hcaptcha.com/siteverify';
168
169 $args = [
170 'body' => $body,
171 'timeout' => 15,
172 ];
173
174 $response = wp_remote_post( $url, $args );
175
176 if ( is_wp_error( $response ) ) {
177 $error_message = $response->get_error_message();
178 return [
179 'success' => false,
180 'error' => $error_message,
181 ];
182 }
183
184 return json_decode( wp_remote_retrieve_body( $response ), true );
185 }
186
187 /**
188 * Handle Form Submission
189 *
190 * @param \WP_REST_Request $request Request object or array containing form data.
191 * @since 0.0.1
192 * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
193 */
194 public function handle_form_submission( $request ) {
195
196 $nonce = Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) );
197
198 if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'wp_rest' ) ) {
199 wp_send_json_error(
200 [
201 'data' => __( 'Nonce verification failed.', 'sureforms' ),
202 'status' => false,
203 ]
204 );
205 }
206
207 $form_data = Helper::sanitize_by_field_type( $request->get_params() );
208
209 if ( empty( $form_data ) || ! is_array( $form_data ) ) {
210 wp_send_json_error( __( 'Form data is not found.', 'sureforms' ) );
211 }
212
213 if ( ! $form_data['form-id'] ) {
214 wp_send_json_error( __( 'Form Id is missing.', 'sureforms' ) );
215 }
216 $current_form_id = $form_data['form-id'];
217 $security_type = Helper::get_meta_value( Helper::get_integer_value( $current_form_id ), '_srfm_captcha_security_type' );
218 $selected_captcha_type = get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_form_recaptcha', true ) ? Helper::get_string_value( get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_form_recaptcha', true ) ) : '';
219
220 if ( 'none' !== $security_type ) {
221 $global_setting_options = get_option( 'srfm_security_settings_options' );
222 } else {
223 $global_setting_options = [];
224 }
225
226 if ( 'g-recaptcha' === $security_type ) {
227 switch ( $selected_captcha_type ) {
228 case 'v2-checkbox':
229 $key = 'srfm_v2_checkbox_secret_key';
230 break;
231 case 'v2-invisible':
232 $key = 'srfm_v2_invisible_secret_key';
233 break;
234 case 'v3-reCAPTCHA':
235 $key = 'srfm_v3_secret_key';
236 break;
237 default:
238 $key = '';
239 break;
240 }
241
242 $google_captcha_secret_key = is_array( $global_setting_options ) && isset( $global_setting_options[ $key ] ) ? $global_setting_options[ $key ] : '';
243 }
244
245 if ( 'cf-turnstile' === $security_type ) {
246 // Turnstile validation.
247 $srfm_cf_turnstile_secret_key = is_array( $global_setting_options ) && isset( $global_setting_options['srfm_cf_turnstile_secret_key'] ) ? Helper::get_string_value( $global_setting_options['srfm_cf_turnstile_secret_key'] ) : '';
248 $cf_response = ! empty( $form_data['cf-turnstile-response'] ) ? $form_data['cf-turnstile-response'] : false;
249
250 // if gdpr is enabled then set remote ip to empty.
251 $compliance = get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_compliance', true );
252 $gdpr = false;
253
254 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
255 $gdpr = ! empty( $compliance[0]['gdpr'] ) ? $compliance[0]['gdpr'] : false;
256 }
257
258 // check if ip logging is disabled in global settings then set remote ip to empty.
259 $gb_general_settinionsgs_opt = get_option( 'srfm_general_settings_options' );
260 $srfm_ip_log = is_array( $gb_general_settinionsgs_opt ) && isset( $gb_general_settinionsgs_opt['srfm_ip_log'] ) ? $gb_general_settinionsgs_opt['srfm_ip_log'] : '';
261
262 $remote_ip = $gdpr || ( ! $srfm_ip_log ) ? '' : ( isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '' );
263
264 $turnstile_validation_result = self::validate_turnstile_token( $srfm_cf_turnstile_secret_key, $cf_response, $remote_ip );
265
266 // If the cloudflare validation fails, return an error.
267 if ( is_array( $turnstile_validation_result ) && isset( $turnstile_validation_result['success'] ) && false === $turnstile_validation_result['success'] ) {
268 $error_message = $turnstile_validation_result['error'] ?? __( 'Cloudflare Turnstile validation failed.', 'sureforms' );
269 return new \WP_Error( 'cf_turnstile_error', $error_message, [ 'status' => 403 ] );
270 }
271 }
272
273 if ( 'hcaptcha' === $security_type ) {
274 $srfm_hcaptcha_secret_key = is_array( $global_setting_options ) && isset( $global_setting_options['srfm_hcaptcha_secret_key'] ) ? Helper::get_string_value( $global_setting_options['srfm_hcaptcha_secret_key'] ) : '';
275 $hcaptcha_response = ! empty( $form_data['h-captcha-response'] ) ? $form_data['h-captcha-response'] : false;
276
277 // if gdpr is enabled then set remote ip to empty.
278 $compliance = get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_compliance', true );
279 $gdpr = false;
280
281 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
282 $gdpr = ! empty( $compliance[0]['gdpr'] ) ? $compliance[0]['gdpr'] : false;
283 }
284
285 // check if ip logging is disabled in global settings then set remote ip to empty.
286 $gb_general_settings_options = get_option( 'srfm_general_settings_options' );
287 $srfm_ip_log = is_array( $gb_general_settings_options ) && isset( $gb_general_settings_options['srfm_ip_log'] ) ? $gb_general_settings_options['srfm_ip_log'] : '';
288
289 $remote_ip = $gdpr || ( ! $srfm_ip_log ) ? '' : ( isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '' );
290 $hcaptcha_validation_result = self::validate_hcaptcha_token( $srfm_hcaptcha_secret_key, $hcaptcha_response, $remote_ip );
291
292 // If the hcaptcha validation fails, return an error.
293 if ( is_array( $hcaptcha_validation_result ) && isset( $hcaptcha_validation_result['success'] ) && false === $hcaptcha_validation_result['success'] ) {
294 $error_message = $hcaptcha_validation_result['error'] ?? __( 'hCaptcha validation failed.', 'sureforms' );
295 return new \WP_Error( 'hcaptcha_error', $error_message, [ 'status' => 403 ] );
296 }
297 }
298
299 if ( isset( $form_data['srfm-honeypot-field'] ) && empty( $form_data['srfm-honeypot-field'] ) ) {
300 if ( ! empty( $google_captcha_secret_key ) ) {
301 if ( isset( $form_data['sureforms_form_submit'] ) ) {
302 $secret_key = $google_captcha_secret_key;
303 $ipaddress = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
304 $captcha_response = $form_data['g-recaptcha-response'];
305 $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $captcha_response . '&ip=' . $ipaddress;
306
307 $response = wp_remote_get( $url );
308
309 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
310 $json_string = wp_remote_retrieve_body( $response );
311 $data = (array) json_decode( $json_string, true );
312 } else {
313 $data = [];
314 }
315 $sureforms_captcha_data = $data;
316
317 } else {
318 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
319 }
320 if ( isset( $sureforms_captcha_data['success'] ) && true === $sureforms_captcha_data['success'] ) {
321 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
322 }
323 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
324
325 }
326 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
327
328 }
329 if ( ! isset( $form_data['srfm-honeypot-field'] ) ) {
330 if ( ! empty( $google_captcha_secret_key ) ) {
331 if ( isset( $form_data['sureforms_form_submit'] ) ) {
332 $secret_key = $google_captcha_secret_key;
333 $ipaddress = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
334 $captcha_response = $form_data['g-recaptcha-response'];
335 $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $captcha_response . '&ip=' . $ipaddress;
336
337 $response = wp_remote_get( $url );
338
339 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
340 $json_string = wp_remote_retrieve_body( $response );
341 $data = (array) json_decode( $json_string, true );
342 } else {
343 $data = [];
344 }
345 $sureforms_captcha_data = $data;
346
347 } else {
348 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
349 }
350 if ( true === $sureforms_captcha_data['success'] ) {
351 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
352 }
353 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
354
355 }
356 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
357
358 }
359 return new \WP_Error( 'spam_detected', __( 'Spam Detected', 'sureforms' ), [ 'status' => 403 ] );
360 }
361
362 /**
363 * Change the upload directory
364 *
365 * @param array<mixed> $dirs upload directory.
366 * @return array<mixed>
367 * @since 0.0.1
368 */
369 public function change_upload_dir( $dirs ) {
370 $dirs['subdir'] = '/sureforms';
371 $dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
372 $dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
373 return $dirs;
374 }
375
376 /**
377 * Send Email and Create Entry.
378 *
379 * @param array<string> $form_data Request object or array containing form data.
380 * @since 0.0.1
381 * @return array<mixed> Array containing the response data.
382 */
383 public function handle_form_entry( $form_data ) {
384 $is_error = false;
385 if ( defined( 'SRFM_PRO_VER' ) && isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] && ! empty( $_FILES ) ) {
386 add_filter( 'upload_dir', [ $this, 'change_upload_dir' ] );
387
388 // Get the file types.
389 $file_types = Helper::get_wp_file_types();
390
391 // Get the allowed file types.
392 $allowed_file_types = $file_types['formats'];
393
394 // Allowed file types should be array.
395 if ( ! is_array( $allowed_file_types ) ) {
396 $is_error = true;
397 wp_send_json_error(
398 [
399 'message' => __( 'File types are not allowed', 'sureforms' ),
400 ]
401 );
402 }
403
404 foreach ( $_FILES as $field => $file ) {
405 if ( is_array( $file['name'] ) ) {
406 foreach ( $file['name'] as $key => $filename ) {
407 $temp_path = $file['tmp_name'][ $key ];
408 $file_size = $file['size'][ $key ];
409 $file_type = $file['type'][ $key ];
410 $file_error = $file['error'][ $key ];
411
412 if ( ! $filename && ! $temp_path && ! $file_size && ! $file_type ) {
413 $form_data[ $field ][] = '';
414 continue;
415 }
416
417 // Check if the file type is allowed.
418 $get_file_type = explode( '/', $file_type );
419
420 // Check isset $get_file_type[1] it should be string.
421 if ( ! isset( $get_file_type[1] ) ) {
422 $is_error = true;
423 continue;
424 }
425
426 // $get_file_type[1] should be string.
427 if ( ! is_string( $get_file_type[1] ) ) {
428 $is_error = true;
429 continue;
430 }
431
432 // Check if the file type is allowed.
433 if ( ! in_array( $get_file_type[1], $allowed_file_types, true ) ) {
434 $is_error = true;
435 continue;
436 }
437
438 $uploaded_file = [
439 'name' => sanitize_file_name( $filename ),
440 'type' => $file_type,
441 'tmp_name' => $temp_path,
442 'error' => $file_error,
443 'size' => $file_size,
444 ];
445
446 $upload_overrides = [
447 'test_form' => false,
448 ];
449 $move_file = wp_handle_upload( $uploaded_file, $upload_overrides );
450 remove_filter( 'upload_dir', [ $this, 'change_upload_dir' ] );
451
452 if ( $move_file && ! isset( $move_file['error'] ) ) {
453 $form_data[ $field ][] = $move_file['url'];
454 } else {
455 $is_error = true;
456 continue;
457 }
458 }
459 } else {
460 $form_data[ $field ][] = '';
461 }
462 }
463 }
464
465 if ( $is_error ) {
466 wp_send_json_error(
467 [
468 'message' => __( 'File is not uploaded', 'sureforms' ),
469 ]
470 );
471 }
472
473 $id = sanitize_text_field( $form_data['form-id'] );
474
475 // Get the compliance settings.
476 $compliance = get_post_meta( Helper::get_integer_value( $id ), '_srfm_compliance', true );
477 $gdpr = '';
478 $do_not_store_entries = '';
479
480 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
481 $gdpr = $compliance[0]['gdpr'] ?? '';
482 $do_not_store_entries = $compliance[0]['do_not_store_entries'] ?? '';
483 }
484
485 $submission_data = [];
486
487 $form_data_keys = array_keys( $form_data );
488 $form_data_count = count( $form_data );
489
490 for ( $i = 0; $i < $form_data_count; $i++ ) {
491 $key = strval( $form_data_keys[ $i ] );
492
493 /**
494 * This will allow to pass only sureforms fields
495 * checking -lbl- as thats mandatory for in key of sureforms fields.
496 */
497 if ( false === str_contains( $key, '-lbl-' ) ) {
498 continue;
499 }
500
501 $value = $form_data[ $key ];
502
503 $field_name = htmlspecialchars( str_replace( '_', ' ', $key ) );
504
505 // If the field is an array, encode the values. This is to add support for multi-upload field.
506 if ( is_array( $value ) ) {
507 $submission_data[ $field_name ] =
508 array_map(
509 static function ( $val ) {
510 return rawurlencode( $val );
511 },
512 $value
513 );
514 } else {
515 $submission_data[ $field_name ] = htmlspecialchars( $value );
516 }
517 }
518
519 $modified_message = $this->prepare_submission_data( $submission_data );
520
521 $form_before_submission_data = [
522 'form_id' => $id ? intval( $id ) : '',
523 'data' => $modified_message,
524 ];
525
526 /**
527 * Fires before submission process starts.
528 */
529 do_action( 'srfm_before_submission', $form_before_submission_data );
530
531 $name = sanitize_text_field( get_the_title( intval( $id ) ) );
532 $send_email = $this->send_email( $id, $submission_data, $form_data );
533 $emails = [];
534
535 if ( $send_email ) {
536 $emails = $send_email['emails'];
537 }
538
539 // Check if GDPR is enabled and do not store entries is enabled.
540 // If so, send email and do not store entries.
541 if ( $gdpr && $do_not_store_entries ) {
542
543 $form_submit_response = [
544 'success' => true,
545 'form_id' => $id ? intval( $id ) : '',
546 'to_emails' => $emails,
547 'form_name' => $name ? esc_attr( $name ) : '',
548 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
549 'data' => $modified_message,
550 ];
551
552 do_action( 'srfm_form_submit', $form_submit_response );
553
554 /**
555 * Hook for enabling background processes.
556 *
557 * @param array $form_data form data related to submission.
558 */
559 $form_data['form_id'] = $id ? intval( $id ) : '';
560 do_action( 'srfm_after_submission_process', $form_data );
561
562 return [
563 'success' => true,
564 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
565 'data' => [
566 'name' => $name,
567 'after_submit' => false,
568 ],
569 'redirect_url' => Generate_Form_Markup::get_redirect_url( $form_data, $submission_data ),
570 ];
571
572 }
573
574 $global_setting_options = get_option( 'srfm_general_settings_options' );
575
576 // If GDPR is enabled, do not store IP, browser, and device info.
577 // If not, store IP, browser, and device info.
578 $user_ip = '';
579 $browser_name = '';
580 $device_name = '';
581 if ( ! $gdpr ) {
582 $srfm_ip_log = is_array( $global_setting_options ) && isset( $global_setting_options['srfm_ip_log'] ) ? $global_setting_options['srfm_ip_log'] : '';
583
584 $user_ip = $srfm_ip_log && isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
585 $browser = new Browser();
586 $browser_name = sanitize_text_field( $browser->getBrowser() );
587 $device_name = sanitize_text_field( $browser->getPlatform() );
588 }
589
590 $form_markup = get_the_content( null, false, Helper::get_integer_value( $form_data['form-id'] ) );
591 $pattern = '/"label":"(.*?)"/';
592 preg_match_all( $pattern, $form_markup, $matches );
593 $submission_info = [
594 'user_ip' => $user_ip,
595 'browser_name' => $browser_name,
596 'device_name' => $device_name,
597 ];
598 $entries_data = [
599 'form_id' => $id,
600 'form_data' => $submission_data,
601 'submission_info' => $submission_info,
602 ];
603 if ( is_user_logged_in() ) {
604 // If user is logged in then save their user id.
605 $entries_data['user_id'] = get_current_user_id();
606 }
607 $entry_id = Entries::add( $entries_data );
608 if ( $entry_id ) {
609
610 $response = [
611 'success' => true,
612 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
613 'data' => [
614 'name' => $name,
615 'submission_id' => $entry_id,
616 'after_submit' => true,
617 ],
618 'redirect_url' => Generate_Form_Markup::get_redirect_url( $form_data, $submission_data ),
619 ];
620
621 $form_submit_response = apply_filters(
622 'srfm_form_submit_response',
623 [
624 'success' => true,
625 'form_id' => $id ? intval( $id ) : '',
626 'entry_id' => intval( $entry_id ),
627 'to_emails' => $emails,
628 'form_name' => $name ? esc_attr( $name ) : '',
629 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
630 'data' => $modified_message,
631 ]
632 );
633
634 do_action( 'srfm_form_submit', $form_submit_response );
635 } else {
636 $response = [
637 'success' => false,
638 'message' => __( 'Error submitting form', 'sureforms' ),
639 ];
640 }
641
642 return $response;
643 }
644
645 /**
646 * Prepare submission data.
647 *
648 * @param array<mixed> $submission_data Submission data.
649 * @since 0.0.7
650 * @return array<mixed> Modified submission data.
651 */
652 public function prepare_submission_data( $submission_data ) {
653 $modified_message = [];
654 foreach ( $submission_data as $key => $value ) {
655 $parts = explode( '-lbl-', $key );
656 $label = '';
657
658 if ( ! empty( $parts[1] ) ) {
659 $tokens = explode( '-', $parts[1] );
660 if ( count( $tokens ) > 1 ) {
661 $label = implode( '-', array_slice( $tokens, 1 ) );
662 }
663
664 $fields = explode( '-', $parts[0] );
665
666 // Since the upload field returns an array of file URLs, we need to implode them with a comma.
667 if ( 'upload' === $fields[1] && ! empty( $value ) && is_array( $value ) ) {
668 $modified_message[ $label ] = urldecode( implode( ', ', $value ) );
669 } else {
670 $modified_message[ $label ] = html_entity_decode( esc_attr( Helper::get_string_value( $value ) ) );
671 }
672 }
673 }
674
675 return $modified_message;
676 }
677
678 /**
679 * Send Email.
680 *
681 * @param string $id Form ID.
682 * @param array<mixed> $submission_data Submission data.
683 * @param array<string> $form_data Request object or array containing form data.
684 * @since 0.0.1
685 * @return array<mixed> Array containing the response data.
686 */
687 public static function send_email( $id, $submission_data, $form_data = [] ) {
688 $email_notification = get_post_meta( intval( $id ), '_srfm_email_notification' );
689 $smart_tags = new Smart_Tags();
690 $is_mail_sent = false;
691 $emails = [];
692
693 if ( is_iterable( $email_notification ) ) {
694 $entries_db_instance = Entries::get_instance();
695 $log_key = $entries_db_instance->add_log( __( 'Email Notification Initiated', 'sureforms' ) );
696
697 foreach ( $email_notification as $notification ) {
698 foreach ( $notification as $item ) {
699 if ( true === $item['status'] ) {
700 $from = Helper::get_string_value( get_option( 'admin_email' ) );
701 $to = $smart_tags->process_smart_tags( $item['email_to'], $submission_data );
702 $subject = $smart_tags->process_smart_tags( $item['subject'], $submission_data, $form_data );
703 $email_body = $smart_tags->process_smart_tags( $item['email_body'], $submission_data, $form_data );
704 $email_template = new Email_Template();
705 $message = $email_template->render( $submission_data, $email_body );
706 $headers = "From: {$from}\r\nX-Mailer: PHP/" . phpversion() . "\r\nContent-Type: text/html; charset=utf-8\r\n";
707 if ( isset( $item['email_reply_to'] ) && ! empty( $item['email_reply_to'] ) ) {
708 $headers .= 'Reply-To:' . $smart_tags->process_smart_tags( $item['email_reply_to'], $submission_data ) . "\r\n";
709 } else {
710 $headers .= "Reply-To: {$from}\r\n";
711 }
712 if ( isset( $item['email_cc'] ) && ! empty( $item['email_cc'] ) ) {
713 $headers .= 'Cc:' . $smart_tags->process_smart_tags( $item['email_cc'], $submission_data ) . "\r\n";
714 }
715 if ( isset( $item['email_bcc'] ) && ! empty( $item['email_bcc'] ) ) {
716 $headers .= 'Bcc:' . $smart_tags->process_smart_tags( $item['email_bcc'], $submission_data ) . "\r\n";
717 }
718
719 /**
720 * Temporary override the content type for wp_mail.
721 * This helps us from breaking of content type from other plugins.
722 *
723 * @since 1.2.2
724 */
725 add_filter(
726 'wp_mail_content_type',
727 static function() {
728 return 'text/html'; // We need "text/html" content type to render our emails.
729 },
730 99
731 );
732
733 /**
734 * Start sending email.
735 * Wrapping it in the buffer because when some plugin such as zoho mail, overrides the wp_mail
736 * function and any exception is thrown ( Or printed ) from that plugin side, it affects the JSON response.
737 * So, to make sure such exceptions doesn't affect our JSON response, we are wrapping it inside buffer.
738 *
739 * Try-Catch does not work because the notice or errors might be echoed by other plugins rather than thrown as an exception.
740 *
741 * @since 1.2.2
742 */
743 $sent = false;
744 ob_start();
745 $sent = wp_mail( $to, $subject, $message, $headers );
746 if ( ! $sent ) {
747 // Fallback to default PHP mail if for some reasons wp_mail fails.
748 $sent = mail( $to, $subject, $message, $headers );
749 }
750 $email_report = ob_get_clean(); // Catch any printed notice/errors/message for reports.
751
752 if ( is_int( $log_key ) ) {
753 if ( true === $sent ) {
754 $entries_db_instance->update_log(
755 $log_key,
756 null,
757 [
758 /* translators: Here, %s is the comma separated emails list. */
759 sprintf( __( 'Email notification sent to %s', 'sureforms' ), esc_html( $to ) ),
760 ]
761 );
762 } else {
763 $entries_db_instance->update_log(
764 $log_key,
765 null,
766 [
767 sprintf(
768 /* translators: Here, %1$s is the comma separated emails list and %2$s is error report ( if any ). */
769 __( 'Failed sending email notification to %1$s. Reason: %2$s', 'sureforms' ),
770 esc_html( $to ),
771 ! empty( $email_report ) ? esc_html( $email_report ) : esc_html__( 'Unknown', 'sureforms' )
772 ),
773 ]
774 );
775 }
776 }
777
778 $is_mail_sent = $sent;
779 $emails[] = $to;
780 }
781 }
782 }
783 }
784
785 return [
786 'success' => $is_mail_sent,
787 'emails' => $emails,
788 ];
789 }
790
791 /**
792 * Retrieve all entries data for a specific form ID to check for unique values.
793 *
794 * @since 0.0.1
795 * @return void
796 */
797 public function field_unique_validation() {
798 if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'unique_validation_nonce' ) ) {
799 $error_message = __( 'Nonce verification failed.', 'sureforms' );
800 $error_data = [
801 'error' => $error_message,
802 ];
803 wp_send_json_error( $error_data );
804 }
805
806 global $wpdb;
807 $id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
808 $meta_value = $id;
809
810 if ( ! $meta_value ) {
811 $error_message = __( 'Invalid form ID.', 'sureforms' );
812 $error_data = [
813 'error' => $error_message,
814 ];
815 wp_send_json_error( $error_data );
816 }
817
818 $_POST = array_map( 'wp_unslash', $_POST );
819
820 // Get the entry IDs for the particualr form to perform unique field validation.
821 $entry_ids = Entries::get_all_entry_ids_for_form( $id );
822
823 $all_form_entries = [];
824 $keys = array_keys( $_POST );
825 $length = count( $keys );
826
827 for ( $i = 3; $i < $length; $i++ ) {
828 $key = $keys[ $i ];
829 $value = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : '';
830 $key = str_replace( '_', ' ', $keys[ $i ] );
831
832 foreach ( $entry_ids as $entry_id ) {
833 $entry_id = is_array( $entry_id ) ? Helper::get_integer_value( $entry_id['ID'] ) : 0;
834 $form_data = Entries::get_form_data( $entry_id );
835 if ( is_array( $form_data ) && isset( $form_data[ $key ] ) && $form_data[ $key ] === $value ) {
836 $obj = [ $key => 'not unique' ];
837 array_push( $all_form_entries, $obj );
838 break;
839 }
840 }
841 }
842
843 $results = [
844 'data' => $all_form_entries,
845 ];
846
847 wp_send_json( $results );
848 }
849
850 /**
851 * Function to save allowed block data.
852 *
853 * @since 0.0.1
854 * @return void
855 */
856 public function srfm_global_update_allowed_block() {
857 if ( ! current_user_can( 'manage_options' ) ) {
858 wp_send_json_error();
859 }
860
861 if ( ! check_ajax_referer( 'srfm_ajax_nonce', 'security', false ) ) {
862 wp_send_json_error();
863 }
864
865 if ( ! empty( $_POST['defaultAllowedQuickSidebarBlocks'] ) ) {
866 $srfm_default_allowed_quick_sidebar_blocks = json_decode( sanitize_text_field( wp_unslash( $_POST['defaultAllowedQuickSidebarBlocks'] ) ), true );
867 Helper::update_admin_settings_option( 'srfm_quick_sidebar_allowed_blocks', $srfm_default_allowed_quick_sidebar_blocks );
868 wp_send_json_success();
869 }
870 wp_send_json_error();
871 }
872
873 /**
874 * Function to save enable/disable data.
875 *
876 * @since 0.0.1
877 * @return void
878 */
879 public function srfm_global_sidebar_enabled() {
880 if ( ! current_user_can( 'manage_options' ) ) {
881 wp_send_json_error();
882 }
883
884 if ( ! check_ajax_referer( 'srfm_ajax_nonce', 'security', false ) ) {
885 wp_send_json_error();
886 }
887
888 if ( ! empty( $_POST['enableQuickActionSidebar'] ) ) {
889 $srfm_enable_quick_action_sidebar = ( 'enabled' === $_POST['enableQuickActionSidebar'] ? 'enabled' : 'disabled' );
890 Helper::update_admin_settings_option( 'srfm_enable_quick_action_sidebar', $srfm_enable_quick_action_sidebar );
891 wp_send_json_success();
892 }
893 wp_send_json_error();
894 }
895 }
896