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

873 lines 29.5 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 ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] && ! empty( $_FILES ) ) {
214 add_filter( 'upload_dir', [ $this, 'change_upload_dir' ] );
215
216 foreach ( $_FILES as $field => $file ) {
217 if ( is_array( $file['name'] ) ) {
218 foreach ( $file['name'] as $key => $filename ) {
219 $temp_path = $file['tmp_name'][ $key ];
220 $file_size = $file['size'][ $key ];
221 $file_type = $file['type'][ $key ];
222 $file_error = $file['error'][ $key ];
223
224 if ( ! $filename && ! $temp_path && ! $file_size && ! $file_type ) {
225 $form_data[ $field ][] = '';
226 continue;
227 }
228
229 $uploaded_file = [
230 'name' => $filename,
231 'type' => $file_type,
232 'tmp_name' => $temp_path,
233 'error' => $file_error,
234 'size' => $file_size,
235 ];
236
237 $upload_overrides = [
238 'test_form' => false,
239 ];
240 $move_file = wp_handle_upload( $uploaded_file, $upload_overrides );
241 remove_filter( 'upload_dir', [ $this, 'change_upload_dir' ] );
242
243 if ( $move_file && ! isset( $move_file['error'] ) ) {
244 $form_data[ $field ][] = $move_file['url'];
245 } else {
246 wp_send_json_error( __( 'File is not uploaded', 'sureforms' ) );
247 }
248 }
249 } else {
250 $form_data[ $field ][] = '';
251 }
252 }
253 }
254
255 if ( ! $form_data['form-id'] ) {
256 wp_send_json_error( __( 'Form Id is missing.', 'sureforms' ) );
257 }
258 $current_form_id = $form_data['form-id'];
259 $security_type = Helper::get_meta_value( Helper::get_integer_value( $current_form_id ), '_srfm_captcha_security_type' );
260 $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 ) ) : '';
261
262 if ( 'none' !== $security_type ) {
263 $global_setting_options = get_option( 'srfm_security_settings_options' );
264 } else {
265 $global_setting_options = [];
266 }
267
268 if ( 'g-recaptcha' === $security_type ) {
269 switch ( $selected_captcha_type ) {
270 case 'v2-checkbox':
271 $key = 'srfm_v2_checkbox_secret_key';
272 break;
273 case 'v2-invisible':
274 $key = 'srfm_v2_invisible_secret_key';
275 break;
276 case 'v3-reCAPTCHA':
277 $key = 'srfm_v3_secret_key';
278 break;
279 default:
280 $key = '';
281 break;
282 }
283
284 $google_captcha_secret_key = is_array( $global_setting_options ) && isset( $global_setting_options[ $key ] ) ? $global_setting_options[ $key ] : '';
285 }
286
287 if ( 'cf-turnstile' === $security_type ) {
288 // Turnstile validation.
289 $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'] ) : '';
290 $cf_response = ! empty( $form_data['cf-turnstile-response'] ) ? $form_data['cf-turnstile-response'] : false;
291
292 // if gdpr is enabled then set remote ip to empty.
293 $compliance = get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_compliance', true );
294 $gdpr = false;
295
296 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
297 $gdpr = ! empty( $compliance[0]['gdpr'] ) ? $compliance[0]['gdpr'] : false;
298 }
299
300 // check if ip logging is disabled in global settings then set remote ip to empty.
301 $gb_general_settinionsgs_opt = get_option( 'srfm_general_settings_options' );
302 $srfm_ip_log = is_array( $gb_general_settinionsgs_opt ) && isset( $gb_general_settinionsgs_opt['srfm_ip_log'] ) ? $gb_general_settinionsgs_opt['srfm_ip_log'] : '';
303
304 $remote_ip = $gdpr || ( ! $srfm_ip_log ) ? '' : ( isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '' );
305
306 $turnstile_validation_result = self::validate_turnstile_token( $srfm_cf_turnstile_secret_key, $cf_response, $remote_ip );
307
308 // If the cloudflare validation fails, return an error.
309 if ( is_array( $turnstile_validation_result ) && isset( $turnstile_validation_result['success'] ) && false === $turnstile_validation_result['success'] ) {
310 $error_message = $turnstile_validation_result['error'] ?? __( 'Cloudflare Turnstile validation failed.', 'sureforms' );
311 return new \WP_Error( 'cf_turnstile_error', $error_message, [ 'status' => 403 ] );
312 }
313 }
314
315 if ( 'hcaptcha' === $security_type ) {
316 $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'] ) : '';
317 $hcaptcha_response = ! empty( $form_data['h-captcha-response'] ) ? $form_data['h-captcha-response'] : false;
318
319 // if gdpr is enabled then set remote ip to empty.
320 $compliance = get_post_meta( Helper::get_integer_value( $current_form_id ), '_srfm_compliance', true );
321 $gdpr = false;
322
323 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
324 $gdpr = ! empty( $compliance[0]['gdpr'] ) ? $compliance[0]['gdpr'] : false;
325 }
326
327 // check if ip logging is disabled in global settings then set remote ip to empty.
328 $gb_general_settings_options = get_option( 'srfm_general_settings_options' );
329 $srfm_ip_log = is_array( $gb_general_settings_options ) && isset( $gb_general_settings_options['srfm_ip_log'] ) ? $gb_general_settings_options['srfm_ip_log'] : '';
330
331 $remote_ip = $gdpr || ( ! $srfm_ip_log ) ? '' : ( isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '' );
332 $hcaptcha_validation_result = self::validate_hcaptcha_token( $srfm_hcaptcha_secret_key, $hcaptcha_response, $remote_ip );
333
334 // If the hcaptcha validation fails, return an error.
335 if ( is_array( $hcaptcha_validation_result ) && isset( $hcaptcha_validation_result['success'] ) && false === $hcaptcha_validation_result['success'] ) {
336 $error_message = $hcaptcha_validation_result['error'] ?? __( 'hCaptcha validation failed.', 'sureforms' );
337 return new \WP_Error( 'hcaptcha_error', $error_message, [ 'status' => 403 ] );
338 }
339 }
340
341 if ( isset( $form_data['srfm-honeypot-field'] ) && empty( $form_data['srfm-honeypot-field'] ) ) {
342 if ( ! empty( $google_captcha_secret_key ) ) {
343 if ( isset( $form_data['sureforms_form_submit'] ) ) {
344 $secret_key = $google_captcha_secret_key;
345 $ipaddress = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
346 $captcha_response = $form_data['g-recaptcha-response'];
347 $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $captcha_response . '&ip=' . $ipaddress;
348
349 $response = wp_remote_get( $url );
350
351 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
352 $json_string = wp_remote_retrieve_body( $response );
353 $data = (array) json_decode( $json_string, true );
354 } else {
355 $data = [];
356 }
357 $sureforms_captcha_data = $data;
358
359 } else {
360 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
361 }
362 if ( isset( $sureforms_captcha_data['success'] ) && true === $sureforms_captcha_data['success'] ) {
363 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
364 }
365 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
366
367 }
368 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
369
370 }
371 if ( ! isset( $form_data['srfm-honeypot-field'] ) ) {
372 if ( ! empty( $google_captcha_secret_key ) ) {
373 if ( isset( $form_data['sureforms_form_submit'] ) ) {
374 $secret_key = $google_captcha_secret_key;
375 $ipaddress = isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
376 $captcha_response = $form_data['g-recaptcha-response'];
377 $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $captcha_response . '&ip=' . $ipaddress;
378
379 $response = wp_remote_get( $url );
380
381 if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
382 $json_string = wp_remote_retrieve_body( $response );
383 $data = (array) json_decode( $json_string, true );
384 } else {
385 $data = [];
386 }
387 $sureforms_captcha_data = $data;
388
389 } else {
390 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
391 }
392 if ( true === $sureforms_captcha_data['success'] ) {
393 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
394 }
395 return new \WP_Error( 'recaptcha_error', __( 'reCAPTCHA error.', 'sureforms' ), [ 'status' => 403 ] );
396
397 }
398 return rest_ensure_response( $this->handle_form_entry( $form_data ) );
399
400 }
401 return new \WP_Error( 'spam_detected', __( 'Spam Detected', 'sureforms' ), [ 'status' => 403 ] );
402 }
403
404 /**
405 * Change the upload directory
406 *
407 * @param array<mixed> $dirs upload directory.
408 * @return array<mixed>
409 * @since 0.0.1
410 */
411 public function change_upload_dir( $dirs ) {
412 $dirs['subdir'] = '/sureforms';
413 $dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
414 $dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
415 return $dirs;
416 }
417
418 /**
419 * Send Email and Create Entry.
420 *
421 * @param array<string> $form_data Request object or array containing form data.
422 * @since 0.0.1
423 * @return array<mixed> Array containing the response data.
424 */
425 public function handle_form_entry( $form_data ) {
426
427 $id = sanitize_text_field( $form_data['form-id'] );
428
429 // Get the compliance settings.
430 $compliance = get_post_meta( Helper::get_integer_value( $id ), '_srfm_compliance', true );
431 $gdpr = '';
432 $do_not_store_entries = '';
433
434 if ( is_array( $compliance ) && is_array( $compliance[0] ) ) {
435 $gdpr = $compliance[0]['gdpr'] ?? '';
436 $do_not_store_entries = $compliance[0]['do_not_store_entries'] ?? '';
437 }
438
439 $submission_data = [];
440
441 $form_data_keys = array_keys( $form_data );
442 $form_data_count = count( $form_data );
443
444 for ( $i = 0; $i < $form_data_count; $i++ ) {
445 $key = strval( $form_data_keys[ $i ] );
446
447 /**
448 * This will allow to pass only sureforms fields
449 * checking -lbl- as thats mandatory for in key of sureforms fields.
450 */
451 if ( false === str_contains( $key, '-lbl-' ) ) {
452 continue;
453 }
454
455 $value = $form_data[ $key ];
456
457 $field_name = htmlspecialchars( str_replace( '_', ' ', $key ) );
458
459 // If the field is an array, encode the values. This is to add support for multi-upload field.
460 if ( is_array( $value ) ) {
461 $submission_data[ $field_name ] =
462 array_map(
463 static function ( $val ) {
464 return rawurlencode( $val );
465 },
466 $value
467 );
468 } else {
469 $submission_data[ $field_name ] = htmlspecialchars( $value );
470 }
471 }
472
473 $modified_message = $this->prepare_submission_data( $submission_data );
474
475 $form_before_submission_data = [
476 'form_id' => $id ? intval( $id ) : '',
477 'data' => $modified_message,
478 ];
479
480 /**
481 * Fires before submission process starts.
482 */
483 do_action( 'srfm_before_submission', $form_before_submission_data );
484
485 $name = sanitize_text_field( get_the_title( intval( $id ) ) );
486 $send_email = $this->send_email( $id, $submission_data, $form_data );
487 $emails = [];
488
489 if ( $send_email ) {
490 $emails = $send_email['emails'];
491 }
492
493 // Check if GDPR is enabled and do not store entries is enabled.
494 // If so, send email and do not store entries.
495 if ( $gdpr && $do_not_store_entries ) {
496
497 $form_submit_response = [
498 'success' => true,
499 'form_id' => $id ? intval( $id ) : '',
500 'to_emails' => $emails,
501 'form_name' => $name ? esc_attr( $name ) : '',
502 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
503 'data' => $modified_message,
504 ];
505
506 do_action( 'srfm_form_submit', $form_submit_response );
507
508 /**
509 * Hook for enabling background processes.
510 *
511 * @param array $form_data form data related to submission.
512 */
513 $form_data['form_id'] = $id ? intval( $id ) : '';
514 do_action( 'srfm_after_submission_process', $form_data );
515
516 return [
517 'success' => true,
518 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
519 'data' => [
520 'name' => $name,
521 'after_submit' => false,
522 ],
523 'redirect_url' => Generate_Form_Markup::get_redirect_url( $form_data, $submission_data ),
524 ];
525
526 }
527
528 $global_setting_options = get_option( 'srfm_general_settings_options' );
529
530 // If GDPR is enabled, do not store IP, browser, and device info.
531 // If not, store IP, browser, and device info.
532 $user_ip = '';
533 $browser_name = '';
534 $device_name = '';
535 if ( ! $gdpr ) {
536 $srfm_ip_log = is_array( $global_setting_options ) && isset( $global_setting_options['srfm_ip_log'] ) ? $global_setting_options['srfm_ip_log'] : '';
537
538 $user_ip = $srfm_ip_log && isset( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP ) : '';
539 $browser = new Browser();
540 $browser_name = sanitize_text_field( $browser->getBrowser() );
541 $device_name = sanitize_text_field( $browser->getPlatform() );
542 }
543
544 $form_markup = get_the_content( null, false, Helper::get_integer_value( $form_data['form-id'] ) );
545 $pattern = '/"label":"(.*?)"/';
546 preg_match_all( $pattern, $form_markup, $matches );
547 $submission_info = [
548 'user_ip' => $user_ip,
549 'browser_name' => $browser_name,
550 'device_name' => $device_name,
551 ];
552 $entries_data = [
553 'form_id' => $id,
554 'form_data' => $submission_data,
555 'submission_info' => $submission_info,
556 ];
557 if ( is_user_logged_in() ) {
558 // If user is logged in then save their user id.
559 $entries_data['user_id'] = get_current_user_id();
560 }
561 $entry_id = Entries::add( $entries_data );
562 if ( $entry_id ) {
563
564 $response = [
565 'success' => true,
566 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
567 'data' => [
568 'name' => $name,
569 'submission_id' => $entry_id,
570 'after_submit' => true,
571 ],
572 'redirect_url' => Generate_Form_Markup::get_redirect_url( $form_data, $submission_data ),
573 ];
574
575 $form_submit_response = apply_filters(
576 'srfm_form_submit_response',
577 [
578 'success' => true,
579 'form_id' => $id ? intval( $id ) : '',
580 'entry_id' => intval( $entry_id ),
581 'to_emails' => $emails,
582 'form_name' => $name ? esc_attr( $name ) : '',
583 'message' => Generate_Form_Markup::get_confirmation_markup( $form_data, $submission_data ),
584 'data' => $modified_message,
585 ]
586 );
587
588 do_action( 'srfm_form_submit', $form_submit_response );
589 } else {
590 $response = [
591 'success' => false,
592 'message' => __( 'Error submitting form', 'sureforms' ),
593 ];
594 }
595
596 return $response;
597 }
598
599 /**
600 * Prepare submission data.
601 *
602 * @param array<mixed> $submission_data Submission data.
603 * @since 0.0.7
604 * @return array<mixed> Modified submission data.
605 */
606 public function prepare_submission_data( $submission_data ) {
607 $modified_message = [];
608 foreach ( $submission_data as $key => $value ) {
609 $parts = explode( '-lbl-', $key );
610 $label = '';
611
612 if ( ! empty( $parts[1] ) ) {
613 $tokens = explode( '-', $parts[1] );
614 if ( count( $tokens ) > 1 ) {
615 $label = implode( '-', array_slice( $tokens, 1 ) );
616 }
617
618 $fields = explode( '-', $parts[0] );
619
620 // Since the upload field returns an array of file URLs, we need to implode them with a comma.
621 if ( 'upload' === $fields[1] && ! empty( $value ) && is_array( $value ) ) {
622 $modified_message[ $label ] = urldecode( implode( ', ', $value ) );
623 } else {
624 $modified_message[ $label ] = html_entity_decode( esc_attr( Helper::get_string_value( $value ) ) );
625 }
626 }
627 }
628
629 return $modified_message;
630 }
631
632 /**
633 * Parse an email notification template and generate the necessary components for sending an email.
634 *
635 * @param array<mixed> $submission_data An associative array containing submission data to be used in the email template.
636 * @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'.
637 * @param array<string> $form_data Request object or array containing form data.
638 * @since 1.3.0
639 * @return array<string,string> An associative array containing 'to', 'subject', 'message', and 'headers' for the email.
640 */
641 public static function parse_email_notification_template( $submission_data, $item, $form_data = [] ) {
642 $smart_tags = Smart_Tags::get_instance();
643
644 $from = Helper::get_string_value( get_option( 'admin_email' ) );
645 $to = $smart_tags->process_smart_tags( $item['email_to'], $submission_data );
646 $subject = $smart_tags->process_smart_tags( $item['subject'], $submission_data, $form_data );
647 $email_body = $smart_tags->process_smart_tags( $item['email_body'], $submission_data, $form_data );
648 $email_template = new Email_Template();
649 $message = $email_template->render( $submission_data, $email_body );
650 $headers = "From: {$from}\r\n";
651 $headers .= 'X-Mailer: PHP/' . phpversion() . "\r\n";
652 $headers .= "Content-Type: text/html; charset=utf-8\r\n";
653 if ( isset( $item['email_reply_to'] ) && ! empty( $item['email_reply_to'] ) ) {
654 $headers .= 'Reply-To:' . $smart_tags->process_smart_tags( $item['email_reply_to'], $submission_data ) . "\r\n";
655 } else {
656 $headers .= "Reply-To: {$from}\r\n";
657 }
658 if ( isset( $item['email_cc'] ) && ! empty( $item['email_cc'] ) ) {
659 $headers .= 'Cc:' . $smart_tags->process_smart_tags( $item['email_cc'], $submission_data ) . "\r\n";
660 }
661 if ( isset( $item['email_bcc'] ) && ! empty( $item['email_bcc'] ) ) {
662 $headers .= 'Bcc:' . $smart_tags->process_smart_tags( $item['email_bcc'], $submission_data ) . "\r\n";
663 }
664
665 return compact( 'to', 'subject', 'message', 'headers' );
666 }
667
668 /**
669 * Send Email.
670 *
671 * @param string $id Form ID.
672 * @param array<mixed> $submission_data Submission data.
673 * @param array<string> $form_data Request object or array containing form data.
674 * @since 0.0.1
675 * @return array<mixed> Array containing the response data.
676 */
677 public static function send_email( $id, $submission_data, $form_data = [] ) {
678 $email_notification = get_post_meta( intval( $id ), '_srfm_email_notification' );
679 $is_mail_sent = false;
680 $emails = [];
681
682 if ( is_iterable( $email_notification ) ) {
683 $entries_db_instance = Entries::get_instance();
684 $log_key = $entries_db_instance->add_log( __( 'Email notification passed to the sending server', 'sureforms' ) );
685
686 foreach ( $email_notification as $notification ) {
687 foreach ( $notification as $item ) {
688 if ( true === $item['status'] ) {
689
690 $parsed = self::parse_email_notification_template( $submission_data, $item, $form_data );
691
692 /**
693 * Temporary override the content type for wp_mail.
694 * This helps us from breaking of content type from other plugins.
695 *
696 * @since 1.2.2
697 */
698 add_filter(
699 'wp_mail_content_type',
700 static function() {
701 return 'text/html'; // We need "text/html" content type to render our emails.
702 },
703 99
704 );
705
706 /**
707 * Start sending email.
708 * Wrapping it in the buffer because when some plugin such as zoho mail, overrides the wp_mail
709 * function and any exception is thrown ( Or printed ) from that plugin side, it affects the JSON response.
710 * So, to make sure such exceptions doesn't affect our JSON response, we are wrapping it inside buffer.
711 *
712 * Try-Catch does not work because the notice or errors might be echoed by other plugins rather than thrown as an exception.
713 *
714 * @since 1.2.2
715 */
716 $sent = false;
717 ob_start();
718 $sent = wp_mail( $parsed['to'], $parsed['subject'], $parsed['message'], $parsed['headers'] );
719 if ( ! $sent ) {
720 // Fallback to default PHP mail if for some reasons wp_mail fails.
721 $sent = mail( $parsed['to'], $parsed['subject'], $parsed['message'], $parsed['headers'] );
722 }
723 $email_report = ob_get_clean(); // Catch any printed notice/errors/message for reports.
724
725 if ( is_int( $log_key ) ) {
726 if ( true === $sent ) {
727 $entries_db_instance->update_log(
728 $log_key,
729 null,
730 [
731 /* translators: Here, %s is the comma separated emails list. */
732 sprintf( __( 'Email notification recipient: %s', 'sureforms' ), esc_html( $parsed['to'] ) ),
733 ]
734 );
735 } else {
736 $entries_db_instance->update_log(
737 $log_key,
738 null,
739 [
740 sprintf(
741 /* translators: Here, %1$s is the comma separated emails list and %2$s is error report ( if any ). */
742 __( 'Email server was unable to send the email notification. Recipient: %1$s. Reason: %2$s', 'sureforms' ),
743 esc_html( $parsed['to'] ),
744 ! empty( $email_report ) ? esc_html( $email_report ) : esc_html__( 'Unknown', 'sureforms' )
745 ),
746 ]
747 );
748 }
749 }
750
751 $is_mail_sent = $sent;
752 $emails[] = $parsed['to'];
753 }
754 }
755 }
756
757 if ( empty( $emails ) ) {
758 $entries_db_instance->add_log( __( 'No emails were sent', 'sureforms' ) );
759 }
760 }
761
762 return [
763 'success' => $is_mail_sent,
764 'emails' => $emails,
765 ];
766 }
767
768 /**
769 * Retrieve all entries data for a specific form ID to check for unique values.
770 *
771 * @since 0.0.1
772 * @return void
773 */
774 public function field_unique_validation() {
775 if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'unique_validation_nonce' ) ) {
776 $error_message = __( 'Nonce verification failed.', 'sureforms' );
777 $error_data = [
778 'error' => $error_message,
779 ];
780 wp_send_json_error( $error_data );
781 }
782
783 global $wpdb;
784 $id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
785 $meta_value = $id;
786
787 if ( ! $meta_value ) {
788 $error_message = __( 'Invalid form ID.', 'sureforms' );
789 $error_data = [
790 'error' => $error_message,
791 ];
792 wp_send_json_error( $error_data );
793 }
794
795 $_POST = array_map( 'wp_unslash', $_POST );
796
797 // Get the entry IDs for the particualr form to perform unique field validation.
798 $entry_ids = Entries::get_all_entry_ids_for_form( $id );
799
800 $all_form_entries = [];
801 $keys = array_keys( $_POST );
802 $length = count( $keys );
803
804 for ( $i = 3; $i < $length; $i++ ) {
805 $key = $keys[ $i ];
806 $value = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : '';
807 $key = str_replace( '_', ' ', $keys[ $i ] );
808
809 foreach ( $entry_ids as $entry_id ) {
810 $entry_id = is_array( $entry_id ) ? Helper::get_integer_value( $entry_id['ID'] ) : 0;
811 $form_data = Entries::get_form_data( $entry_id );
812 if ( is_array( $form_data ) && isset( $form_data[ $key ] ) && $form_data[ $key ] === $value ) {
813 $obj = [ $key => 'not unique' ];
814 array_push( $all_form_entries, $obj );
815 break;
816 }
817 }
818 }
819
820 $results = [
821 'data' => $all_form_entries,
822 ];
823
824 wp_send_json( $results );
825 }
826
827 /**
828 * Function to save allowed block data.
829 *
830 * @since 0.0.1
831 * @return void
832 */
833 public function srfm_global_update_allowed_block() {
834 if ( ! current_user_can( 'manage_options' ) ) {
835 wp_send_json_error();
836 }
837
838 if ( ! check_ajax_referer( 'srfm_ajax_nonce', 'security', false ) ) {
839 wp_send_json_error();
840 }
841
842 if ( ! empty( $_POST['defaultAllowedQuickSidebarBlocks'] ) ) {
843 $srfm_default_allowed_quick_sidebar_blocks = json_decode( sanitize_text_field( wp_unslash( $_POST['defaultAllowedQuickSidebarBlocks'] ) ), true );
844 Helper::update_admin_settings_option( 'srfm_quick_sidebar_allowed_blocks', $srfm_default_allowed_quick_sidebar_blocks );
845 wp_send_json_success();
846 }
847 wp_send_json_error();
848 }
849
850 /**
851 * Function to save enable/disable data.
852 *
853 * @since 0.0.1
854 * @return void
855 */
856 public function srfm_global_sidebar_enabled() {
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['enableQuickActionSidebar'] ) ) {
866 $srfm_enable_quick_action_sidebar = ( 'enabled' === $_POST['enableQuickActionSidebar'] ? 'enabled' : 'disabled' );
867 Helper::update_admin_settings_option( 'srfm_enable_quick_action_sidebar', $srfm_enable_quick_action_sidebar );
868 wp_send_json_success();
869 }
870 wp_send_json_error();
871 }
872 }
873