PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 1.3.2
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v1.3.2
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 in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 1.3.2, at inc/form-submit.php

919 lines 30.6 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 * Parse an email notification template and generate the necessary components for sending an email.
680 *
681 * @param array<mixed> $submission_data An associative array containing submission data to be used in the email template.
682 * @param array<string,string> $item An associative array containing email settings, such as 'email_to', 'subject', 'email_body', and optional headers like 'email_reply_to', 'email_cc', and 'email_bcc'.
683 * @param array<string> $form_data Request object or array containing form data.
684 * @since 1.3.0
685 * @return array<string,string> An associative array containing 'to', 'subject', 'message', and 'headers' for the email.
686 */
687 public static function parse_email_notification_template( $submission_data, $item, $form_data = [] ) {
688 $smart_tags = Smart_Tags::get_instance();
689
690 $from = Helper::get_string_value( get_option( 'admin_email' ) );
691 $to = $smart_tags->process_smart_tags( $item['email_to'], $submission_data );
692 $subject = $smart_tags->process_smart_tags( $item['subject'], $submission_data, $form_data );
693 $email_body = $smart_tags->process_smart_tags( $item['email_body'], $submission_data, $form_data );
694 $email_template = new Email_Template();
695 $message = $email_template->render( $submission_data, $email_body );
696 $headers = "From: {$from}\r\n";
697 $headers .= 'X-Mailer: PHP/' . phpversion() . "\r\n";
698 $headers .= "Content-Type: text/html; charset=utf-8\r\n";
699 if ( isset( $item['email_reply_to'] ) && ! empty( $item['email_reply_to'] ) ) {
700 $headers .= 'Reply-To:' . $smart_tags->process_smart_tags( $item['email_reply_to'], $submission_data ) . "\r\n";
701 } else {
702 $headers .= "Reply-To: {$from}\r\n";
703 }
704 if ( isset( $item['email_cc'] ) && ! empty( $item['email_cc'] ) ) {
705 $headers .= 'Cc:' . $smart_tags->process_smart_tags( $item['email_cc'], $submission_data ) . "\r\n";
706 }
707 if ( isset( $item['email_bcc'] ) && ! empty( $item['email_bcc'] ) ) {
708 $headers .= 'Bcc:' . $smart_tags->process_smart_tags( $item['email_bcc'], $submission_data ) . "\r\n";
709 }
710
711 return compact( 'to', 'subject', 'message', 'headers' );
712 }
713
714 /**
715 * Send Email.
716 *
717 * @param string $id Form ID.
718 * @param array<mixed> $submission_data Submission data.
719 * @param array<string> $form_data Request object or array containing form data.
720 * @since 0.0.1
721 * @return array<mixed> Array containing the response data.
722 */
723 public static function send_email( $id, $submission_data, $form_data = [] ) {
724 $email_notification = get_post_meta( intval( $id ), '_srfm_email_notification' );
725 $is_mail_sent = false;
726 $emails = [];
727
728 if ( is_iterable( $email_notification ) ) {
729 $entries_db_instance = Entries::get_instance();
730 $log_key = $entries_db_instance->add_log( __( 'Email notification passed to the sending server', 'sureforms' ) );
731
732 foreach ( $email_notification as $notification ) {
733 foreach ( $notification as $item ) {
734 if ( true === $item['status'] ) {
735
736 $parsed = self::parse_email_notification_template( $submission_data, $item, $form_data );
737
738 /**
739 * Temporary override the content type for wp_mail.
740 * This helps us from breaking of content type from other plugins.
741 *
742 * @since 1.2.2
743 */
744 add_filter(
745 'wp_mail_content_type',
746 static function() {
747 return 'text/html'; // We need "text/html" content type to render our emails.
748 },
749 99
750 );
751
752 /**
753 * Start sending email.
754 * Wrapping it in the buffer because when some plugin such as zoho mail, overrides the wp_mail
755 * function and any exception is thrown ( Or printed ) from that plugin side, it affects the JSON response.
756 * So, to make sure such exceptions doesn't affect our JSON response, we are wrapping it inside buffer.
757 *
758 * Try-Catch does not work because the notice or errors might be echoed by other plugins rather than thrown as an exception.
759 *
760 * @since 1.2.2
761 */
762 $sent = false;
763 ob_start();
764 $sent = wp_mail( $parsed['to'], $parsed['subject'], $parsed['message'], $parsed['headers'] );
765 if ( ! $sent ) {
766 // Fallback to default PHP mail if for some reasons wp_mail fails.
767 $sent = mail( $parsed['to'], $parsed['subject'], $parsed['message'], $parsed['headers'] );
768 }
769 $email_report = ob_get_clean(); // Catch any printed notice/errors/message for reports.
770
771 if ( is_int( $log_key ) ) {
772 if ( true === $sent ) {
773 $entries_db_instance->update_log(
774 $log_key,
775 null,
776 [
777 /* translators: Here, %s is the comma separated emails list. */
778 sprintf( __( 'Email notification recipient: %s', 'sureforms' ), esc_html( $parsed['to'] ) ),
779 ]
780 );
781 } else {
782 $entries_db_instance->update_log(
783 $log_key,
784 null,
785 [
786 sprintf(
787 /* translators: Here, %1$s is the comma separated emails list and %2$s is error report ( if any ). */
788 __( 'Email server was unable to send the email notification. Recipient: %1$s. Reason: %2$s', 'sureforms' ),
789 esc_html( $parsed['to'] ),
790 ! empty( $email_report ) ? esc_html( $email_report ) : esc_html__( 'Unknown', 'sureforms' )
791 ),
792 ]
793 );
794 }
795 }
796
797 $is_mail_sent = $sent;
798 $emails[] = $parsed['to'];
799 }
800 }
801 }
802
803 if ( empty( $emails ) ) {
804 $entries_db_instance->add_log( __( 'No emails were sent', 'sureforms' ) );
805 }
806 }
807
808 return [
809 'success' => $is_mail_sent,
810 'emails' => $emails,
811 ];
812 }
813
814 /**
815 * Retrieve all entries data for a specific form ID to check for unique values.
816 *
817 * @since 0.0.1
818 * @return void
819 */
820 public function field_unique_validation() {
821 if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'unique_validation_nonce' ) ) {
822 $error_message = __( 'Nonce verification failed.', 'sureforms' );
823 $error_data = [
824 'error' => $error_message,
825 ];
826 wp_send_json_error( $error_data );
827 }
828
829 global $wpdb;
830 $id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
831 $meta_value = $id;
832
833 if ( ! $meta_value ) {
834 $error_message = __( 'Invalid form ID.', 'sureforms' );
835 $error_data = [
836 'error' => $error_message,
837 ];
838 wp_send_json_error( $error_data );
839 }
840
841 $_POST = array_map( 'wp_unslash', $_POST );
842
843 // Get the entry IDs for the particualr form to perform unique field validation.
844 $entry_ids = Entries::get_all_entry_ids_for_form( $id );
845
846 $all_form_entries = [];
847 $keys = array_keys( $_POST );
848 $length = count( $keys );
849
850 for ( $i = 3; $i < $length; $i++ ) {
851 $key = $keys[ $i ];
852 $value = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : '';
853 $key = str_replace( '_', ' ', $keys[ $i ] );
854
855 foreach ( $entry_ids as $entry_id ) {
856 $entry_id = is_array( $entry_id ) ? Helper::get_integer_value( $entry_id['ID'] ) : 0;
857 $form_data = Entries::get_form_data( $entry_id );
858 if ( is_array( $form_data ) && isset( $form_data[ $key ] ) && $form_data[ $key ] === $value ) {
859 $obj = [ $key => 'not unique' ];
860 array_push( $all_form_entries, $obj );
861 break;
862 }
863 }
864 }
865
866 $results = [
867 'data' => $all_form_entries,
868 ];
869
870 wp_send_json( $results );
871 }
872
873 /**
874 * Function to save allowed block data.
875 *
876 * @since 0.0.1
877 * @return void
878 */
879 public function srfm_global_update_allowed_block() {
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['defaultAllowedQuickSidebarBlocks'] ) ) {
889 $srfm_default_allowed_quick_sidebar_blocks = json_decode( sanitize_text_field( wp_unslash( $_POST['defaultAllowedQuickSidebarBlocks'] ) ), true );
890 Helper::update_admin_settings_option( 'srfm_quick_sidebar_allowed_blocks', $srfm_default_allowed_quick_sidebar_blocks );
891 wp_send_json_success();
892 }
893 wp_send_json_error();
894 }
895
896 /**
897 * Function to save enable/disable data.
898 *
899 * @since 0.0.1
900 * @return void
901 */
902 public function srfm_global_sidebar_enabled() {
903 if ( ! current_user_can( 'manage_options' ) ) {
904 wp_send_json_error();
905 }
906
907 if ( ! check_ajax_referer( 'srfm_ajax_nonce', 'security', false ) ) {
908 wp_send_json_error();
909 }
910
911 if ( ! empty( $_POST['enableQuickActionSidebar'] ) ) {
912 $srfm_enable_quick_action_sidebar = ( 'enabled' === $_POST['enableQuickActionSidebar'] ? 'enabled' : 'disabled' );
913 Helper::update_admin_settings_option( 'srfm_enable_quick_action_sidebar', $srfm_enable_quick_action_sidebar );
914 wp_send_json_success();
915 }
916 wp_send_json_error();
917 }
918 }
919