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

850 lines 28.3 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 * Send Email.
634 *
635 * @param string $id Form ID.
636 * @param array<mixed> $submission_data Submission data.
637 * @param array<string> $form_data Request object or array containing form data.
638 * @since 0.0.1
639 * @return array<mixed> Array containing the response data.
640 */
641 public static function send_email( $id, $submission_data, $form_data = [] ) {
642 $email_notification = get_post_meta( intval( $id ), '_srfm_email_notification' );
643 $smart_tags = new Smart_Tags();
644 $is_mail_sent = false;
645 $emails = [];
646
647 if ( is_iterable( $email_notification ) ) {
648 $entries_db_instance = Entries::get_instance();
649 $log_key = $entries_db_instance->add_log( __( 'Email Notification Initiated', 'sureforms' ) );
650
651 foreach ( $email_notification as $notification ) {
652 foreach ( $notification as $item ) {
653 if ( true === $item['status'] ) {
654 $from = Helper::get_string_value( get_option( 'admin_email' ) );
655 $to = $smart_tags->process_smart_tags( $item['email_to'], $submission_data );
656 $subject = $smart_tags->process_smart_tags( $item['subject'], $submission_data, $form_data );
657 $email_body = $smart_tags->process_smart_tags( $item['email_body'], $submission_data, $form_data );
658 $email_template = new Email_Template();
659 $message = $email_template->render( $submission_data, $email_body );
660 $headers = "From: {$from}\r\nX-Mailer: PHP/" . phpversion() . "\r\nContent-Type: text/html; charset=utf-8\r\n";
661 if ( isset( $item['email_reply_to'] ) && ! empty( $item['email_reply_to'] ) ) {
662 $headers .= 'Reply-To:' . $smart_tags->process_smart_tags( $item['email_reply_to'], $submission_data ) . "\r\n";
663 } else {
664 $headers .= "Reply-To: {$from}\r\n";
665 }
666 if ( isset( $item['email_cc'] ) && ! empty( $item['email_cc'] ) ) {
667 $headers .= 'Cc:' . $smart_tags->process_smart_tags( $item['email_cc'], $submission_data ) . "\r\n";
668 }
669 if ( isset( $item['email_bcc'] ) && ! empty( $item['email_bcc'] ) ) {
670 $headers .= 'Bcc:' . $smart_tags->process_smart_tags( $item['email_bcc'], $submission_data ) . "\r\n";
671 }
672
673 /**
674 * Temporary override the content type for wp_mail.
675 * This helps us from breaking of content type from other plugins.
676 *
677 * @since 1.2.2
678 */
679 add_filter(
680 'wp_mail_content_type',
681 static function() {
682 return 'text/html'; // We need "text/html" content type to render our emails.
683 },
684 99
685 );
686
687 /**
688 * Start sending email.
689 * Wrapping it in the buffer because when some plugin such as zoho mail, overrides the wp_mail
690 * function and any exception is thrown ( Or printed ) from that plugin side, it affects the JSON response.
691 * So, to make sure such exceptions doesn't affect our JSON response, we are wrapping it inside buffer.
692 *
693 * Try-Catch does not work because the notice or errors might be echoed by other plugins rather than thrown as an exception.
694 *
695 * @since 1.2.2
696 */
697 $sent = false;
698 ob_start();
699 $sent = wp_mail( $to, $subject, $message, $headers );
700 if ( ! $sent ) {
701 // Fallback to default PHP mail if for some reasons wp_mail fails.
702 $sent = mail( $to, $subject, $message, $headers );
703 }
704 $email_report = ob_get_clean(); // Catch any printed notice/errors/message for reports.
705
706 if ( is_int( $log_key ) ) {
707 if ( true === $sent ) {
708 $entries_db_instance->update_log(
709 $log_key,
710 null,
711 [
712 /* translators: Here, %s is the comma separated emails list. */
713 sprintf( __( 'Email notification sent to %s', 'sureforms' ), esc_html( $to ) ),
714 ]
715 );
716 } else {
717 $entries_db_instance->update_log(
718 $log_key,
719 null,
720 [
721 sprintf(
722 /* translators: Here, %1$s is the comma separated emails list and %2$s is error report ( if any ). */
723 __( 'Failed sending email notification to %1$s. Reason: %2$s', 'sureforms' ),
724 esc_html( $to ),
725 ! empty( $email_report ) ? esc_html( $email_report ) : esc_html__( 'Unknown', 'sureforms' )
726 ),
727 ]
728 );
729 }
730 }
731
732 $is_mail_sent = $sent;
733 $emails[] = $to;
734 }
735 }
736 }
737 }
738
739 return [
740 'success' => $is_mail_sent,
741 'emails' => $emails,
742 ];
743 }
744
745 /**
746 * Retrieve all entries data for a specific form ID to check for unique values.
747 *
748 * @since 0.0.1
749 * @return void
750 */
751 public function field_unique_validation() {
752 if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'unique_validation_nonce' ) ) {
753 $error_message = __( 'Nonce verification failed.', 'sureforms' );
754 $error_data = [
755 'error' => $error_message,
756 ];
757 wp_send_json_error( $error_data );
758 }
759
760 global $wpdb;
761 $id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
762 $meta_value = $id;
763
764 if ( ! $meta_value ) {
765 $error_message = __( 'Invalid form ID.', 'sureforms' );
766 $error_data = [
767 'error' => $error_message,
768 ];
769 wp_send_json_error( $error_data );
770 }
771
772 $_POST = array_map( 'wp_unslash', $_POST );
773
774 // Get the entry IDs for the particualr form to perform unique field validation.
775 $entry_ids = Entries::get_all_entry_ids_for_form( $id );
776
777 $all_form_entries = [];
778 $keys = array_keys( $_POST );
779 $length = count( $keys );
780
781 for ( $i = 3; $i < $length; $i++ ) {
782 $key = $keys[ $i ];
783 $value = isset( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : '';
784 $key = str_replace( '_', ' ', $keys[ $i ] );
785
786 foreach ( $entry_ids as $entry_id ) {
787 $entry_id = is_array( $entry_id ) ? Helper::get_integer_value( $entry_id['ID'] ) : 0;
788 $form_data = Entries::get_form_data( $entry_id );
789 if ( is_array( $form_data ) && isset( $form_data[ $key ] ) && $form_data[ $key ] === $value ) {
790 $obj = [ $key => 'not unique' ];
791 array_push( $all_form_entries, $obj );
792 break;
793 }
794 }
795 }
796
797 $results = [
798 'data' => $all_form_entries,
799 ];
800
801 wp_send_json( $results );
802 }
803
804 /**
805 * Function to save allowed block data.
806 *
807 * @since 0.0.1
808 * @return void
809 */
810 public function srfm_global_update_allowed_block() {
811 if ( ! current_user_can( 'manage_options' ) ) {
812 wp_send_json_error();
813 }
814
815 if ( ! check_ajax_referer( 'srfm_ajax_nonce', 'security', false ) ) {
816 wp_send_json_error();
817 }
818
819 if ( ! empty( $_POST['defaultAllowedQuickSidebarBlocks'] ) ) {
820 $srfm_default_allowed_quick_sidebar_blocks = json_decode( sanitize_text_field( wp_unslash( $_POST['defaultAllowedQuickSidebarBlocks'] ) ), true );
821 Helper::update_admin_settings_option( 'srfm_quick_sidebar_allowed_blocks', $srfm_default_allowed_quick_sidebar_blocks );
822 wp_send_json_success();
823 }
824 wp_send_json_error();
825 }
826
827 /**
828 * Function to save enable/disable data.
829 *
830 * @since 0.0.1
831 * @return void
832 */
833 public function srfm_global_sidebar_enabled() {
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['enableQuickActionSidebar'] ) ) {
843 $srfm_enable_quick_action_sidebar = ( 'enabled' === $_POST['enableQuickActionSidebar'] ? 'enabled' : 'disabled' );
844 Helper::update_admin_settings_option( 'srfm_enable_quick_action_sidebar', $srfm_enable_quick_action_sidebar );
845 wp_send_json_success();
846 }
847 wp_send_json_error();
848 }
849 }
850